Ba giờ sáng, điện thoại rung. Mở ra thấy 47 tin nhắn Telegram từ Alertmanager. Đọc lướt qua — hầu hết là InstanceDown của cùng một node đang rolling restart. Không khẩn cấp. Nhưng não vẫn phải xử lý. Sáng hôm sau mệt mỏi, check lại thấy có 2 alert thật sự quan trọng bị chìm giữa đống noise đó.
Đây là alert fatigue. Mình đã sống với nó suốt 2 năm trước khi quyết định phải làm gì đó nghiêm túc hơn.
So sánh 3 cách tiếp cận để xử lý alert thông minh
Cách 1: Rule-based filtering trong Alertmanager
Gần như ai cũng bắt đầu từ đây — viết thêm route, silence, inhibition rules trong alertmanager.yml.
# alertmanager.yml
routes:
- match:
severity: warning
receiver: 'slack-warnings'
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
- match:
severity: critical
receiver: 'pagerduty-oncall'
inhibit_rules:
- source_match:
alertname: NodeDown
target_match:
job: node
equal: ['instance']
Ưu điểm: Đơn giản, không cần infra thêm, Alertmanager hỗ trợ native.
Nhược điểm: Rules phải viết tay, không scale khi hệ thống phức tạp. Mỗi lần xuất hiện pattern alert mới lại phải mở file YAML ra sửa. Quan trọng hơn — nó lọc chứ không giải thích. SysAdmin vẫn phải đọc từng alert để hiểu context.
Cách 2: ML-based anomaly detection
Các tool như Grafana Machine Learning, Metis, hoặc tự build model với Prophet/LSTM để phát hiện anomaly thay vì dùng threshold cứng.
Ưu điểm: Thông minh hơn, tự học pattern theo thời gian, giảm false positive.
Nhược điểm: Setup phức tạp, cần dữ liệu training, khó debug khi model ra kết quả sai. Chi phí vận hành cao. Team 2-3 người thì đây là overkill.
Cách 3: LLM-based summarization (cách mình đang dùng)
Thay vì lọc hay học pattern, dùng LLM để đọc hiểu alert và trả lời câu hỏi thực sự quan trọng: “Cái này có cần tôi thức dậy ngay không? Nếu có thì vì sao?”
Ưu điểm: Không cần training data. Và khác với rule-based, LLM hiểu ngữ cảnh — biết rằng DiskSpaceRunningLow trên node database nguy hiểm hơn nhiều so với trên node log collector. Prompt dễ customize theo domain knowledge của từng team.
Nhược điểm: Có latency (1-3 giây mỗi lần gọi API). Chi phí API — nhưng với volume alert bình thường của hệ thống vừa, tổng chi phí chỉ vài đô mỗi tháng.
Vì sao mình chọn LLM?
Mình đã thử cả 3. Rule-based chạy được 6 tháng thì file alertmanager.yml có gần 300 dòng và không ai dám đụng vào nữa. ML thì mình setup thử Grafana ML — kết quả không tệ, nhưng sau một lần deploy lớn thay đổi alert pattern, model mất gần 2 tuần mới adapt lại.
LLM thắng ở một điểm mà hai cách kia bó tay: nó trả lời câu hỏi của con người, không phải câu hỏi của máy. Thay vì chỉ biết “CPU usage > 90%”, nó nói được: “CPU database server đang 95% trong 20 phút, trùng với giờ chạy backup job hàng ngày — khả năng cao là bình thường, nhưng nên check nếu backup chưa xong sau 2 giờ nữa.”
Rule-based không làm được điều này. Không phải vì thiếu dữ liệu — mà vì nó không hiểu ngữ nghĩa.
Kiến trúc hệ thống
Flow đơn giản:
Prometheus → Alertmanager → Webhook Receiver (Python) → LLM API → Telegram/Slack
Alertmanager gửi alert sang một Python server nhỏ qua webhook. Server này gọi LLM để phân loại và tóm tắt, rồi forward kết quả đã được “dịch” sang Telegram của team.
Hướng dẫn triển khai thực tế
Bước 1: Cấu hình Alertmanager webhook receiver
Thêm webhook receiver vào alertmanager.yml:
receivers:
- name: 'llm-summarizer'
webhook_configs:
- url: 'http://localhost:8080/alert'
send_resolved: true
http_config:
bearer_token: 'your-secret-token'
routes:
- receiver: 'llm-summarizer'
group_by: ['alertname', 'cluster', 'service']
group_wait: 10s
group_interval: 2m
repeat_interval: 1h
Trick quan trọng: set group_interval: 2m để Alertmanager gom alert cùng nhóm trong 2 phút trước khi gửi — LLM sẽ nhận được một batch gồm nhiều alert liên quan thay vì từng cái rời rạc.
Bước 2: Python webhook server với LLM
Cài dependencies:
pip install fastapi uvicorn anthropic httpx python-dotenv
File alert_summarizer.py:
import os
import json
import httpx
import anthropic
from fastapi import FastAPI, Request, HTTPException, Header
from typing import Optional
app = FastAPI()
client = anthropic.Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])
TELEGRAM_TOKEN = os.environ["TELEGRAM_BOT_TOKEN"]
TELEGRAM_CHAT_ID = os.environ["TELEGRAM_CHAT_ID"]
WEBHOOK_TOKEN = os.environ["WEBHOOK_SECRET_TOKEN"]
SYSTEM_PROMPT = """Bạn là SRE senior phân tích alert Prometheus cho hệ thống production.
Phân loại mức độ: CRITICAL (cần xử lý ngay), WARNING (cần theo dõi), INFO (thông tin).
Tóm tắt ngắn gọn bằng tiếng Việt, giải thích impact thực tế và gợi ý bước xử lý đầu tiên.
Format trả về JSON: {"severity": "CRITICAL|WARNING|INFO", "summary": "...", "impact": "...", "action": "..."}"""
def format_alerts_for_llm(alerts: list) -> str:
lines = []
for a in alerts:
labels = a.get("labels", {})
annotations = a.get("annotations", {})
status = a.get("status", "firing")
lines.append(
f"- [{status.upper()}] {labels.get('alertname', 'Unknown')}"
f" | instance={labels.get('instance', 'N/A')}"
f" | severity={labels.get('severity', 'N/A')}"
f" | {annotations.get('description', annotations.get('summary', ''))}"
)
return "\n".join(lines)
async def send_telegram(text: str):
url = f"https://api.telegram.org/bot{TELEGRAM_TOKEN}/sendMessage"
async with httpx.AsyncClient() as client_http:
await client_http.post(url, json={
"chat_id": TELEGRAM_CHAT_ID,
"text": text,
"parse_mode": "Markdown"
})
@app.post("/alert")
async def receive_alert(
request: Request,
authorization: Optional[str] = Header(None)
):
if authorization != f"Bearer {WEBHOOK_TOKEN}":
raise HTTPException(status_code=401)
payload = await request.json()
alerts = payload.get("alerts", [])
if not alerts:
return {"status": "no alerts"}
alert_text = format_alerts_for_llm(alerts)
response = client.messages.create(
model="claude-haiku-4-5-20251001", # Dùng Haiku để tiết kiệm chi phí
max_tokens=512,
system=SYSTEM_PROMPT,
messages=[{"role": "user", "content": f"Phân tích các alert sau:\n{alert_text}"}]
)
result = json.loads(response.content[0].text)
severity = result.get("severity", "INFO")
emoji = {"CRITICAL": "🔴", "WARNING": "🟡", "INFO": "🔵"}.get(severity, "⚪")
message = (
f"{emoji} *{severity}* — {len(alerts)} alert(s)\n\n"
f"*Tóm tắt:* {result.get('summary')}\n"
f"*Impact:* {result.get('impact')}\n"
f"*Nên làm:* {result.get('action')}"
)
await send_telegram(message)
return {"status": "ok", "severity": severity}
Chạy server:
export ANTHROPIC_API_KEY="sk-ant-..."
export TELEGRAM_BOT_TOKEN="..."
export TELEGRAM_CHAT_ID="-100..."
export WEBHOOK_SECRET_TOKEN="your-secret"
uvicorn alert_summarizer:app --host 0.0.0.0 --port 8080
Bước 3: Chạy như systemd service
[Unit]
Description=LLM Alert Summarizer
After=network.target
[Service]
Type=simple
User=prometheus
EnvironmentFile=/etc/alert-summarizer/env
ExecStart=/usr/local/bin/uvicorn alert_summarizer:app --host 0.0.0.0 --port 8080
Restart=always
RestartSec=5
WorkingDirectory=/opt/alert-summarizer
[Install]
WantedBy=multi-user.target
Mẹo tối ưu từ thực chiến
- Dùng Haiku thay vì Sonnet cho alert summarization — response time nhanh hơn (~0.8s vs ~2s), chi phí thấp hơn 5x. Với 500 alert events/ngày, tổng chi phí chưa đến $3/tháng.
- Cache alert đã xử lý: Dùng Redis hoặc dict in-memory lưu hash của alert batch trong 30 phút để tránh gọi LLM trùng lặp khi Alertmanager resend.
- Inject context hệ thống vào system prompt: Liệt kê những service quan trọng nhất, giờ maintenance định kỳ, pattern alert bình thường của team. LLM sẽ phân loại chính xác hơn nhiều.
- Set ngưỡng CRITICAL riêng: Chỉ ping oncall khi severity là CRITICAL. WARNING và INFO gom vào digest gửi mỗi sáng — không wake up người lúc 3 giờ sáng vì chuyện có thể đợi đến 9 giờ.
Kết quả sau 3 tháng dùng thực tế
Từ 40-60 tin nhắn Telegram mỗi đêm xuống còn 3-5 tin nhắn có ý nghĩa. Giảm hơn 90% noise. Team bắt đầu đọc alert trở lại — thay vì reflex mute bot đi cho yên.
Chi phí API với Claude Haiku: khoảng $2-3/tháng cho một hệ thống vừa (~500 alert events/ngày). Rẻ hơn nhiều so với những đêm mất ngủ không cần thiết.
Cái thay đổi lớn nhất không phải con số. Khi có alert thật sự nghiêm trọng, nó nổi bật rõ — không còn bị chìm trong đống noise nữa. Và mình ngủ được.

