chundev
日期:2026-03-22 環境:Prometheus + Alertmanager(kube-prometheus-stack)
Prometheus 提供三層告警查詢能力:Alertmanager API 看「正在發生的」、ALERTS metric 看「過去發生過的」、ALERTS_FOR_STATE 看「什麼時候開始的」。多數人只用第一層,結果已 resolve 的告警就查不到。掌握 query_range + ALERTS metric 就能回溯任意時間範圍內的告警歷史。
在 K8s 叢集上跑 kube-prometheus-stack,某天收到告警通知(email 或 Slack),但打開監控 Dashboard 只看到一個永遠在 firing 的 Watchdog — 今天明明有告警,為什麼看不到?
原因:Alertmanager API 只回傳正在 firing 或 pending 的告警。一旦告警 resolve,就從 API 結果中消失了。要看歷史告警,需要用不同的查詢方式。
| 層級 | 工具 | 查什麼 | 能看到已 resolve 的嗎? |
|---|---|---|---|
| 1. Alertmanager API | /api/v1/alerts |
當前 firing/pending 的告警 | ❌ 不能 |
| 2. ALERTS metric | query_range |
告警的時間序列 | ✅ 可以回溯 |
| 3. ALERTS_FOR_STATE | query |
告警開始的 Unix timestamp | ✅ 告警起始時間 |
這是最常用的,也是多數 Dashboard 預設使用的。
curl -s http://<prometheus>:9090/api/v1/alerts | python3 -m json.tool
回傳結構:
{
"status": "success",
"data": {
"alerts": [
{
"labels": { "alertname": "Watchdog", "severity": "none" },
"annotations": { "summary": "..." },
"state": "firing",
"activeAt": "2026-03-01T06:11:02Z",
"value": "1e+00"
}
]
}
}
curl -s http://<prometheus>:9090/api/v1/rules | python3 -c "
import json, sys
data = json.load(sys.stdin)
for group in data['data']['groups']:
for rule in group['rules']:
if rule['type'] == 'alerting':
state = rule['state']
name = rule['name']
alerts = rule.get('alerts', [])
print(f'[{state.upper()}] {name} ({len(alerts)} active)')
"
⚠️ 限制:/api/v1/alerts 只顯示 firing 和 pending。告警 resolve 後就消失了 — 你無法知道「今天早上 10 點有什麼告警」。
這是關鍵 — Prometheus 會自動產生一個叫 ALERTS 的合成時間序列(synthetic time series),記錄每個告警的狀態。
ALERTS{alertname="KubeJobFailed", alertstate="firing", namespace="default", severity="warning"} 1
alertstate 只有 firing 或 pending 兩種值1(表示該狀態活躍)# 計算時間範圍
START=$(python3 -c "import time; print(int(time.time()) - 86400)")
END=$(python3 -c "import time; print(int(time.time()))")
# 查詢(排除永遠在 firing 的 Watchdog)
curl -s "http://<prometheus>:9090/api/v1/query_range" \
--data-urlencode 'query=ALERTS{alertstate="firing",alertname!="Watchdog"}' \
--data-urlencode "start=$START" \
--data-urlencode "end=$END" \
--data-urlencode "step=300"
curl -s "http://<prometheus>:9090/api/v1/query_range" \
--data-urlencode 'query=ALERTS{alertstate="firing",alertname!="Watchdog"}' \
--data-urlencode "start=$START" \
--data-urlencode "end=$END" \
--data-urlencode "step=60" | python3 -c "
import json, sys
from datetime import datetime
data = json.load(sys.stdin)
for r in data['data']['result']:
name = r['metric'].get('alertname', '?')
severity = r['metric'].get('severity', '?')
ns = r['metric'].get('namespace', '')
job_name = r['metric'].get('job_name', '')
values = r.get('values', [])
if values:
first = datetime.fromtimestamp(values[0][0]).strftime('%H:%M')
last = datetime.fromtimestamp(values[-1][0]).strftime('%H:%M')
duration_min = (values[-1][0] - values[0][0]) / 60
print(f'[{severity}] {name}')
print(f' namespace: {ns}')
if job_name:
print(f' job: {job_name}')
print(f' 時間: {first} ~ {last} (持續 {duration_min:.0f} 分鐘)')
print()
"
範例輸出:
[warning] KubeJobFailed
namespace: kymo-task
job: mission-notification-29569020
時間: 09:46 ~ 10:30 (持續 44 分鐘)
# 7 天 = 604800 秒,step 用 900(15 分鐘)避免回傳太多資料點
START=$(python3 -c "import time; print(int(time.time()) - 604800)")
END=$(python3 -c "import time; print(int(time.time()))")
curl -s "http://<prometheus>:9090/api/v1/query_range" \
--data-urlencode 'query=ALERTS{alertstate="firing",alertname!="Watchdog"}' \
--data-urlencode "start=$START" \
--data-urlencode "end=$END" \
--data-urlencode "step=900"
# 只看 critical
'query=ALERTS{alertstate="firing",severity="critical"}'
# 只看 warning
'query=ALERTS{alertstate="firing",severity="warning"}'
# 只看特定 namespace
'query=ALERTS{alertstate="firing",namespace="production"}'
# 只看特定告警名稱
'query=ALERTS{alertstate="firing",alertname="KubeJobFailed"}'
ALERTS_FOR_STATE 是一個比較少人知道的 metric。它的值是 Unix timestamp,代表該告警規則的表達式「開始為 true」的時間。
curl -s "http://<prometheus>:9090/api/v1/query" \
--data-urlencode 'query=ALERTS_FOR_STATE' | python3 -c "
import json, sys
from datetime import datetime
data = json.load(sys.stdin)
for r in data['data']['result']:
name = r['metric'].get('alertname', '?')
ts = float(r['value'][1])
started = datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S')
print(f'{name}: started at {started}')
"
⚠️ 注意:ALERTS_FOR_STATE 只顯示目前活躍的告警的起始時間。已 resolve 的告警也不會出現。
這是 Prometheus 的設計哲學:
query_range 可以回溯看到/api/v1/alerts 只看當下 — 這個 API 不查 TSDB,它只看 rule engine 的 in-memory 狀態所以要看告警歷史,唯一可靠的方式就是查 ALERTS metric 的 time series。
告警歷史的可追溯範圍 = Prometheus 的 TSDB retention period。預設是 15 天,可以在 Prometheus 啟動參數中調整:
# kube-prometheus-stack values.yaml
prometheus:
prometheusSpec:
retention: 30d # 保留 30 天
query_range 的 step 參數決定取樣間隔。選擇取決於你需要的精度和時間範圍:
| 時間範圍 | 建議 step | 說明 |
|---|---|---|
| 1 小時 | 15s | 精確到秒,看告警的精確起止 |
| 24 小時 | 60s | 精確到分鐘,找出告警時間段 |
| 7 天 | 300s (5m) | 足夠看出哪天哪個時段有告警 |
| 30 天 | 900s (15m) | 粗略瀏覽整月告警趨勢 |
⚠️ Step 太小 + 時間範圍太大 = 回傳海量資料,可能讓 Prometheus 變慢甚至 OOM。
# === 即時查詢 ===
# 1. 當前 firing 的告警
curl -s http://<prometheus>:9090/api/v1/alerts
# 2. 所有告警規則和狀態(含 inactive)
curl -s http://<prometheus>:9090/api/v1/rules
# === 歷史查詢 ===
# 3. 過去 24 小時所有告警(排除 Watchdog)
START=$(python3 -c "import time; print(int(time.time()) - 86400)")
END=$(python3 -c "import time; print(int(time.time()))")
curl -s "http://<prometheus>:9090/api/v1/query_range" \
--data-urlencode 'query=ALERTS{alertstate="firing",alertname!="Watchdog"}' \
--data-urlencode "start=$START" \
--data-urlencode "end=$END" \
--data-urlencode "step=60"
# 4. 過去 7 天的 critical 告警
START=$(python3 -c "import time; print(int(time.time()) - 604800)")
END=$(python3 -c "import time; print(int(time.time()))")
curl -s "http://<prometheus>:9090/api/v1/query_range" \
--data-urlencode 'query=ALERTS{alertstate="firing",severity="critical"}' \
--data-urlencode "start=$START" \
--data-urlencode "end=$END" \
--data-urlencode "step=300"
# 5. 某個特定告警的歷史
curl -s "http://<prometheus>:9090/api/v1/query_range" \
--data-urlencode 'query=ALERTS{alertname="KubeJobFailed"}' \
--data-urlencode "start=$START" \
--data-urlencode "end=$END" \
--data-urlencode "step=60"
# === K8s 環境 ===
# 6. 透過 kubectl exec 查詢(不需要 port-forward)
kubectl exec -n monitoring <prometheus-pod> -c prometheus -- \
wget -qO- 'http://localhost:9090/api/v1/alerts'
# 7. 透過 port-forward 查詢
kubectl port-forward -n monitoring svc/<prometheus-svc> 9090:9090
# 然後用 localhost:9090 查詢
/api/v1/alerts 只看當下 — 它不查 TSDB,所以已 resolve 的告警看不到。這是最常見的困惑來源。ALERTS metric 是回溯歷史的關鍵 — Prometheus 在告警活躍期間持續寫 sample 到 TSDB,用 query_range 就能查到任意時間範圍內的告警。step 參數影響精度和效能 — 時間範圍越大,step 應該越大,否則會產生大量資料點。alertname!="Watchdog" 排除。