# Move the `sec` pen-test seat (M.O.G.-SEC-27B) from ana-ml2 GPU 1 to GPU 0 and bring it up. # # Why: GPU 1 carries the five resident fleet seats (gen 46 GB + embed 9.8 + coder 8.4 + # rerank 3.5 + reward 2.1 = ~69.9 GB of 97.9), leaving ~28 GB. This seat reserves # MOG_GPU_MEM_UTIL=0.52 -> ~51 GB, so it could not start on GPU 1 at all. GPU 0 has been # idle since run 3c was stopped on 2026-08-26. Operator-directed 2026-08-28. # # ⚠ POWER. This re-arms the two-GPU load condition that tripped the Anaheim rack breaker # on 2026-08-26. One circuit feeds the whole rack including ana-gw and ana-wg, so a trip # costs the site AND the way back in. Idle draw is negligible; the risk materialises when # sec and gen are under concurrent load. Operator accepted this with the constraint stated. # # Labels only apply at container CREATION, so this uses `up -d`, never `restart` -- # the homepage description carries the GPU number and would otherwise stay stale. # # Run: scripts/elway infra-ops@ana-ml2 --playbook playbooks/mog-sec-move-to-gpu0.yaml # Model load is slow (22 GB + 262K ctx + MTP graft); the verify phase polls rather than # assuming readiness, and the compose healthcheck allows a 900s start_period. vars: stack_dir: /opt/docker/compose/mog-sec container: vllm-mog-sec service: vllm-mog-sec gpu_id: "0" port: "8019" staging: /tmp/mog-sec-compose.yaml steps: - name: Stage the updated compose (GPU pin default + label now say GPU 0) upload: src: stacks/mog-sec/compose.yaml dest: "{{ staging }}" mode: "0644" - name: Install it over the deployed copy # /opt/docker/compose is root-owned, so the scp above lands in /tmp and this # promotes it. Verified byte-identical against the deployed file beforehand: # the only diff was these edits, so nothing on the host is being clobbered. shell: sudo install -o root -g root -m 0644 {{ staging }} {{ stack_dir }}/compose.yaml changed_when: "! sudo cmp -s {{ staging }} {{ stack_dir }}/compose.yaml" - name: Pin the seat to GPU {{ gpu_id }} in the host .env # The .env is the tunable surface and is NOT in git (secrets//tunables are # excluded both directions). The compose default now matches, but the .env # is what actually decides, so set it explicitly rather than relying on the # default resolving. shell: sudo sed -i 's/^MOG_GPU_ID=.*/MOG_GPU_ID={{ gpu_id }}/' {{ stack_dir }}/.env when: "! sudo grep -qxF 'MOG_GPU_ID={{ gpu_id }}' {{ stack_dir }}/.env" - name: Bring the seat up (up -d, not restart — labels apply at creation) shell: cd {{ stack_dir }} && sudo docker compose up -d {{ service }} verify: - name: Container exists and is running # ⚠ No `docker inspect -f` here. Go templates use {{ }} and so does elway's own # variable substitution, so an inspect format string gets eaten before it reaches # the host -- these two checks reported FAILED on a deploy that had in fact # succeeded. Filter-and-grep has no such collision. shell: sudo docker ps --filter name={{ container }} --filter status=running --quiet | grep -q . changed_when: "false" - name: The container is actually pinned to GPU {{ gpu_id }} # Assert the EFFECTIVE device reservation on the running container, not the # .env string we wrote -- the .env is an input, this is the outcome. shell: sudo docker inspect {{ container }} | tr -d ' \n' | grep -q '"DeviceIDs":\["{{ gpu_id }}"\]' changed_when: "false" - name: GPU 0 now holds a vLLM process (the seat really loaded onto that card) shell: nvidia-smi --id={{ gpu_id }} --query-compute-apps=pid,used_memory --format=csv,noheader | grep -qE '[0-9]' changed_when: "false" - name: Health endpoint answers shell: curl -fsS --max-time 10 http://127.0.0.1:{{ port }}/health >/dev/null changed_when: "false" - name: Both served names are advertised (base + thinking) shell: | MODELS=$(curl -fsS --max-time 10 http://127.0.0.1:{{ port }}/v1/models) echo "$MODELS" | grep -q 'mog-sec-27b' && echo "$MODELS" | grep -q 'mog-sec-27b-thinking' changed_when: "false"