Bài 4

Alerting & Thông báo

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".

Grafana Unified Alerting
Kiến trúc cảnh báo hợp nhất từ Grafana 8+

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ĩaGắn chặt vào panel của dashboardTách riêng, quản lý tập trung ở mục Alerting
Query1 query, 1 điều kiện cho mỗi alertNhiều query + biểu thức (reduce, math, threshold) ghép lại
Định tuyếnNotification channel phẳngCây notification policies theo label matcher
Nguồn dữ liệuChỉ nguồn của panelNhiều data source khác nhau trong cùng một rule
Nền tảngCơ chế nội bộ riêng của GrafanaMô 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ầnVai trò
Alert rulesQuy 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 pointsKênh nhận thông báo: Email, Slack, Webhook, PagerDuty, Telegram... Định nghĩa gửi đi đâugửi như thế nào.
Notification policiesCâ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ì.
SilencesTắ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 timingsLị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.
Cách nhớ: Alert rule sinh ra alert instance kèm labels → Alertmanager (tích hợp trong Grafana) dùng notification policies để định tuyến alert đó tới contact points phù hợp, trừ khi bị silence hoặc mute timing chặn lại.
Cấu trúc một Alert Rule
Query → Reduce → Threshold → Trạng thái

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:

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 groupNhóm các rule đánh giá cùng nhau, tuần tự. Các rule cùng group chia sẻ chung interval.
Evaluation intervalBao 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 handlingKhi 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 handlingKhi 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.
FiringVi 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.
NoDataQuery không trả về dữ liệu (theo cấu hình no-data handling ở trên).
ErrorKhô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.

Vì sao cần Reduce? PromQL trả về cả một đường (nhiều điểm theo thời gian). Threshold chỉ so được một con số. Reduce (thường là 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".
Labels & Annotations
Định tuyến bằng label, hiển thị bằng annotation

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:

LabelsAnnotations
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ưởngQuyết định alert đi tới contact point nàoChỉ 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"
Quy tắc vàng: đặt 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 Points
Các kênh nhận thông báo

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ạiDùng khiCấu hình cần
EmailThông báo cơ bản, lưu vếtĐịa chỉ nhận (cần cấu hình SMTP ở grafana.ini)
SlackCảnh báo cho team qua chatIncoming Webhook URL hoặc Bot token + channel
WebhookTích hợp hệ thống tuỳ ý (gửi HTTP POST JSON)URL đích, HTTP method, auth (tuỳ chọn)
PagerDutyOn-call, escalation, gọi điệnIntegration Key (Events API v2)
TelegramThô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" . }}
Test ngay: mỗi contact point có nút Test gửi một thông báo giả. Luôn bấm Test sau khi tạo — sai webhook URL hay SMTP là lỗi phổ biến nhất, và bạn không muốn phát hiện điều đó lúc hệ thống đang cháy.
Notification Policies, Silences & Mute Timings
Định tuyến, gom nhóm và tắt tạm thời

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ĩaGiá trị mẫu
group_byGom 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_waitChờ 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_intervalSau 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_intervalNế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:

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'
Phân biệt: Silence = tắt một lần, theo label matcher, có hạn (bảo trì đột xuất). Mute timing = tắt lặp lại theo lịch, gắn vào policy (vd không báo warning ban đêm). Đừng dùng silence dài hạn để né alert phiền — hãy sửa chính alert rule đó.
Best Practices
Cảnh báo hữu ích, không gây mệt mỏi

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:

Thước đo sức khoẻ: nếu team bạn bắt đầu tạo rule Slack để "ẩn kênh alert", đó là dấu hiệu đỏ của alert fatigue. Mục tiêu: mỗi alert firing đều dẫn tới một hành động thật. Ít mà chất hơn nhiều mà nhiễu.
← Bài trước
Bài 3: Dashboard