Dashboard đẹp nhưng không ai ngồi nhìn màn hình 24/7. Alerting là cách Grafana tự động đánh giá truy vấn theo chu kỳ, phát hiện bất thường và gửi thông báo tới đúng người, đúng kênh. Bài này đi từ kiến trúc Unified Alerting, cấu trúc một alert rule, tới contact points, notification policies và các best practice để cảnh báo thực sự hữu ích chứ không gây "alert fatigue".
Từ Grafana 8.0, Grafana giới thiệu Unified Alerting — hệ thống cảnh báo mới dựa trên Prometheus Alertmanager, thay thế cho legacy dashboard alerting (kiểu cũ gắn alert trực tiếp vào từng panel). Từ Grafana 9 nó là mặc định, và legacy alerting đã bị gỡ hẳn ở Grafana 11.
| Tiêu chí | Legacy alerting (cũ) | Unified Alerting (mới) |
|---|---|---|
| Nơi định nghĩa | Gắn chặt vào panel của dashboard | Tách riêng, quản lý tập trung ở mục Alerting |
| Query | 1 query, 1 điều kiện cho mỗi alert | Nhiều query + biểu thức (reduce, math, threshold) ghép lại |
| Định tuyến | Notification channel phẳng | Cây notification policies theo label matcher |
| Nguồn dữ liệu | Chỉ nguồn của panel | Nhiều data source khác nhau trong cùng một rule |
| Nền tảng | Cơ chế nội bộ riêng của Grafana | Mô hình Prometheus (labels, Alertmanager) |
Unified Alerting gồm năm thành phần chính, phối hợp với nhau:
| Thành phần | Vai trò |
|---|---|
| Alert rules | Quy tắc: truy vấn gì, đánh giá bao lâu một lần, ngưỡng nào thì kêu. Đây là "bộ não" quyết định khi nào có sự cố. |
| Contact points | Kênh nhận thông báo: Email, Slack, Webhook, PagerDuty, Telegram... Định nghĩa gửi đi đâu và gửi như thế nào. |
| Notification policies | Cây định tuyến: alert có label nào thì gửi tới contact point nào, gom nhóm ra sao. Quyết định ai nhận cái gì. |
| Silences | Tắt thông báo tạm thời cho các alert khớp label matcher (vd trong lúc bảo trì). Alert vẫn firing nhưng không gửi. |
| Mute timings | Lịch tắt thông báo lặp lại (vd ngoài giờ hành chính, cuối tuần). Gắn vào notification policy. |
Một alert rule trong Grafana được xây theo mô hình pipeline nhiều bước. Mỗi bước là một "expression" nhận đầu ra của bước trước:
last, mean, max... Alert cần một giá trị đơn để so sánh, không thể so cả một đường.IS ABOVE 80). Kết quả là đúng/sai — quyết định instance có vi phạm không.Ngoài pipeline, mỗi rule còn có các thiết lập điều phối thời gian:
| Thiết lập | Ý nghĩa |
|---|---|
| Evaluation group | Nhóm các rule đánh giá cùng nhau, tuần tự. Các rule cùng group chia sẻ chung interval. |
| Evaluation interval | Bao lâu đánh giá một lần (vd mỗi 1m). Càng ngắn càng nhạy nhưng càng tốn tải lên data source. |
Pending period (for) | Điều kiện phải đúng liên tục trong khoảng này thì mới chuyển sang Firing. Vd for: 5m → CPU phải cao suốt 5 phút. Đây là "bộ lọc chống nhiễu" quan trọng nhất. |
| No data handling | Khi query không trả về dữ liệu: chọn NoData (trạng thái riêng), Alerting (coi như firing), hoặc OK (bỏ qua). |
| Error handling | Khi query lỗi (data source sập, timeout): chọn Error, Alerting, hoặc OK. |
Mỗi alert instance trải qua các trạng thái sau:
| Trạng thái | Ý nghĩa |
|---|---|
| Normal | Điều kiện không vi phạm — mọi thứ bình thường, không gửi gì. |
| Pending | Điều kiện đã vi phạm nhưng chưa đủ lâu theo for. Đang "đếm ngược". Chưa gửi thông báo. |
| Firing | Vi phạm đã kéo dài qua hết for → kích hoạt thật, gửi thông báo qua notification policy. |
| NoData | Query không trả về dữ liệu (theo cấu hình no-data handling ở trên). |
| Error | Không đánh giá được rule do lỗi truy vấn/data source. |
Ví dụ cụ thể — cảnh báo CPU > 80% trong 5 phút. Query A dùng PromQL tính phần trăm CPU đang bận theo từng instance:
# Query A (data source: Prometheus) — % CPU không idle 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
Expression B reduce lấy giá trị cuối cùng của mỗi series, rồi expression C là threshold so với 80:
# B — Reduce: Function = Last, Input = A # C — Threshold: Input = B, IS ABOVE 80 (đây là điều kiện alert) # Cấu hình thời gian: Evaluation group: infra-cpu Evaluation interval: 1m Pending period: 5m # for: phải >80% liên tục 5 phút No data: NoData Error: Error
Diễn giải: mỗi 1m Grafana chạy query A cho từng instance, reduce về một số, so với 80. Nếu instance nào vượt 80%, nó vào Pending; nếu vẫn vượt suốt 5m thì chuyển Firing và gửi thông báo. Nếu tụt xuống dưới 80% trước khi hết 5 phút, nó quay về Normal mà không làm phiền ai.
last hoặc mean) biến đường đó thành một giá trị đại diện để so sánh — thiếu bước này rule sẽ báo lỗi "input is not a number".Mỗi alert mang hai loại metadata với vai trò rất khác nhau — đây là điểm hay nhầm lẫn nhất:
| Labels | Annotations | |
|---|---|---|
| Mục đích | Định danh & routing (định tuyến, gom nhóm, silence) | Cung cấp thông tin cho con người đọc |
| Ảnh hưởng | Quyết định alert đi tới contact point nào | Chỉ hiển thị trong thông báo, không ảnh hưởng routing |
| Ví dụ | severity="critical", team="infra", env="prod" | summary, description, runbook_url |
Ba annotation quy ước phổ biến (theo chuẩn Prometheus):
Labels và annotations có thể dùng template để chèn giá trị động từ query (cú pháp Go template của Prometheus):
# Labels (dùng cho routing) severity: critical team: infra # Annotations (dùng cho hiển thị) summary: "CPU cao trên {{ $labels.instance }}" description: "CPU đang ở mức {{ $values.B.Value | printf \"%.1f\" }}% (ngưỡng 80%)" runbook_url: "https://wiki.congty.vn/runbooks/high-cpu"
severity label lên mọi alert. Cây notification policy hầu như luôn định tuyến theo severity (critical → gọi điện/PagerDuty, warning → Slack). Không có label này thì không thể phân luồng theo mức độ nghiêm trọng.Contact point định nghĩa gửi thông báo đi đâu. Một contact point có thể chứa nhiều "integration" (vd vừa gửi email vừa gửi Slack). Các loại phổ biến:
| Loại | Dùng khi | Cấu hình cần |
|---|---|---|
| Thông báo cơ bản, lưu vết | Địa chỉ nhận (cần cấu hình SMTP ở grafana.ini) | |
| Slack | Cảnh báo cho team qua chat | Incoming Webhook URL hoặc Bot token + channel |
| Webhook | Tích hợp hệ thống tuỳ ý (gửi HTTP POST JSON) | URL đích, HTTP method, auth (tuỳ chọn) |
| PagerDuty | On-call, escalation, gọi điện | Integration Key (Events API v2) |
| Telegram | Thông báo cá nhân/nhóm nhỏ | Bot API token + Chat ID |
Cấu hình mẫu — Email. Trước hết phải khai báo SMTP trong grafana.ini (hoặc biến môi trường), nếu không email sẽ không gửi được:
# grafana.ini — bật SMTP [smtp] enabled = true host = smtp.gmail.com:587 user = alerts@congty.vn password = "<app-password>" from_address = alerts@congty.vn from_name = Grafana Alerts
Sau đó tạo contact point loại Email với danh sách người nhận (Alerting → Contact points → New):
# Contact point: "email-infra" Integration: Email Addresses: oncall@congty.vn; sre-team@congty.vn Single email: false # mỗi người một email riêng
Cấu hình mẫu — Slack (Incoming Webhook). Vào api.slack.com tạo app, bật Incoming Webhooks, lấy URL dạng https://hooks.slack.com/services/T00/B00/xxxx, rồi:
# Contact point: "slack-alerts" Integration: Slack Webhook URL: https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXX Recipient: #alerts-prod Title: {{ template "slack.title" . }} Text body: {{ template "slack.text" . }}
Notification policy là một cây: có một root policy (mặc định) và các nhánh con. Mỗi alert đi từ root xuống, khớp với nhánh con đầu tiên có label matcher trùng; nếu không nhánh nào khớp thì dùng root. Đây là cơ chế quyết định alert nào gửi tới contact point nào.
# Cây định tuyến ví dụ Root policy → contact point: email-infra (group_by: alertname, cluster) │ ├─ matcher: severity = critical → contact point: pagerduty-oncall │ repeat_interval: 1h │ ├─ matcher: team = infra → contact point: slack-alerts │ └─ matcher: env = staging → contact point: email-infra mute timings: ngoai-gio
Ba tham số thời gian điều khiển cách gom nhóm thông báo — cực kỳ quan trọng để chống spam:
| Tham số | Ý nghĩa | Giá trị mẫu |
|---|---|---|
| group_by | Gom các alert có cùng giá trị label này vào một thông báo. Vd group theo alertname → 10 server cùng lỗi = 1 tin nhắn thay vì 10. | [alertname, cluster] |
| group_wait | Chờ bao lâu trước khi gửi thông báo đầu tiên của một nhóm mới — để gom thêm alert đến trễ. | 30s |
| group_interval | Sau tin đầu, chờ tối thiểu bao lâu mới gửi tin tiếp cho nhóm đó khi có alert mới thêm vào. | 5m |
| repeat_interval | Nếu alert vẫn firing, bao lâu nhắc lại một lần. Đừng đặt quá ngắn kẻo phiền. | 4h |
Silences — tắt thông báo tạm thời theo label matcher, có thời hạn cụ thể. Alert vẫn được đánh giá và vẫn Firing, chỉ là không gửi đi:
cluster="db-prod" trong 2 giờ.Mute timings — khác silence ở chỗ nó lặp lại theo lịch và gắn vào notification policy, thay vì một lần theo matcher:
# Mute timing "ngoai-gio" — chỉ mute warning ngoài giờ hành chính Name: ngoai-gio Time intervals: - weekdays: 'monday:friday' times: '18:00' - '09:00' # buổi tối - weekdays: 'saturday, sunday' # cả cuối tuần location: 'Asia/Ho_Chi_Minh'
Một hệ thống alert tồi còn tệ hơn không có alert: người ta học cách bỏ qua nó (alert fatigue), và rồi cảnh báo thật cũng bị lờ đi. Vài nguyên tắc:
for (pending period) để chống flapping — chỉ báo khi vấn đề kéo dài, bỏ qua spike thoáng qua. CPU nhảy 81% trong 10 giây không đáng đánh thức ai.severity label nhất quán (critical / warning / info) và định tuyến theo nó: critical → gọi điện/PagerDuty, warning → Slack, info → chỉ ghi log.group_by để một sự cố hạ tầng không tạo ra 200 thông báo. Đặt repeat_interval đủ dài (vài giờ).runbook_url và summary rõ ràng — người trực lúc 3 giờ sáng cần biết ngay phải mở tài liệu nào, không phải đi hỏi cả team.