Files
awoooi/docs/runbooks/RUNBOOK-OLLAMA-FAILOVER.md
Your Name 4111ea4f9f
All checks were successful
Code Review / ai-code-review (push) Successful in 12s
CD Pipeline / tests (push) Successful in 1m13s
CD Pipeline / build-and-deploy (push) Successful in 3m36s
CD Pipeline / post-deploy-checks (push) Successful in 1m20s
fix(ai): remove 188 ollama provider
2026-05-06 14:34:48 +08:00

241 lines
9.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# RUNBOOK-OLLAMA-FAILOVER.md
# Ollama 容災監控 Runbook
# 2026-04-26 P2.3 by Claude Sonnet 4.6 (tool-expert)
# 對應告警規則: ops/monitoring/ollama_health_rules.yaml
# 對應 Dashboard: ops/monitoring/grafana/dashboards/ollama_failover.json
---
## Grafana Dashboard 使用說明
Dashboard 路徑:`Ollama 容災監控`uid: `ollama-failover-p23`
匯入方式Grafana UI → Dashboards → Import → Upload JSON file → 選 `ops/monitoring/grafana/dashboards/ollama_failover.json`
### Panel 1 — Ollama 可用性 (Stat)
**看什麼**`up{job=~"ollama_gcp_a|ollama_gcp_b|ollama_local|ollama_111"}` × 100顯示每個 Ollama provider endpoint 的 scrape 存活狀態。
| 顏色 | 意義 |
|------|------|
| 綠色 100% | Prometheus 探測正常,主機在線 |
| 黃色 | 部分 endpoint 離線,系統應進入容災 |
| 紅色 0% | Ollama provider pool 全離線,高風險 |
**注意**:此面板反映 Prometheus scrape 狀態,需要 scrape job 命名對齊 `ollama_gcp_a` / `ollama_gcp_b` / `ollama_local`
設定檔位於 `ops/monitoring/generated/prometheus-scrape-generated.yaml`
---
### Panel 2 — 推理延遲 P50 / P99 (Time Series)
**看什麼**:推理延遲分位數。
| 門檻 | 含義 |
|------|------|
| < 10s (P50) | HEALTHY — 正常使用 111 |
| 1030s (P50) | SLOW — 系統已切至 Gemini |
| > 30s (P99) | DEGRADED — 應觸發 failover |
**⚠️ BACKLOG 警告**`ollama_inference_duration_seconds_bucket` 尚未在 API 暴露(需在 `_check_inference()` 加 Histogram.observe())。
面板顯示 "No Data" 是正常的,等 backlog 補完後啟用。
---
### Panel 3 — AI Provider 路由分布 (Pie Chart)
**看什麼**:過去 5 分鐘各 provider 被選中的請求比例。
| 分布 | 意義 |
|------|------|
| ollama / ollama_gcp_a 佔 >90% | 正常GCP-A 健康 |
| ollama_gcp_b 佔多數 | GCP-A SLOW/DEGRADED/OFFLINE容災到 GCP-B |
| ollama_local 出現 | GCP-A/B 均不可用,容災到 111 local |
| gemini 佔多數 | Ollama provider pool 全部不可用,使用付費備援 |
| 全部 nemotron/claude | 極端情況,所有主力 provider 失敗 |
---
### Panel 4 — Failover / Recovery 觸發次數 (Bar Chart)
**看什麼**:每小時 failover和 recovery的觸發次數。
| 模式 | 意義 |
|------|------|
| 兩條都接近 0 | 正常111 穩定運行中 |
| 橘色上升後綠色跟上 | Auto recovery 正常:切出後又切回 |
| 橘色上升,綠色不動 | **`OllamaRecoveryStuck` alert**,見下方 runbook |
| 橘色持續高頻(>5/h | **`OllamaFailoverFrequent` alert**111 不穩定 |
---
## Alert Runbook
### `OllamaInstanceDown` — Ollama 主機離線
**觸發條件**`up{job=~"ollama_gcp_a|ollama_gcp_b|ollama_local|ollama_111"} == 0` 持續 2 分鐘。
**影響評估**
- 系統應已依序切至 GCP-B / 111 local / Gemini查 Panel 3 確認)
- 查 Panel 4 是否有 Failover 計數上升
**排查步驟**
```bash
# 步驟 1確認主機存活
ping -c 3 192.168.0.111
# 步驟 2SSH 進主機確認 ollama 服務狀態
ssh wooo@192.168.0.111 'systemctl status ollama'
# 步驟 3查 ollama 最近的 journal log
ssh wooo@192.168.0.111 'journalctl -u ollama -n 50 --no-pager'
# 步驟 4確認 GPU 記憶體111 是 GPU 主機)
ssh wooo@192.168.0.111 'nvidia-smi'
# 步驟 5如果服務掛了重啟
ssh wooo@192.168.0.111 'systemctl restart ollama'
# 等 30s確認服務啟動
ssh wooo@192.168.0.111 'systemctl status ollama'
```
**恢復確認**
Panel 1 變綠色Panel 4 出現 Recovery 計數上升,表示 auto recovery 已觸發切回。
---
### `OllamaFailoverFrequent` — Failover 頻率過高
**觸發條件**`rate(ollama_failover_triggered_total[1h]) > 5` 持續 10 分鐘(每小時超過 5 次切換)。
**影響評估**
- 服務本身仍可用Gemini 在接手)
- 但 Gemini 配額消耗加速,有觸發 `GeminiQuotaApproaching` 的風險
**排查步驟**
```bash
# 步驟 1確認 111 近況(反覆 OFFLINE/HEALTHY 之間跳動?)
ssh wooo@192.168.0.111 'nvidia-smi --query-gpu=utilization.gpu,memory.used,memory.total --format=csv'
# 步驟 2查 API log 找 failover 原因
kubectl logs -n awoooi-prod deploy/api --since=30m | grep "ollama_failover_triggered"
# 步驟 3查推理延遲是否長期在 SLOW 邊界?)
kubectl logs -n awoooi-prod deploy/api --since=30m | grep "ollama_health_checked"
# 步驟 4如果是 GPU 記憶體問題,清除 model cache
ssh wooo@192.168.0.111 'systemctl restart ollama'
```
---
### `OllamaRecoveryStuck` — Auto Recovery 停滯
**觸發條件**`ollama_health_status{host="111"} == 1 AND ollama_current_primary_is_ollama == 0` 持續 5 分鐘。
111 已 HEALTHY 但路由仍走 Gemini
**影響評估**
- API 功能正常Gemini 在服務)
- 但 Gemini 配額持續消耗111 GPU 資源浪費
**排查步驟**
```bash
# 步驟 1確認 OllamaAutoRecoveryService 是否在運行
kubectl logs -n awoooi-prod deploy/api --since=10m | grep "ollama_auto_recovery"
# 步驟 2查 recovery service 狀態
kubectl logs -n awoooi-prod deploy/api --since=10m | grep -E "ollama_auto_recovery_started|ollama_auto_recovery_stopped|ollama_auto_recovery_loop_error"
# 步驟 3查 current_primary Redis key
kubectl exec -n awoooi-prod deploy/api -- python -c "
import asyncio
from src.core.redis_client import get_redis
async def check():
r = get_redis()
val = await r.get('ollama:current_primary')
print('current_primary:', val)
asyncio.run(check())
"
# 步驟 4如果 recovery service 掛了,重啟 API pod會重新啟動 lifespan
kubectl rollout restart deployment/api -n awoooi-prod
kubectl rollout status deployment/api -n awoooi-prod
```
---
### `GeminiQuotaApproaching` — Gemini 配額 >80%
**觸發條件**`gemini_daily_call_count / gemini_daily_quota > 0.8` 持續 5 分鐘。
**注意**`gemini_daily_quota` 來自 `settings.GEMINI_DAILY_QUOTA`(預設 1000
`gemini_daily_call_count` 從 Redis key `ollama:gemini_daily_count:{YYYY-MM-DD}` 讀取並刷新 Gauge。
**影響評估**
- 當日 Gemini 配額即將耗盡
- 耗盡後系統會自動切至 188 CPU-only 備援qwen2.5:7b-instruct速度較慢
**行動步驟**
```bash
# 步驟 1確認當日 Gemini 使用量
kubectl exec -n awoooi-prod deploy/api -- python -c "
import asyncio, datetime
from src.core.redis_client import get_redis
async def check():
r = get_redis()
today = datetime.date.today().isoformat()
val = await r.get(f'ollama:gemini_daily_count:{today}')
print(f'gemini_daily_count[{today}]:', val)
asyncio.run(check())
"
# 步驟 2確認 111 是否能快速恢復(讓流量切回 Ollama
ssh wooo@192.168.0.111 'systemctl status ollama && nvidia-smi'
# 步驟 3如需增加配額修改 settings
# k8s/awoooi-prod/04-configmap.yaml.patch-* 找 GEMINI_DAILY_QUOTA
# 改完後 kubectl apply + rollout restart
# 步驟 4緊急手動重置計數謹慎使用只在確認誤計時才用
# kubectl exec -n awoooi-prod deploy/api -- redis-cli DEL "ollama:gemini_daily_count:$(date +%Y-%m-%d)"
```
---
## Metric 清單
| Metric | 類型 | 狀態 | 說明 |
|--------|------|------|------|
| `up{job="ollama_gcp_a"}` | Gauge | ✅ 現有 | Prometheus scrape 存活 |
| `up{job="ollama_gcp_b"}` | Gauge | ✅ 現有 | Prometheus scrape 存活 |
| `up{job="ollama_local"}` | Gauge | ✅ 現有 | Prometheus scrape 存活 |
| `ollama_failover_triggered_total` | Counter | ✅ P2.3 補入 | failover 切換次數labels: from_provider, to_provider |
| `ollama_recovery_triggered_total` | Counter | ✅ P2.3 補入 | recovery 切回次數labels: from_provider |
| `ollama_health_status{host}` | Gauge | ✅ P2.3 補入 | 健康狀態 1=healthy, 0=not_healthy |
| `ollama_current_primary_is_ollama` | Gauge | ✅ P2.3 補入 | 1=primary 是 ollama, 0=failover 中 |
| `ai_router_selected_provider_total` | Counter | ✅ P2.3 補入 | AI router 選擇次數labels: provider |
| `gemini_daily_call_count` | Gauge | ✅ P2.3 補入 | 今日 Gemini 呼叫次數 |
| `gemini_daily_quota` | Gauge | ✅ P2.3 補入 | Gemini 每日配額 |
| `ollama_inference_duration_seconds` | Histogram | ⏳ BACKLOG | 推理延遲分布,需在 `_check_inference()` 加 observe |
| `post_execution_verification_total` | Counter | ⏳ BACKLOG | Verifier 執行次數,需 auto_repair_service.py 補入 |
| `post_execution_verification_failed_total` | Counter | ⏳ BACKLOG | Verifier 失敗次數,需 auto_repair_service.py 補入 |
## Backlog 補完指引
### `ollama_inference_duration_seconds`
`apps/api/src/services/ollama_health_monitor.py``_check_inference()` 方法結尾,加:
```python
from src.core.metrics import OLLAMA_INFERENCE_DURATION # 需先在 metrics.py 加 Histogram
OLLAMA_INFERENCE_DURATION.labels(host=host_label).observe(latency_ms / 1000)
```
### `post_execution_verification_*`
`apps/api/src/services/auto_repair_service.py` 的 verifier 路徑,加 Counter inc()。
需先確認 verifier 執行點grep `post_execution``verif` 找入口)。