|
| 1 | +#!/bin/bash |
| 2 | +# Swap Ollama model: pull new model, remove old, verify GPU loading |
| 3 | +# Usage: update-model.sh [new_model] [old_model] |
| 4 | +# |
| 5 | +# Examples: |
| 6 | +# update-model.sh # default: pull qwen3:4b, remove qwen2.5:3b |
| 7 | +# update-model.sh qwen3:4b # pull qwen3:4b only (no removal) |
| 8 | +# update-model.sh qwen3:4b qwen2.5:3b # pull new, remove old |
| 9 | +# |
| 10 | +# This script: |
| 11 | +# 1. Pulls the new model via Ollama API |
| 12 | +# 2. Verifies the new model loaded on GPU |
| 13 | +# 3. Removes the old model to free PVC space (if specified) |
| 14 | +# 4. Updates HA conversation agent (optional, with --ha flag) |
| 15 | + |
| 16 | +set -e |
| 17 | + |
| 18 | +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
| 19 | +KUBECONFIG="${KUBECONFIG:-$HOME/kubeconfig}" |
| 20 | +export KUBECONFIG |
| 21 | + |
| 22 | +OLLAMA_NS="${OLLAMA_NS:-ollama}" |
| 23 | +NEW_MODEL="${1:-qwen3:4b}" |
| 24 | +OLD_MODEL="${2:-qwen2.5:3b}" |
| 25 | +UPDATE_HA=false |
| 26 | + |
| 27 | +# Parse flags |
| 28 | +for arg in "$@"; do |
| 29 | + [[ "$arg" == "--ha" ]] && UPDATE_HA=true |
| 30 | +done |
| 31 | + |
| 32 | +# Get pod name |
| 33 | +POD=$(kubectl get pods -n "$OLLAMA_NS" -l app=ollama-gpu -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) |
| 34 | +if [[ -z "$POD" ]]; then |
| 35 | + echo "ERROR: No ollama-gpu pod found in namespace $OLLAMA_NS" |
| 36 | + exit 1 |
| 37 | +fi |
| 38 | + |
| 39 | +# Get Ollama URL |
| 40 | +OLLAMA_IP=$(kubectl get svc -n "$OLLAMA_NS" -o jsonpath='{.items[0].status.loadBalancer.ingress[0].ip}' 2>/dev/null) |
| 41 | +OLLAMA_PORT=$(kubectl get svc -n "$OLLAMA_NS" -o jsonpath='{.items[0].spec.ports[0].port}' 2>/dev/null) |
| 42 | +OLLAMA_URL="http://${OLLAMA_IP}:${OLLAMA_PORT:-80}" |
| 43 | + |
| 44 | +echo "=== Ollama Model Swap ===" |
| 45 | +echo "Pod: $POD" |
| 46 | +echo "URL: $OLLAMA_URL" |
| 47 | +echo "New model: $NEW_MODEL" |
| 48 | +echo "Old model: $OLD_MODEL" |
| 49 | +echo "" |
| 50 | + |
| 51 | +# 1. Show current models |
| 52 | +echo "--- Current Models ---" |
| 53 | +kubectl exec -n "$OLLAMA_NS" "$POD" -- ollama list 2>/dev/null || echo " No models found" |
| 54 | +echo "" |
| 55 | + |
| 56 | +# 2. Pull new model |
| 57 | +echo "--- Pulling $NEW_MODEL ---" |
| 58 | +echo "This may take a few minutes depending on model size..." |
| 59 | +kubectl exec -n "$OLLAMA_NS" "$POD" -- ollama pull "$NEW_MODEL" |
| 60 | +echo "" |
| 61 | + |
| 62 | +# 3. Verify new model exists |
| 63 | +echo "--- Verifying $NEW_MODEL ---" |
| 64 | +if kubectl exec -n "$OLLAMA_NS" "$POD" -- ollama list 2>/dev/null | grep -q "$NEW_MODEL"; then |
| 65 | + echo "OK: $NEW_MODEL is available" |
| 66 | +else |
| 67 | + echo "ERROR: $NEW_MODEL not found after pull" |
| 68 | + exit 1 |
| 69 | +fi |
| 70 | +echo "" |
| 71 | + |
| 72 | +# 4. Test inference on GPU |
| 73 | +echo "--- Testing inference (GPU check) ---" |
| 74 | +RESPONSE=$(curl -s --connect-timeout 30 --max-time 120 \ |
| 75 | + -X POST "$OLLAMA_URL/api/generate" \ |
| 76 | + -d "{\"model\":\"$NEW_MODEL\",\"prompt\":\"Say hello in one sentence.\",\"stream\":false}" 2>/dev/null) |
| 77 | + |
| 78 | +echo "$RESPONSE" | python3 -c " |
| 79 | +import json, sys |
| 80 | +try: |
| 81 | + d = json.load(sys.stdin) |
| 82 | + resp = d.get('response', 'N/A') |
| 83 | + eval_s = d.get('eval_duration', 0) / 1e9 |
| 84 | + tokens = d.get('eval_count', 0) |
| 85 | + tps = tokens / eval_s if eval_s > 0 else 0 |
| 86 | + print(f' Response: {resp[:100]}') |
| 87 | + print(f' Speed: {tps:.1f} tokens/sec ({tokens} tokens in {eval_s:.1f}s)') |
| 88 | + if tps < 5: |
| 89 | + print(f' WARNING: Likely running on CPU!') |
| 90 | + else: |
| 91 | + print(f' OK: GPU acceleration confirmed') |
| 92 | +except Exception as e: |
| 93 | + print(f'ERROR: {e}') |
| 94 | + sys.exit(1) |
| 95 | +" 2>/dev/null |
| 96 | +echo "" |
| 97 | + |
| 98 | +# 5. GPU memory check |
| 99 | +echo "--- GPU Memory ---" |
| 100 | +kubectl exec -n "$OLLAMA_NS" "$POD" -- nvidia-smi --query-gpu=memory.used,memory.total --format=csv,noheader 2>/dev/null || echo " nvidia-smi unavailable" |
| 101 | +echo "" |
| 102 | + |
| 103 | +# 6. Remove old model (if different from new) |
| 104 | +if [[ -n "$OLD_MODEL" && "$OLD_MODEL" != "$NEW_MODEL" ]]; then |
| 105 | + if kubectl exec -n "$OLLAMA_NS" "$POD" -- ollama list 2>/dev/null | grep -q "$OLD_MODEL"; then |
| 106 | + echo "--- Removing old model: $OLD_MODEL ---" |
| 107 | + kubectl exec -n "$OLLAMA_NS" "$POD" -- ollama rm "$OLD_MODEL" |
| 108 | + echo "Removed $OLD_MODEL" |
| 109 | + else |
| 110 | + echo "--- Old model $OLD_MODEL not present (skipping removal) ---" |
| 111 | + fi |
| 112 | + echo "" |
| 113 | +fi |
| 114 | + |
| 115 | +# 7. Final model list |
| 116 | +echo "--- Final Models ---" |
| 117 | +kubectl exec -n "$OLLAMA_NS" "$POD" -- ollama list 2>/dev/null |
| 118 | +echo "" |
| 119 | + |
| 120 | +# 8. Optionally update HA |
| 121 | +if [[ "$UPDATE_HA" == true ]]; then |
| 122 | + echo "--- Updating HA conversation agent ---" |
| 123 | + "$SCRIPT_DIR/set-ha-model.sh" "$NEW_MODEL" |
| 124 | +fi |
| 125 | + |
| 126 | +echo "=== Done ===" |
0 commit comments