新增: - ADR-025: 告警鏈路 E2E 驗證架構 (2026-03-26 事故教訓) 更新: - ADR-011: 新增 DNS 規則最佳實踐 (附錄 B) - Skill 04: 新增 NetworkPolicy DNS 規則 + CoreDNS 設定 - Skill 05: 新增告警鏈路 Smoke Test 要求 - CLAUDE.md: 新增告警鏈路驗證到任務前必讀 事故根因: 1. URL 路徑錯誤 (webhook vs webhooks) 2. NetworkPolicy DNS 規則標籤不匹配 3. CoreDNS 上游 DNS 依賴 systemd-resolved Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
197 lines
8.8 KiB
Markdown
197 lines
8.8 KiB
Markdown
# ADR-025: 告警鏈路 E2E 驗證架構
|
||
|
||
**狀態**: 批准
|
||
**日期**: 2026-03-26
|
||
**決策者**: 統帥
|
||
**觸發**: URL 路徑錯誤 + NetworkPolicy DNS 規則錯誤導致 2 天無告警
|
||
|
||
## 問題陳述
|
||
|
||
```
|
||
事故時間線 (2026-03-26):
|
||
├── Alertmanager 設定 /api/v1/webhook/alertmanager (單數)
|
||
├── API 實際路徑 /api/v1/webhooks/alertmanager (複數)
|
||
├── 結果: 404 Not Found,所有告警丟失
|
||
├── 同時: NetworkPolicy DNS 規則使用錯誤標籤
|
||
├── CoreDNS 無法解析外部 DNS (使用 127.0.0.53)
|
||
└── 後果: 2 天完全無 Telegram 告警
|
||
```
|
||
|
||
**根本原因**:
|
||
1. 沒有 E2E 測試驗證 Alertmanager → API → Telegram 鏈路
|
||
2. 部署後沒有 Smoke Test 確認端點可達
|
||
3. NetworkPolicy DNS 規則標籤與 CoreDNS 不匹配
|
||
4. CoreDNS 上游 DNS 設定依賴 systemd-resolved (容器內無效)
|
||
|
||
---
|
||
|
||
## 決策:四層驗證架構
|
||
|
||
```
|
||
┌──────────────────────────────────────────────────────────────────────┐
|
||
│ 告警鏈路 E2E 驗證架構 │
|
||
├──────────────────────────────────────────────────────────────────────┤
|
||
│ │
|
||
│ Layer 1: 部署後 Smoke Test (強制) │
|
||
│ ═══════════════════════════════════ │
|
||
│ ┌─────────────────────────────────────────────────────────────┐ │
|
||
│ │ 每次部署後自動執行: │ │
|
||
│ │ 1. curl POST /api/v1/webhooks/alertmanager (測試告警) │ │
|
||
│ │ 2. 驗證回應 success=true │ │
|
||
│ │ 3. 驗證 Telegram message_id 存在 │ │
|
||
│ │ 4. 失敗 → 部署回滾 │ │
|
||
│ └─────────────────────────────────────────────────────────────┘ │
|
||
│ │
|
||
│ Layer 2: DNS 連通性檢查 │
|
||
│ ══════════════════════════ │
|
||
│ ┌─────────────────────────────────────────────────────────────┐ │
|
||
│ │ Health Probe 必須包含: │ │
|
||
│ │ - 內部 DNS: kubernetes.default.svc.cluster.local │ │
|
||
│ │ - 外部 DNS: api.telegram.org │ │
|
||
│ │ 任一失敗 → Pod 標記 Not Ready │ │
|
||
│ └─────────────────────────────────────────────────────────────┘ │
|
||
│ │
|
||
│ Layer 3: 鏈路心跳監控 │
|
||
│ ════════════════════════ │
|
||
│ ┌─────────────────────────────────────────────────────────────┐ │
|
||
│ │ Prometheus 規則: │ │
|
||
│ │ - awoooi_alerts_received_total │ │
|
||
│ │ - awoooi_telegram_sent_total │ │
|
||
│ │ 連續 1 小時為 0 → 觸發 CRITICAL 告警 │ │
|
||
│ └─────────────────────────────────────────────────────────────┘ │
|
||
│ │
|
||
│ Layer 4: ConfigMap 驗證 Hook │
|
||
│ ════════════════════════════ │
|
||
│ ┌─────────────────────────────────────────────────────────────┐ │
|
||
│ │ Alertmanager ConfigMap 修改前: │ │
|
||
│ │ 1. 提取 webhook URL │ │
|
||
│ │ 2. curl 測試 URL 可達性 │ │
|
||
│ │ 3. 必須收到 200 或 422 (格式錯但端點存在) │ │
|
||
│ │ 4. 驗證失敗 → 阻止 apply │ │
|
||
│ └─────────────────────────────────────────────────────────────┘ │
|
||
│ │
|
||
└──────────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## 實施細節
|
||
|
||
### 1. 部署後 Smoke Test
|
||
|
||
```yaml
|
||
# CI/CD 強制步驟
|
||
- name: Alert Chain Smoke Test
|
||
run: |
|
||
# 發送測試告警
|
||
RESPONSE=$(curl -s -X POST "$API_URL/api/v1/webhooks/alertmanager" \
|
||
-H 'Content-Type: application/json' \
|
||
-d '{"receiver":"smoke-test","status":"firing","alerts":[{"status":"firing","labels":{"alertname":"SmokeTest","severity":"info"},"annotations":{"summary":"CI Smoke Test"}}]}')
|
||
|
||
# 驗證成功
|
||
echo "$RESPONSE" | jq -e '.success == true' || exit 1
|
||
echo "Alert chain smoke test passed"
|
||
```
|
||
|
||
### 2. NetworkPolicy DNS 規則 (正確寫法)
|
||
|
||
```yaml
|
||
# ❌ 錯誤: 使用不存在的標籤
|
||
- ports:
|
||
- port: 53
|
||
to:
|
||
- podSelector:
|
||
matchLabels:
|
||
environment: prod # CoreDNS 沒有這個標籤!
|
||
k8s-app: kube-dns
|
||
system: awoooi # CoreDNS 沒有這個標籤!
|
||
|
||
# ✅ 正確: 使用 namespace selector
|
||
- ports:
|
||
- port: 53
|
||
protocol: UDP
|
||
- port: 53
|
||
protocol: TCP
|
||
to:
|
||
- namespaceSelector:
|
||
matchLabels:
|
||
kubernetes.io/metadata.name: kube-system
|
||
podSelector:
|
||
matchLabels:
|
||
k8s-app: kube-dns
|
||
```
|
||
|
||
### 3. CoreDNS 上游 DNS 設定
|
||
|
||
```yaml
|
||
# ❌ 錯誤: 使用 /etc/resolv.conf (指向 127.0.0.53)
|
||
forward . /etc/resolv.conf
|
||
|
||
# ✅ 正確: 使用真實 DNS 伺服器
|
||
forward . 8.8.8.8 1.1.1.1
|
||
```
|
||
|
||
### 4. Prometheus 鏈路監控規則
|
||
|
||
```yaml
|
||
groups:
|
||
- name: alert-chain-health
|
||
rules:
|
||
- alert: AlertChainBroken
|
||
expr: increase(awoooi_alerts_received_total[1h]) == 0
|
||
for: 1h
|
||
labels:
|
||
severity: critical
|
||
annotations:
|
||
summary: "告警鏈路斷裂!1 小時內沒有收到任何告警"
|
||
|
||
- alert: TelegramNotificationFailed
|
||
expr: increase(awoooi_telegram_sent_total[1h]) == 0 and increase(awoooi_alerts_received_total[1h]) > 0
|
||
for: 30m
|
||
labels:
|
||
severity: critical
|
||
annotations:
|
||
summary: "Telegram 通知失敗!有告警但沒有發送成功"
|
||
```
|
||
|
||
---
|
||
|
||
## URL 路徑規範
|
||
|
||
| 正確 | 錯誤 |
|
||
|-----|------|
|
||
| `/api/v1/webhooks/alertmanager` | `/api/v1/webhook/alertmanager` |
|
||
| 複數形式 `webhooks` | 單數形式 `webhook` |
|
||
| `/api/v1/approvals` | `/api/v1/approval` |
|
||
| `/api/v1/incidents` | `/api/v1/incident` |
|
||
|
||
**原則**: API Router 統一使用複數命名
|
||
|
||
---
|
||
|
||
## 驗收標準
|
||
|
||
| 項目 | 狀態 |
|
||
|------|------|
|
||
| CI/CD 包含 Alert Chain Smoke Test | ⬜ |
|
||
| NetworkPolicy DNS 規則使用正確標籤 | ✅ |
|
||
| CoreDNS 使用真實上游 DNS | ✅ |
|
||
| Prometheus 鏈路監控規則已部署 | ⬜ |
|
||
| ConfigMap 修改前驗證 Hook | ⬜ |
|
||
|
||
---
|
||
|
||
## 教訓
|
||
|
||
> "路徑差一個 s,所以 404" — 這種低級錯誤絕對不能再犯。
|
||
> 必須靠自動化驗證,不能靠人眼審查。
|
||
|
||
---
|
||
|
||
## 關聯文件
|
||
|
||
- Memory: `feedback_alertchain_e2e_validation.md`
|
||
- ADR-011: NetworkPolicy 變更治理架構
|
||
- Skill 04: DevOps Commander
|
||
- Skill 05: SRE QA
|