diff --git a/scripts/bench-spec-stack.sh b/scripts/bench-spec-stack.sh index 005b089..857a003 100755 --- a/scripts/bench-spec-stack.sh +++ b/scripts/bench-spec-stack.sh @@ -16,7 +16,7 @@ SPEC="${1:?usage: bench-spec-stack.sh [out_tokens]}" CTX="${2:?usage: bench-spec-stack.sh [out_tokens]}" OUT_TOKENS="${3:-256}" -ENV_FILE=/home/dank/vllmScripts/scripts/frank-default-large.env +ENV_FILE=/home/dank/serverAppConfigs/scripts/frank-default-large.env source "$ENV_FILE" # This harness benchmarks the retired v3 stack. Force the v3 values so the diff --git a/scripts/deploy.sh b/scripts/deploy.sh index 5835277..7eea0f3 100755 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -1,20 +1,29 @@ #!/usr/bin/env bash -# deploy.sh — Deploy the serverAppConfigs repo to a target and restart its stack. +# deploy.sh — Deploy the serverAppConfigs repo to a target and restart its +# podman stack and LLM fleet services. # -# Implements the git deployment workflow from AGENTS.md. Assumes local changes -# are committed and pushed, then: +# Implements the git deployment workflow from AGENTS.md. Assumes local +# changes are committed and pushed, then: # 1. Pushes main to origin # 2. Pulls the repo on the target -# 3. Copies shared configs into place -# 4. Restarts the podman stack (podman-compose up -d) -# 5. Waits for the Caddy homepage to respond +# 3. Copies shared configs into place and restarts the podman stack +# (dankclaw only; the dankserver media stack runs as bare containers) +# 4. Relinks the LLM systemd units and restarts the active LLM service +# 5. Waits for the Caddy homepage and the LLM health endpoint +# +# Sudo: the fleet hosts allow passwordless sudo for the deploy user. For +# headless runs on a host without that, set FRANK_SUDO_PW so remote sudo +# reads the password from stdin (`sudo -S`). The password is never stored +# in this repo. # # Usage: # ./scripts/deploy.sh [--dry-run] # # Targets: -# dankclaw - AI/search/dev/observability stack (dankclaw/compose.yaml) -# dankserver - media/library stack (dankserver/compose.yaml) +# dankclaw - AI/search/dev/observability stack + frank-default-large +# (CIRU ROCmFPX llama.cpp, Qwen3.8-27B, port 52625) +# dankserver - media/library stack (bare containers) + frank-default-small +# (upstream llama.cpp, Qwen3.6-35B-A3B, port 7770) # # Example: # ./scripts/deploy.sh dankclaw @@ -25,10 +34,11 @@ set -euo pipefail # Target configuration # --------------------------------------------------------------------------- declare -A REMOTE_USER REMOTE_HOST REMOTE_DIR COMPOSE HEALTH_URL COMPOSE_UP COPY_SHARED +declare -A LLM_SERVICES LLM_UNITS LLM_HEALTH LLM_WAIT # Caddy is consolidated on dankclaw: it fronts both hosts' services. The # dankserver media stack runs as bare containers (not this repo's compose), -# so its deploy is pull-only; health is checked against the shared Caddy. +# so its stack deploy is pull-only; health is checked against the shared Caddy. REMOTE_USER[dankclaw]=dank REMOTE_HOST[dankclaw]=dankclaw REMOTE_DIR[dankclaw]="/home/dank/serverAppConfigs" @@ -36,6 +46,16 @@ COMPOSE[dankclaw]="dankclaw/compose.yaml" HEALTH_URL[dankclaw]="http://127.0.0.1/" COMPOSE_UP[dankclaw]=1 COPY_SHARED[dankclaw]=1 +# LLM: frank-default-large is the active service (restart + health wait). +# frank-default-npu is relinked only (not enabled on the host); its unit +# picks up the new paths at its next manual start. +LLM_SERVICES[dankclaw]="frank-default-large" +LLM_UNITS[dankclaw]="frank-default-large frank-default-npu" +LLM_HEALTH[dankclaw]="http://127.0.0.1:52625/health" +# CIRU llama.cpp (ROCm 7.15) loads the v3 five-file payload (GGUF + FFN + +# GDN + Output-K8 .pfs + mmproj, ~36.7 GiB) + MTP draft/Output-K8 proxy and +# initializes PromptForge views; allow 20 minutes. +LLM_WAIT[dankclaw]=600 REMOTE_USER[dankserver]=dank REMOTE_HOST[dankserver]=dankserver @@ -44,6 +64,13 @@ COMPOSE[dankserver]="dankserver/compose.yaml" HEALTH_URL[dankserver]="http://192.168.0.143/" COMPOSE_UP[dankserver]=0 COPY_SHARED[dankserver]=0 +# LLM: frank-default-small is the active service (restart + health wait). +LLM_SERVICES[dankserver]="frank-default-small" +LLM_UNITS[dankserver]="frank-default-small" +LLM_HEALTH[dankserver]="http://127.0.0.1:7770/health" +# Cold load: --no-mmap reads the 30.6 GB Q6_K_P through RAM (MoE experts on +# CPU); allow 20 minutes. +LLM_WAIT[dankserver]=600 TARGET="${1:-}" DRY_RUN=0 @@ -63,6 +90,14 @@ if [ -z "${REMOTE_HOST[$TARGET]:-}" ]; then exit 1 fi +# --------------------------------------------------------------------------- +# Sudo wrapper (headless-friendly) +# --------------------------------------------------------------------------- +SUDO="sudo" +if [ -n "${FRANK_SUDO_PW:-}" ]; then + SUDO="printf '%s\n' '$FRANK_SUDO_PW' | sudo -S" +fi + # --------------------------------------------------------------------------- # Local checks (pre-flight) # --------------------------------------------------------------------------- @@ -77,10 +112,12 @@ if [ "$(git rev-parse --abbrev-ref HEAD)" != "main" ]; then fi echo "=== Deploying to ${TARGET} ===" -echo " host: ${REMOTE_HOST[$TARGET]}" -echo " dir: ${REMOTE_DIR[$TARGET]}" -echo " compose: ${COMPOSE[$TARGET]}" -echo " health: ${HEALTH_URL[$TARGET]}" +echo " host: ${REMOTE_HOST[$TARGET]}" +echo " dir: ${REMOTE_DIR[$TARGET]}" +echo " compose: ${COMPOSE[$TARGET]}" +echo " llm svc: ${LLM_SERVICES[$TARGET]}" +echo " llm health: ${LLM_HEALTH[$TARGET]}" +echo " homepage: ${HEALTH_URL[$TARGET]}" echo "" # --------------------------------------------------------------------------- @@ -90,7 +127,7 @@ if [ "$DRY_RUN" = "1" ]; then echo "[dry-run] Would push local main to origin" echo "[dry-run] Would SSH to ${REMOTE_HOST[$TARGET]} and git pull" else - echo "[1/4] Pushing local main to origin..." + echo "[1/5] Pushing local main to origin..." git push origin main fi @@ -99,13 +136,13 @@ REMOTE="$( )" # --------------------------------------------------------------------------- -# Remote: pull + copy shared configs + restart stack +# Remote: pull + copy shared configs + restart stack + restart LLM service # --------------------------------------------------------------------------- REMOTE_SCRIPT=" set -euo pipefail cd '${REMOTE_DIR[$TARGET]}' -echo '[2/4] Pulling repo on ${TARGET}...' +echo '[2/5] Pulling repo on ${TARGET}...' git pull origin main if [ -f .env ]; then @@ -115,7 +152,7 @@ else fi if [ '${COPY_SHARED[$TARGET]}' = '1' ]; then - echo '[3/4] Copying shared configs into place...' + echo '[3/5] Copying shared configs into place...' mkdir -p /opt/searxng/config /opt/prometheus/config /opt/loki/config /opt/alloy/config cp shared/searxng/settings.yml /opt/searxng/config/settings.yml cp shared/searxng/limiter.toml /opt/searxng/config/limiter.toml @@ -138,7 +175,7 @@ if [ '${COPY_SHARED[$TARGET]}' = '1' ]; then fi if [ '${COMPOSE_UP[$TARGET]}' = '1' ]; then - echo '[4/4] Restarting stack...' + echo '[4/5] Restarting stack...' # Locally built images: caddy + porkbun module, and loki/alloy + a static # busybox (their upstream images lack the wget the healthchecks need). for dir in caddy loki alloy; do @@ -177,8 +214,19 @@ if [ '${COMPOSE_UP[$TARGET]}' = '1' ]; then podman restart caddy fi else - echo '[4/4] Skipping compose restart (stack runs as bare containers on this host)' + echo '[4/5] Skipping compose restart (stack runs as bare containers on this host)' fi + +echo '[4/5] Relinking LLM units and restarting LLM services...' +for unit in ${LLM_UNITS[$TARGET]}; do + ${SUDO} ln -sf '${REMOTE_DIR[$TARGET]}/scripts/\${unit}.service' /etc/systemd/system/\${unit}.service +done +${SUDO} systemctl daemon-reload +for svc in ${LLM_SERVICES[$TARGET]}; do + ${SUDO} systemctl enable \"\${svc}\" + ${SUDO} systemctl restart \"\${svc}\" + echo \" restarted \${svc}\" +done " if [ "$DRY_RUN" = "1" ]; then @@ -192,22 +240,37 @@ REMOTE_EOF fi # --------------------------------------------------------------------------- -# Health check +# Health checks: homepage, then the LLM endpoint # --------------------------------------------------------------------------- if [ "$DRY_RUN" = "1" ]; then - echo "[dry-run] Would wait for health at ${HEALTH_URL[$TARGET]}" + echo "[dry-run] Would wait for health at ${HEALTH_URL[$TARGET]} and ${LLM_HEALTH[$TARGET]}" else - echo "[5/4] Waiting for health endpoint ${HEALTH_URL[$TARGET]} (via ssh)..." + echo "[5/5] Waiting for homepage ${HEALTH_URL[$TARGET]} (via ssh)..." HEALTH="${HEALTH_URL[$TARGET]}" for i in $(seq 1 60); do if ssh "$REMOTE" "curl -sf '$HEALTH'" > /dev/null 2>&1; then echo " Homepage healthy after approx $((i * 2))s" + break + fi + sleep 2 + done + if ! ssh "$REMOTE" "curl -sf '$HEALTH'" > /dev/null 2>&1; then + echo "FAIL: Homepage did not become healthy in 120s" + exit 1 + fi + + echo "[5/5] Waiting for LLM health ${LLM_HEALTH[$TARGET]} (via ssh)..." + LLM_URL="${LLM_HEALTH[$TARGET]}" + LLM_ITERS="${LLM_WAIT[$TARGET]}" + for i in $(seq 1 "$LLM_ITERS"); do + if ssh "$REMOTE" "curl -sf '$LLM_URL'" > /dev/null 2>&1; then + echo " LLM healthy after approx $((i * 2))s" echo "" echo "=== Deploy complete: ${TARGET} ===" exit 0 fi sleep 2 done - echo "FAIL: Homepage did not become healthy in 120s" + echo "FAIL: LLM service did not become healthy in $((LLM_ITERS * 2))s" exit 1 fi diff --git a/scripts/frank-default-large.service b/scripts/frank-default-large.service index 70cdd45..c7834dd 100644 --- a/scripts/frank-default-large.service +++ b/scripts/frank-default-large.service @@ -7,8 +7,8 @@ Wants=network-online.target Type=simple User=dank Group=dank -Environment="FRANK_LARGE_ENV_FILE=/home/dank/vllmScripts/scripts/frank-default-large.env" -ExecStart=/home/dank/vllmScripts/scripts/serve-frank-large.sh +Environment="FRANK_LARGE_ENV_FILE=/home/dank/serverAppConfigs/scripts/frank-default-large.env" +ExecStart=/home/dank/serverAppConfigs/scripts/serve-frank-large.sh Restart=on-failure RestartSec=10 diff --git a/scripts/frank-default-npu.service b/scripts/frank-default-npu.service index 1dadeb6..14a7af5 100644 --- a/scripts/frank-default-npu.service +++ b/scripts/frank-default-npu.service @@ -7,8 +7,8 @@ Wants=network-online.target Type=simple User=dank Group=dank -Environment="FLM_NPU_ENV_FILE=/home/dank/vllmScripts/scripts/frank-default-npu.env" -ExecStart=/home/dank/vllmScripts/scripts/serve-frank-default-npu.sh +Environment="FLM_NPU_ENV_FILE=/home/dank/serverAppConfigs/scripts/frank-default-npu.env" +ExecStart=/home/dank/serverAppConfigs/scripts/serve-frank-default-npu.sh LimitMEMLOCK=infinity Restart=on-failure RestartSec=10 diff --git a/scripts/frank-default-small.service b/scripts/frank-default-small.service index 0a27cd2..33d8415 100644 --- a/scripts/frank-default-small.service +++ b/scripts/frank-default-small.service @@ -7,8 +7,8 @@ Wants=network-online.target Type=simple User=dank Group=dank -Environment="FRANK_SMALL_ENV_FILE=/home/dank/vLLM/scripts/frank-default-small.env" -ExecStart=/home/dank/vLLM/scripts/serve-frank-small.sh +Environment="FRANK_SMALL_ENV_FILE=/home/dank/serverAppConfigs/scripts/frank-default-small.env" +ExecStart=/home/dank/serverAppConfigs/scripts/serve-frank-small.sh Restart=on-failure RestartSec=10