Log mà không có alert thì giống camera giám sát không có người ngồi xem: ghi lại đầy đủ mọi thứ nhưng bạn chỉ biết bị trộm vào sáng hôm sau. Kibana từ 7.13 trở đi có hệ thống alert đầy đủ, và tới 8.x thì gần ngang bằng các tool thương mại như PagerDuty Insights hay Datadog Monitor.
Bài này tập trung vào ba loại rule cơ bản nhất, cũng là ba loại dùng nhiều nhất trong production:
- ES query rule: viết DSL hoặc KQL, trigger khi có document khớp
- Threshold rule: aggregate metric, trigger khi vượt ngưỡng
- Burn rate rule: track SLO error budget consumption
Mục tiêu:
- Hiểu mental model về Rule, Alert, Action trong Kibana
- Setup đúng schedule, time window, threshold cho từng loại rule
- Biết khi nào dùng rule type nào và tránh các pitfall hay gặp
Phần 1: Mental model Rule và Alert
Trong Kibana, ba khái niệm dễ nhầm:
| Concept | Định nghĩa |
|---|---|
| Rule | Một định nghĩa: “Mỗi N phút, chạy query Y, nếu Y trả về kết quả thoả X thì sinh ra alert” |
| Alert | Một instance trigger từ rule. Cùng một rule có thể sinh nhiều alert (mỗi alert cho một entity) |
| Action | Hành động khi alert trigger: gửi Slack, gọi webhook, tạo issue |
Ví dụ: rule “Service down” check mỗi service trong 5 phút. Nếu 3 service payment, auth, inventory đều down, rule sẽ sinh 3 alert song song, mỗi alert có context riêng. Action gửi Slack sẽ gửi 3 message rời (trừ khi setup throttling).
Phân biệt này quan trọng khi debug: rule đang chạy nhưng không gửi notification, có thể vì alert đang ở state Active mà action chỉ gửi khi state đổi (new alert vs ongoing).
Phần 2: ES query rule
Đây là rule type linh hoạt nhất. Bạn viết một query Elasticsearch DSL hoặc KQL, rule trigger khi query trả về document.
Setup qua GUI
Stack Management → Rules → Create rule → Elasticsearch query.
Form chính:
- Index: nhập
app-logs-*hoặc data view ID. - Time field:
@timestamp. - Query type: KQL/Lucene hoặc Query DSL.
- Query: ví dụ KQL
Level : "Fatal". - Threshold:
IS ABOVE 0(trigger nếu có bất kỳ document nào khớp). - Time window:
5 minutes. - Run rule every:
1 minute.
Setup qua API
curl -s -u "$KB_USER:$KB_PASS" \
-H "kbn-xsrf: true" \
-H "Content-Type: application/json" \
-X POST "$KIBANA_URL/api/alerting/rule" \
-d '{
"name": "Fatal error detected",
"rule_type_id": ".es-query",
"consumer": "alerts",
"schedule": {"interval": "1m"},
"params": {
"searchType": "esQuery",
"index": ["app-logs-*"],
"timeField": "@timestamp",
"esQuery": "{\"query\":{\"match\":{\"Level\":\"Fatal\"}}}",
"size": 100,
"threshold": [0],
"thresholdComparator": ">",
"timeWindowSize": 5,
"timeWindowUnit": "m"
},
"actions": []
}'
Time window và schedule
Hai tham số dễ nhầm:
timeWindowSize(5m): rule sẽ scan document trong khoảngnow - 5mđếnnow.schedule.interval(1m): rule chạy mỗi phút một lần.
Nếu schedule = 1m, window = 5m, mỗi document sẽ được scan 5 lần (trong 5 lần chạy liên tiếp). Điều này gây trùng alert. Cách tránh: dùng deduplication hoặc throttle action (bài 11 sẽ đi sâu).
Pitfall: window quá nhỏ
Một rule check error 5xx với timeWindowSize: 1m, schedule mỗi 1 phút. Nhưng nếu ES ingest delay 30 giây (Vector buffer, hay Logstash batch), thì document vào ES sau khi rule chạy 30 giây. Rule kế tiếp 1 phút sau scan window mới, document đã rơi ngoài window cũ. Kết quả: alert miss.
Rule of thumb: timeWindowSize >= 2 * ingest_lag + schedule_interval. Nếu ingest lag 30s, schedule 1m thì window ít nhất 2m.
Pitfall: query không match field name
KQL: level : "fatal"
Trả về 0 kết quả nếu Serilog emit Level PascalCase với value "Fatal". Rule sẽ không bao giờ trigger. Trước khi dán query vào rule, test trong Discover với cùng time range và data view. Nếu Discover ra kết quả mà rule không, kiểm tra data view ID có match không (rule lưu data view title; nếu rename data view, link gãy).
Phần 3: Threshold rule
Threshold rule là wrapper trên ES query với UI thân thiện hơn. Thay vì viết DSL, bạn chọn aggregation từ dropdown.
Use case điển hình
“Trigger khi avg(response_time) > 1000ms trong 5 phút qua, group by service.name”.
Setup:
- Rule type: Index threshold.
- Index:
metrics-*. - When:
average of response_time. - Over:
all documentshoặctop 5 by service.name. - For the last:
5 minutes. - Threshold:
IS ABOVE 1000.
Nếu chọn “top 5 by service.name”, rule sẽ tạo 5 alert riêng (mỗi service một alert) nếu thoả threshold. Action có thể template {{context.group}} để biết service nào trigger.
Aggregation choices
| Aggregation | Khi dùng |
|---|---|
count | Số request, số log, số event |
average | Response time, CPU, memory |
min / max | Bottom outlier, peak |
sum | Doanh thu, tổng byte transferred |
cardinality | Unique user, unique session |
Lưu ý: cardinality threshold dễ flaky vì approximate (xem bài 7 về visualization pitfalls).
Pitfall: top N skew
Threshold rule với top 5 chỉ alert cho 5 entity highest. Nếu có 50 service và service nhỏ bị spike, rule có thể miss vì không lọt top 5.
Fix: thay vì top 5, dùng all values với threshold thấp, hoặc tách thành nhiều rule riêng (mỗi service một rule với filter service.name: "X"). Tốn config hơn nhưng không miss.
Phần 4: Burn rate rule
Burn rate là khái niệm từ Google SRE Workbook. Thay vì alert “có error”, alert “đang tiêu error budget quá nhanh”.
Mental model
Giả sử SLO của bạn là 99.9% availability trong 30 ngày. Error budget = 0.1% = 43.2 phút downtime trong 30 ngày.
- Nếu trong 1 giờ qua, downtime là 0.1% (3.6 giây) → tiêu budget với tốc độ 1x (vừa đủ trong 30 ngày).
- Nếu downtime trong 1 giờ là 1% (36 giây) → tiêu 10x → hết budget trong 3 ngày.
Burn rate alert trigger khi rate vượt ngưỡng, không phải khi tổng error vượt.
Multi-window burn rate
Google khuyến nghị dùng 2 hoặc 3 window đồng thời để giảm false positive:
| Window ngắn | Window dài | Burn rate threshold | Severity |
|---|---|---|---|
| 5 phút | 1 giờ | 14.4x | Page (high urgency) |
| 30 phút | 6 giờ | 6x | Page (medium urgency) |
| 1 giờ | 24 giờ | 1x | Ticket (long-term) |
Logic: trigger CHỈ khi cả window ngắn VÀ window dài đều vượt rate. Window ngắn detect spike nhanh; window dài tránh nhiễu một phút bất thường.
Setup trong Kibana
Kibana 8.x có dedicated SLO + Burn rate rule (sẽ đi sâu bài 10). Nhưng nếu chưa muốn dùng SLO feature, có thể tự build với ES query rule:
curl -s -u "$KB_USER:$KB_PASS" \
-H "kbn-xsrf: true" \
-H "Content-Type: application/json" \
-X POST "$KIBANA_URL/api/alerting/rule" \
-d '{
"name": "Burn rate fast (5m + 1h)",
"rule_type_id": ".es-query",
"consumer": "alerts",
"schedule": {"interval": "1m"},
"params": {
"searchType": "esQuery",
"index": ["request-logs-*"],
"timeField": "@timestamp",
"esQuery": "{\"query\":{\"bool\":{\"must\":[{\"range\":{\"@timestamp\":{\"gte\":\"now-5m\"}}},{\"range\":{\"status\":{\"gte\":500}}}]}}}",
"size": 0,
"threshold": [0.0144],
"thresholdComparator": ">",
"timeWindowSize": 5,
"timeWindowUnit": "m"
}
}'
Threshold 0.0144 là số tuyệt đối; bạn cần normalize bằng cách chia tổng request. Cách tốt hơn: dùng pipeline aggregation hoặc transform tạo index error_rate-* precompute, rồi rule chỉ check error_rate > 0.0144.
Transform approach
PUT _transform/error-rate-1m
{
"source": { "index": "request-logs-*" },
"pivot": {
"group_by": {
"ts": { "date_histogram": { "field": "@timestamp", "fixed_interval": "1m" } },
"service": { "terms": { "field": "service.name" } }
},
"aggregations": {
"total": { "value_count": { "field": "@timestamp" } },
"errors": { "filter": { "range": { "status": { "gte": 500 } } } },
"error_rate": {
"bucket_script": {
"buckets_path": { "e": "errors._count", "t": "total" },
"script": "params.e / params.t"
}
}
}
},
"dest": { "index": "error-rate-1m" },
"frequency": "30s",
"sync": { "time": { "field": "@timestamp", "delay": "60s" } }
}
Sau đó rule chỉ cần check avg(error_rate) > 0.0144 trên error-rate-1m. Đơn giản hơn nhiều.
Phần 5: Pitfall storytelling
Ca 1: Alert flapping mỗi đêm
Rule “CPU > 80%” trigger và clear xen kẽ mỗi vài phút lúc 3 giờ sáng. Slack channel ngập noise. Lý do: backup job chạy 3 giờ sáng đẩy CPU lên 85% trong 30 giây rồi xuống. Rule trigger ngay khi vượt.
Fix: dùng for N consecutive checks (Kibana 8.6+) hoặc tăng timeWindowSize lên 5 phút và threshold lên 85%. Rule chỉ trigger khi CPU cao SUSTAINED, không phải spike.
Ca 2: Alert miss vì index pattern thay đổi
Rule check app-logs-2026.*. Sang năm 2027, log đi vào app-logs-2027.*. Rule không trigger nữa vì index pattern không match.
Fix: luôn dùng wildcard rộng app-logs-* thay vì hardcode năm. Hoặc alias app-logs-current rotate manually.
Ca 3: Rule “không chạy”
Rule tạo xong, status Active nhưng không thấy alert. Debug:
- Vào Stack Management → Rules → click rule → Execution log. Xem có error không.
- Check
consumerfield. Nếuconsumer: "alerts"mà bạn không có quyềnalertscluster privilege, rule chạy nhưng không emit alert. - Check API key creator. Rule chạy với quyền của user/key tạo nó. Nếu key expired hoặc revoke, rule chết âm thầm.
Phần 6: Best practices
| Practice | Lý do |
|---|---|
timeWindowSize >= 2 * ingest_lag | Tránh miss alert do ingest delay |
| Dùng wildcard index pattern | Tránh gãy khi rotation |
| Test query trong Discover trước | Đảm bảo trigger condition đúng |
| Tag rule với owner và severity | Dễ filter và route |
Set notification_delay 1-2 phút | Tránh alert spike một lần xảy ra |
| Backup rule definition qua API export | Khôi phục nhanh khi cluster die |
| Document runbook trong rule description | On-call có context khi nhận page |
Cheatsheet
| Rule type | Use case | Schedule điển hình |
|---|---|---|
.es-query | Custom DSL, flexible | 1-5m |
.index-threshold | Aggregate metric đơn giản | 1-5m |
.geo-containment | Geofence (xem bài 6) | 1m |
| Burn rate (custom) | SLO tracking | 1m |
.metrics-* (APM) | Service-specific | 30s-1m |
Lời kết
Alert là chỗ Kibana từ một log viewer trở thành observability platform. Ba rule type cơ bản này phủ 90% nhu cầu production: ES query cho event-based, Threshold cho metric-based, Burn rate cho SLO. Phần khó nhất không phải config, mà là chọn đúng time window và threshold để không noise mà cũng không miss.
Bài tiếp theo trong series Kibana từ A đến Z sẽ đi vào Connectors: cách setup Slack, Email, Webhook, PagerDuty an toàn và đúng pattern. Có rule mà không có connector tốt thì alert vẫn vô dụng. Nếu bạn đang stuck với một rule cụ thể (flapping, miss, không trigger), comment cho mình biết.