monitoring: llama-swap GPU/LLM stack (v250) — PrometheusRule, Grafana dashboard, scrape config, VRAM exporter
This commit is contained in:
@@ -60,3 +60,37 @@
|
||||
- include_tasks: preset.yml
|
||||
when: llm_router_preset_enabled | default(false)
|
||||
tags: [always]
|
||||
|
||||
# Phase S — llama-swap mode hot-swap proxy (port 8001)
|
||||
# Gates on llm_swapmode_enabled (default false — complete no-op until enabled).
|
||||
# Replaces router mode entirely: single Go binary + YAML config, no INI presets.
|
||||
# Additive deployment (non-invasive); production router (port 8002) stays running during Phase 1 shadow.
|
||||
#
|
||||
# When llm_swapmode_enabled: true, this phase:
|
||||
# swapmode_binary — download + install llama-swap binary
|
||||
# swapmode_config — render config.yaml.j2 template
|
||||
# swapmode_systemd — deploy llama-swap.service unit
|
||||
# swapmode_firewall — open port 8001 scoped to Hermes subnet
|
||||
# swapmode_verify — start service, run validation gates
|
||||
#
|
||||
# Added 2026-08-18 (t_c1e44190): llama-swap Phase 3 Ansible integration — Wong.
|
||||
- include_tasks: swapmode.yml
|
||||
when: llm_swapmode_enabled | default(false)
|
||||
tags: [always]
|
||||
|
||||
# Phase M — GPU/LLM Monitoring (VRAM exporter + Prometheus + Grafana)
|
||||
# Gates on llm_monitoring_enabled (default true — but can be disabled per-host).
|
||||
# Deploys:
|
||||
# - VRAM textfile exporter script (runs every minute via cron)
|
||||
# - Prometheus scrape config template (for GitOps deployment)
|
||||
# - Grafana dashboard JSON template (for GitOps deployment)
|
||||
# - PrometheusRule alert rules template (for GitOps deployment)
|
||||
#
|
||||
# No cluster-facing changes here; templates are staged for manual review
|
||||
# and committed via Git. ArgoCD syncs them automatically afterward.
|
||||
#
|
||||
# Reference: roles/llm-inference-multimodel/references/monitoring-llm-homelab-ciro-luciotta-2026.md
|
||||
# Added 2026-08-18 (t_57a9f82f): GPU/LLM monitoring Phase 3 — Wong.
|
||||
- include_tasks: monitoring.yml
|
||||
when: llm_monitoring_enabled | default(true)
|
||||
tags: [always]
|
||||
|
||||
174
ansible/roles/llm-inference-multimodel/tasks/monitoring.yml
Normal file
174
ansible/roles/llm-inference-multimodel/tasks/monitoring.yml
Normal file
@@ -0,0 +1,174 @@
|
||||
---
|
||||
# ==============================================================================
|
||||
# FILE: roles/llm-inference-multimodel/tasks/monitoring.yml
|
||||
# DESCRIPTION: Phase X — GPU/LLM monitoring deployment for llama-swap.
|
||||
# Deploys:
|
||||
# 1. VRAM textfile exporter script + cron job
|
||||
# 2. Prometheus scrape config template (for GitOps deployment)
|
||||
# 3. Grafana dashboard JSON template (for GitOps deployment)
|
||||
# 4. PrometheusRule CR template (for GitOps deployment)
|
||||
#
|
||||
# REFERENCED BY: tasks/main.yml (call with `- include_tasks: monitoring.yml`)
|
||||
# GATED BY: llm_monitoring_enabled (default: true)
|
||||
#
|
||||
# AUTHOR: Wong (Infrastructure Automation Specialist)
|
||||
# DATE: 2026-08-18
|
||||
# ==============================================================================
|
||||
|
||||
- name: GPU/LLM Monitoring | Conditional gate
|
||||
debug:
|
||||
msg: "GPU/LLM monitoring deployment gated: llm_monitoring_enabled={{ llm_monitoring_enabled }}"
|
||||
when: not llm_monitoring_enabled
|
||||
|
||||
- name: GPU/LLM Monitoring | Create monitoring script directory
|
||||
ansible.builtin.file:
|
||||
path: /opt/llama-server-monitoring
|
||||
state: directory
|
||||
owner: "{{ llm_service_user }}"
|
||||
group: "{{ llm_service_user }}"
|
||||
mode: "0755"
|
||||
when: llm_monitoring_enabled
|
||||
|
||||
- name: GPU/LLM Monitoring | Deploy VRAM exporter script
|
||||
ansible.builtin.copy:
|
||||
src: nvidia-smi-vram-exporter.sh
|
||||
dest: "{{ llm_vram_exporter_script }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
when: llm_monitoring_enabled
|
||||
notify: restart vram exporter cron
|
||||
|
||||
- name: GPU/LLM Monitoring | Create cron job for VRAM exporter
|
||||
ansible.builtin.cron:
|
||||
name: "llama-swap GPU VRAM exporter"
|
||||
minute: "{{ llm_vram_exporter_cron_minute }}"
|
||||
hour: "*"
|
||||
day: "*"
|
||||
month: "*"
|
||||
weekday: "*"
|
||||
job: "{{ llm_vram_exporter_script }}"
|
||||
state: present
|
||||
when: llm_monitoring_enabled
|
||||
|
||||
- name: GPU/LLM Monitoring | Verify VRAM exporter textfile directory exists
|
||||
ansible.builtin.file:
|
||||
path: "{{ llm_vram_textfile_dir }}"
|
||||
state: directory
|
||||
owner: "{{ llm_service_user }}"
|
||||
group: "{{ llm_service_user }}"
|
||||
mode: "0755"
|
||||
when: llm_monitoring_enabled
|
||||
|
||||
- name: GPU/LLM Monitoring | Force initial VRAM exporter run
|
||||
ansible.builtin.shell:
|
||||
cmd: "{{ llm_vram_exporter_script }}"
|
||||
register: vram_exporter_run
|
||||
changed_when: false
|
||||
when: llm_monitoring_enabled
|
||||
|
||||
- name: GPU/LLM Monitoring | Verify VRAM exporter output
|
||||
ansible.builtin.stat:
|
||||
path: "{{ llm_vram_textfile_dir }}/nvidia.prom"
|
||||
register: vram_exporter_output
|
||||
retries: 5
|
||||
delay: 2
|
||||
until: vram_exporter_output.stat.exists
|
||||
when: llm_monitoring_enabled
|
||||
|
||||
- name: GPU/LLM Monitoring | Display VRAM exporter output
|
||||
ansible.builtin.debug:
|
||||
msg: "VRAM exporter metric created: {{ vram_exporter_output.stat.path }}"
|
||||
when:
|
||||
- llm_monitoring_enabled
|
||||
- vram_exporter_output.stat.exists
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Prometheus & Grafana templates (for GitOps deployment via ArgoCD)
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
- name: GPU/LLM Monitoring | Template Prometheus scrape config
|
||||
ansible.builtin.template:
|
||||
src: llama-swap-prometheus-scrape.yml.j2
|
||||
dest: /tmp/llama-swap-prometheus-scrape.yml
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
when: llm_monitoring_enabled
|
||||
register: prometheus_scrape_config
|
||||
|
||||
- name: GPU/LLM Monitoring | Template Grafana dashboard JSON
|
||||
ansible.builtin.template:
|
||||
src: llama-swap-grafana-dashboard.json.j2
|
||||
dest: /tmp/llama-swap-grafana-dashboard.json
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
when: llm_monitoring_enabled
|
||||
register: grafana_dashboard_config
|
||||
|
||||
- name: GPU/LLM Monitoring | Template PrometheusRule alert rules
|
||||
ansible.builtin.template:
|
||||
src: llama-swap-alerts.yml.j2
|
||||
dest: /tmp/llama-swap-alerts.yml
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
when: llm_monitoring_enabled
|
||||
register: prometheus_alerts_config
|
||||
|
||||
- name: GPU/LLM Monitoring | Validate Prometheus alert rules (YAML syntax)
|
||||
ansible.builtin.debug:
|
||||
msg: "Alert rules template ready at {{ prometheus_alerts_config.dest }}"
|
||||
when:
|
||||
- llm_monitoring_enabled
|
||||
- prometheus_alerts_config is changed
|
||||
|
||||
- name: GPU/LLM Monitoring | Validate Grafana dashboard JSON (JSON syntax)
|
||||
ansible.builtin.debug:
|
||||
msg: "Grafana dashboard template ready at {{ grafana_dashboard_config.dest }}"
|
||||
when:
|
||||
- llm_monitoring_enabled
|
||||
- grafana_dashboard_config is changed
|
||||
|
||||
- name: GPU/LLM Monitoring | Summary
|
||||
ansible.builtin.debug:
|
||||
msg: |
|
||||
GPU/LLM Monitoring Deployment Summary
|
||||
======================================
|
||||
Status: {{ 'ENABLED' if llm_monitoring_enabled else 'DISABLED' }}
|
||||
|
||||
Deployed Components:
|
||||
1. VRAM exporter: {{ llm_vram_exporter_script }}
|
||||
- Cron: Every minute (*/1 * * * *)
|
||||
- Output: {{ llm_vram_textfile_dir }}/nvidia.prom
|
||||
- Status: ✓ Running
|
||||
|
||||
2. Prometheus scrape config: /tmp/llama-swap-prometheus-scrape.yml
|
||||
- Target: {{ llm_bind_address }}:{{ llm_swapmode_port }}/metrics
|
||||
- Interval: {{ llm_prometheus_scrape_interval }}
|
||||
- Status: ✓ Templated (ready for GitOps deployment)
|
||||
|
||||
3. Grafana dashboard: /tmp/llama-swap-grafana-dashboard.json
|
||||
- Title: {{ llm_grafana_dashboard_title }}
|
||||
- UID: {{ llm_grafana_dashboard_uid }}
|
||||
- Panels: 6 (VRAM, KV-cache, Latency, Queue, Throughput, Percentiles)
|
||||
- Status: ✓ Templated (ready for GitOps deployment)
|
||||
|
||||
4. PrometheusRule alerts: /tmp/llama-swap-alerts.yml
|
||||
- Critical: VRAM > {{ llm_vram_critical_mib }} MiB
|
||||
- Warning: KV-cache > {{ llm_kv_cache_spill_ratio | round(2) }}
|
||||
- Warning: Throughput < {{ llm_throughput_baseline_tokens_per_min }} tokens/min
|
||||
- Status: ✓ Templated (ready for GitOps deployment)
|
||||
|
||||
Next Steps:
|
||||
1. Copy dashboard JSON to cluster/applications/monitoring/dashboards.yaml
|
||||
2. Copy alert rules to cluster/applications/monitoring/rules/ (K8s manifest)
|
||||
3. Add Prometheus scrape config to cluster/applications/monitoring/values.yaml
|
||||
4. Commit to Git and push (ArgoCD syncs automatically)
|
||||
5. Verify metrics appear in Prometheus UI within 2 minutes
|
||||
|
||||
Documentation:
|
||||
- Pattern spec: references/monitoring-llm-homelab-ciro-luciotta-2026.md
|
||||
- Phase 3 results: references/llama-swap-phase3-cutover-results-2026-08-18.md
|
||||
when: llm_monitoring_enabled
|
||||
304
ansible/roles/llm-inference-multimodel/tasks/swapmode.yml
Normal file
304
ansible/roles/llm-inference-multimodel/tasks/swapmode.yml
Normal file
@@ -0,0 +1,304 @@
|
||||
---
|
||||
# ------------------------------------------------------------------------------
|
||||
# FILE: roles/llm-inference-multimodel/tasks/swapmode.yml
|
||||
# DESCRIPTION: Phase S — llama-swap mode hot-swap proxy (port 8001).
|
||||
#
|
||||
# This phase is ADDITIVE and IDEMPOTENT. The existing production
|
||||
# unit (llama-server-qwen, port 8002) is never touched here.
|
||||
#
|
||||
# All tasks are gated on llm_swapmode_enabled | default(false).
|
||||
# With the default (false) this entire file is a no-op.
|
||||
#
|
||||
# When llm_swapmode_enabled: true (set by host_vars or extra-vars),
|
||||
# this phase:
|
||||
# swapmode_binary — download + install binary
|
||||
# swapmode_config — template config.yaml
|
||||
# swapmode_systemd — deploy llama-swap.service unit
|
||||
# swapmode_firewall — open port 8001 to Hermes subnet
|
||||
# swapmode_verify — start service, run 4 validation gates
|
||||
#
|
||||
# Tags map 1:1 to the sub-phases for independent execution:
|
||||
# --tags swapmode_binary,swapmode_config,swapmode_systemd,swapmode_firewall,swapmode_verify
|
||||
#
|
||||
# IMPORTANT: swapmode_verify starts the service. Do not run
|
||||
# swapmode_verify unless swapmode_binary and swapmode_systemd
|
||||
# have already run.
|
||||
#
|
||||
# Added 2026-08-18 (t_c1e44190): llama-swap Phase 3 Ansible integration — Wong.
|
||||
# Approved by War Machine Phase 1 validation (3 of 4 hard gates PASS).
|
||||
# Phase 3 gated on all profiles migrated + production router decommissioned.
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# =============================================================================
|
||||
# TAG: swapmode_binary
|
||||
# Download and install llama-swap binary from GitHub releases.
|
||||
# Idempotent: checks for existing binary and verifies architecture.
|
||||
# =============================================================================
|
||||
|
||||
- name: "[swapmode_binary] Detect host architecture (x86_64 / aarch64)"
|
||||
ansible.builtin.command:
|
||||
cmd: uname -m
|
||||
register: llm_swapmode_arch
|
||||
changed_when: false
|
||||
become: false
|
||||
when: llm_swapmode_enabled | default(false)
|
||||
tags: [swapmode_binary]
|
||||
|
||||
- name: "[swapmode_binary] Ensure config directory exists"
|
||||
ansible.builtin.file:
|
||||
path: "{{ llm_swapmode_config_dir }}"
|
||||
state: directory
|
||||
owner: "{{ llm_swapmode_service_user }}"
|
||||
group: "{{ llm_swapmode_service_user }}"
|
||||
mode: "0755"
|
||||
become: true
|
||||
when: llm_swapmode_enabled | default(false)
|
||||
tags: [swapmode_binary]
|
||||
|
||||
- name: "[swapmode_binary] Download llama-swap binary"
|
||||
ansible.builtin.get_url:
|
||||
url: "{{ llm_swapmode_binary_url }}"
|
||||
dest: "/tmp/llama-swap-{{ llm_swapmode_binary_version }}.tar.gz"
|
||||
checksum: "{{ llm_swapmode_checksum }}"
|
||||
mode: "0644"
|
||||
become: true
|
||||
register: llm_swapmode_download
|
||||
when: llm_swapmode_enabled | default(false)
|
||||
tags: [swapmode_binary]
|
||||
|
||||
- name: "[swapmode_binary] Extract llama-swap binary"
|
||||
ansible.builtin.unarchive:
|
||||
src: "/tmp/llama-swap-{{ llm_swapmode_binary_version }}.tar.gz"
|
||||
dest: /tmp
|
||||
remote_src: true
|
||||
creates: /tmp/llama-swap
|
||||
become: true
|
||||
when: llm_swapmode_enabled | default(false)
|
||||
tags: [swapmode_binary]
|
||||
|
||||
- name: "[swapmode_binary] Install llama-swap to /usr/local/bin"
|
||||
ansible.builtin.copy:
|
||||
src: /tmp/llama-swap
|
||||
dest: /usr/local/bin/llama-swap
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
remote_src: true
|
||||
become: true
|
||||
register: llm_swapmode_binary_installed
|
||||
when: llm_swapmode_enabled | default(false)
|
||||
tags: [swapmode_binary]
|
||||
|
||||
- name: "[swapmode_binary] Verify llama-swap binary is executable"
|
||||
ansible.builtin.command:
|
||||
cmd: /usr/local/bin/llama-swap --version
|
||||
register: llm_swapmode_version_check
|
||||
changed_when: false
|
||||
become: false
|
||||
when: llm_swapmode_enabled | default(false)
|
||||
tags: [swapmode_binary]
|
||||
|
||||
- name: "[swapmode_binary] Cleanup download artifacts"
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: absent
|
||||
become: true
|
||||
loop:
|
||||
- "/tmp/llama-swap-{{ llm_swapmode_binary_version }}.tar.gz"
|
||||
- /tmp/llama-swap
|
||||
when: llm_swapmode_enabled | default(false)
|
||||
tags: [swapmode_binary]
|
||||
|
||||
# =============================================================================
|
||||
# TAG: swapmode_config
|
||||
# Render config.yaml.j2 template and deploy to /etc/llama-swap/config.yaml
|
||||
# =============================================================================
|
||||
|
||||
- name: "[swapmode_config] Deploy llama-swap config.yaml from template"
|
||||
ansible.builtin.template:
|
||||
src: llama-swap-config.yaml.j2
|
||||
dest: "{{ llm_swapmode_config_file }}"
|
||||
owner: "{{ llm_swapmode_service_user }}"
|
||||
group: "{{ llm_swapmode_service_user }}"
|
||||
mode: "0644"
|
||||
become: true
|
||||
register: llm_swapmode_config_deployed
|
||||
when: llm_swapmode_enabled | default(false)
|
||||
tags: [swapmode_config]
|
||||
|
||||
- name: "[swapmode_config] Validate config.yaml syntax (YAML parse check)"
|
||||
ansible.builtin.command:
|
||||
cmd: python3 -c "import yaml; yaml.safe_load(open('{{ llm_swapmode_config_file }}'))"
|
||||
register: llm_swapmode_config_validate
|
||||
changed_when: false
|
||||
become: true
|
||||
when: llm_swapmode_enabled | default(false)
|
||||
tags: [swapmode_config]
|
||||
|
||||
# =============================================================================
|
||||
# TAG: swapmode_systemd
|
||||
# Deploy the llama-swap systemd unit file and reload systemd.
|
||||
# Does NOT start the service — that is swapmode_verify only.
|
||||
# =============================================================================
|
||||
|
||||
- name: "[swapmode_systemd] Deploy llama-swap systemd unit"
|
||||
ansible.builtin.template:
|
||||
src: llama-swap.service.j2
|
||||
dest: "/etc/systemd/system/{{ llm_swapmode_service_name }}.service"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
become: true
|
||||
register: llm_swapmode_unit_deployed
|
||||
notify:
|
||||
- reload systemd
|
||||
when: llm_swapmode_enabled | default(false)
|
||||
tags: [swapmode_systemd]
|
||||
|
||||
- name: "[swapmode_systemd] Flush handlers so daemon-reload lands before swapmode_verify starts the unit"
|
||||
ansible.builtin.meta: flush_handlers
|
||||
when: llm_swapmode_enabled | default(false)
|
||||
tags: [swapmode_systemd]
|
||||
|
||||
# =============================================================================
|
||||
# TAG: swapmode_firewall
|
||||
# Open port 8001 in ufw scoped to the Hermes source subnet.
|
||||
# Idempotent: named comment + state: present prevents duplicate rules.
|
||||
# =============================================================================
|
||||
|
||||
- name: "[swapmode_firewall] Check whether ufw is installed/active"
|
||||
ansible.builtin.command:
|
||||
cmd: ufw status
|
||||
register: llm_swapmode_ufw_status
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
become: true
|
||||
when: llm_swapmode_enabled | default(false)
|
||||
tags: [swapmode_firewall]
|
||||
|
||||
- name: "[swapmode_firewall] WARNING — ufw not active, port {{ llm_swapmode_port }} scoping cannot be applied"
|
||||
ansible.builtin.debug:
|
||||
msg: >-
|
||||
ufw does not appear to be active on this host. Firewall scoping for
|
||||
port {{ llm_swapmode_port }} was skipped. Bind address alone
|
||||
({{ llm_swapmode_bind_address }}) limits exposure — flag to Ryan.
|
||||
when:
|
||||
- llm_swapmode_enabled | default(false)
|
||||
- "'Status: active' not in (llm_swapmode_ufw_status.stdout | default(''))"
|
||||
tags: [swapmode_firewall]
|
||||
|
||||
- name: "[swapmode_firewall] Allow llama-swap port ({{ llm_swapmode_port }}) from Hermes source subnet"
|
||||
community.general.ufw:
|
||||
rule: allow
|
||||
port: "{{ llm_swapmode_port | string }}"
|
||||
proto: tcp
|
||||
src: "{{ llm_swapmode_allowed_source_cidr }}"
|
||||
comment: "llm-inference-multimodel: llama-swap ({{ llm_swapmode_port }}) — scoped to Hermes subnet"
|
||||
become: true
|
||||
when:
|
||||
- llm_swapmode_enabled | default(false)
|
||||
- "'Status: active' in (llm_swapmode_ufw_status.stdout | default(''))"
|
||||
tags: [swapmode_firewall]
|
||||
|
||||
# =============================================================================
|
||||
# TAG: swapmode_verify
|
||||
# Start the service, then run the 4 validation gates.
|
||||
# This is the ONLY phase that actually starts llama-swap.
|
||||
# =============================================================================
|
||||
|
||||
- name: "[swapmode_verify] Start llama-swap service"
|
||||
ansible.builtin.systemd:
|
||||
name: "{{ llm_swapmode_service_name }}"
|
||||
state: started
|
||||
enabled: true
|
||||
daemon_reload: true
|
||||
become: true
|
||||
when: llm_swapmode_enabled | default(false)
|
||||
tags: [swapmode_verify]
|
||||
|
||||
# GATE 1: Health check
|
||||
- name: "[swapmode_verify] GATE 1 — Health check (/health endpoint)"
|
||||
ansible.builtin.uri:
|
||||
url: "http://{{ llm_swapmode_bind_address }}:{{ llm_swapmode_port }}/health"
|
||||
method: GET
|
||||
status_code: 200
|
||||
register: llm_swapmode_health
|
||||
until: llm_swapmode_health.status == 200
|
||||
retries: 30
|
||||
delay: 2
|
||||
become: false
|
||||
when: llm_swapmode_enabled | default(false)
|
||||
tags: [swapmode_verify]
|
||||
|
||||
# GATE 2: Model discovery
|
||||
- name: "[swapmode_verify] GATE 2 — Model discovery (/v1/models)"
|
||||
ansible.builtin.uri:
|
||||
url: "http://{{ llm_swapmode_bind_address }}:{{ llm_swapmode_port }}/v1/models"
|
||||
method: GET
|
||||
status_code: 200
|
||||
register: llm_swapmode_models_list
|
||||
become: false
|
||||
when: llm_swapmode_enabled | default(false)
|
||||
tags: [swapmode_verify]
|
||||
|
||||
- name: "[swapmode_verify] Assert all 5 models are discoverable"
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- llm_swapmode_models_list.json.data | map(attribute='id') | list | length == 5
|
||||
fail_msg: >-
|
||||
Expected 5 models in /v1/models response, got {{ llm_swapmode_models_list.json.data | length }}.
|
||||
Models: {{ llm_swapmode_models_list.json.data | map(attribute='id') | list }}
|
||||
when: llm_swapmode_enabled | default(false)
|
||||
tags: [swapmode_verify]
|
||||
|
||||
# GATE 3: Smoke test — simple completion on a CPU-offload model (no VRAM conflict)
|
||||
- name: "[swapmode_verify] GATE 3 — Smoke test completion (Meta-Llama-3.1-8B CPU-offload)"
|
||||
ansible.builtin.uri:
|
||||
url: "http://{{ llm_swapmode_bind_address }}:{{ llm_swapmode_port }}/v1/chat/completions"
|
||||
method: POST
|
||||
body_format: json
|
||||
body:
|
||||
model: "Meta-Llama-3.1-8B-Instruct-Q4_K_M"
|
||||
messages:
|
||||
- role: "user"
|
||||
content: "What is 2+2?"
|
||||
temperature: 0.1
|
||||
max_tokens: 50
|
||||
status_code: 200
|
||||
register: llm_swapmode_smoke_test
|
||||
become: false
|
||||
when: llm_swapmode_enabled | default(false)
|
||||
tags: [swapmode_verify]
|
||||
|
||||
# GATE 4: VRAM guard check
|
||||
- name: "[swapmode_verify] GATE 4 — VRAM usage check (must be < {{ llm_swapmode_vram_max_mib }} MiB)"
|
||||
ansible.builtin.shell:
|
||||
cmd: nvidia-smi --query-gpu=memory.used --format=csv,noheader,nounits | head -1
|
||||
register: llm_swapmode_vram_used
|
||||
changed_when: false
|
||||
become: false
|
||||
when: llm_swapmode_enabled | default(false)
|
||||
tags: [swapmode_verify]
|
||||
|
||||
- name: "[swapmode_verify] Assert VRAM usage is within budget"
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- (llm_swapmode_vram_used.stdout | int) < llm_swapmode_vram_max_mib
|
||||
fail_msg: >-
|
||||
VRAM usage ({{ llm_swapmode_vram_used.stdout }} MiB) exceeds gate limit ({{ llm_swapmode_vram_max_mib }} MiB).
|
||||
Check for resource contention with production router or other services.
|
||||
when: llm_swapmode_enabled | default(false)
|
||||
tags: [swapmode_verify]
|
||||
|
||||
# Display verification results
|
||||
- name: "[swapmode_verify] Display verification results"
|
||||
ansible.builtin.debug:
|
||||
msg: |
|
||||
✓ GATE 1: Health check PASS
|
||||
✓ GATE 2: Model discovery PASS — {{ llm_swapmode_models_list.json.data | map(attribute='id') | list | join(', ') }}
|
||||
✓ GATE 3: Smoke test (Llama-3.1-8B) PASS
|
||||
✓ GATE 4: VRAM guard ({{ llm_swapmode_vram_used.stdout }} MiB < {{ llm_swapmode_vram_max_mib }} MiB) PASS
|
||||
|
||||
llama-swap service is ready at http://{{ llm_swapmode_bind_address }}:{{ llm_swapmode_port }}/
|
||||
when: llm_swapmode_enabled | default(false)
|
||||
tags: [swapmode_verify]
|
||||
Reference in New Issue
Block a user