Phần 11 kết thúc ở chỗ truy vấn chậm dần theo số chuỗi. Bài này đo cách chữa, và đo xem cảnh báo mất bao lâu để kêu.
Quy tắc ghi trước: 35 lần
Cùng một câu hỏi — p99 của thời gian phản hồi — hỏi theo hai cách:
# tinh tho moi lan hoi
histogram_quantile(0.99, sum(rate(http_duration_seconds_bucket[1m])) by (le))
# doc chi so da duoc ghi truoc
job:http_duration:p99
| Ba lần đo | |
|---|---|
| Tính thô | 22,1 / 21,4 / 22,0 ms |
| Đọc chỉ số ghi trước | 0,7 / 0,6 / 0,6 ms |
35 lần.
Quy tắc ghi trước là một câu lệnh chạy theo lịch, kết quả được ghi vào một chuỗi thời gian mới:
groups:
- name: ghi-truoc
interval: 5s
rules:
- record: job:http_duration:p99
expr: histogram_quantile(0.99, sum(rate(http_duration_seconds_bucket[1m])) by (le))
Nghĩa là công việc tính toán được làm một lần mỗi chu kỳ, thay vì một lần cho mỗi người mở bảng điều khiển.
Với một bảng điều khiển 20 ô, khác biệt là giữa trang tải trong 0,44 giây và trang tải trong 0,012 giây. Và với năm người cùng mở nó, Prometheus làm cùng một phép tính 100 lần thay vì một.
Quy ước đặt tên: mức_gộp:tên_chỉ_số:phép_toán — ví dụ job:http_duration:p99. Dấu hai chấm chỉ dành cho chỉ số ghi trước; chỉ số gốc không bao giờ có nó, nên nhìn tên là biết ngay đây là chỉ số dẫn xuất.
Cái giá: chỉ số ghi trước luôn trễ hơn dữ liệu thô đúng một chu kỳ đánh giá. Với evaluation_interval: 5s thì đó là 5 giây, và với phần lớn bảng điều khiển thì không ai thấy.
Độ trễ phát hiện có công thức
Ba cảnh báo giống hệt nhau, chỉ khác for. Giết mục tiêu rồi bấm giờ:
| Quy tắc | Vào trạng thái chờ | Thực sự kêu | Tổng độ trễ |
|---|---|---|---|
up == 0, không có for |
— | t+8s | 8 giây |
up == 0, for: 30s |
t+8s | t+38s | 38 giây |
up == 0, for: 2m |
t+8s | t+128s | 128 giây |
Công thức:
độ trễ phát hiện = scrape_interval + evaluation_interval + for
Kiểm chứng với cấu hình 5 giây / 5 giây:
- Không
for: 5 + 5 = 8 giây đo được (sàn, gần bằng tổng hai chu kỳ). for: 30s: 8 + 30 = 38 đúng.for: 2m: 8 + 120 = 128 đúng.
Cả ba cảnh báo vào trạng thái pending ở cùng thời điểm t+8s. for không làm chậm việc phát hiện — nó chỉ quyết định phải giữ trạng thái đó bao lâu trước khi chuyển sang firing.
Đây là điều đáng nhớ: for không phải bộ lọc, nó là bộ đếm thời gian. Điều kiện phải đúng liên tục suốt khoảng đó; chỉ cần một lần đánh giá cho kết quả sai là bộ đếm về 0 và bắt đầu lại.
Sàn 8 giây không bỏ được bằng for
Đặt for: 0 cũng không xuống dưới 8 giây, vì hai chu kỳ kia là bắt buộc: Prometheus phải quét được một lần thất bại, rồi phải chạy một vòng đánh giá quy tắc.
Muốn nhanh hơn thì phải hạ scrape_interval và evaluation_interval — và cả hai đều nhân trực tiếp vào tải của Prometheus. Với bảng ở phần 10, hạ chu kỳ quét từ 15 xuống 5 giây là gấp ba lượng dữ liệu ghi vào.
Bảng chọn thực dụng:
| Loại cảnh báo | for nên đặt |
|---|---|
Mục tiêu chết (up == 0) |
1–2 phút |
| Tỷ lệ lỗi vượt ngưỡng | 5–10 phút |
| Đĩa sắp đầy | 15–30 phút |
| Chứng chỉ sắp hết hạn | không cần for |
Dòng cuối cần giải thích: điều kiện đó vốn đã ổn định — chứng chỉ không "hết hạn nhấp nháy" — nên for chỉ làm chậm mà không lọc được gì.
Ba lỗi hay gặp với for
Đặt for dài hơn thời gian sự cố. Một sự cố kéo dài 90 giây với for: 2m thì không bao giờ kêu. Nó xuất hiện trên biểu đồ, người dùng thấy, và cảnh báo im lặng.
Quên rằng for yêu cầu điều kiện đúng liên tục. Một chỉ số dao động quanh ngưỡng sẽ liên tục đặt lại bộ đếm và không bao giờ kêu, dù nó ở trên ngưỡng phần lớn thời gian. Cách chữa là làm mượt biểu thức — avg_over_time(...) — chứ không phải tăng for.
Cảnh báo trên chỉ số ghi trước mà quên độ trễ cộng dồn. Chỉ số ghi trước trễ một chu kỳ, rồi cảnh báo đánh giá trên nó lại trễ một chu kỳ nữa. Với chuỗi ba tầng quy tắc, độ trễ cộng lại thành đáng kể.
Chỗ phép đo này không nói tới
Tôi đo với scrape_interval và evaluation_interval đều là 5 giây — nhanh hơn nhiều so với mặc định 15 giây và 15 giây. Với cấu hình mặc định, sàn không phải 8 giây mà khoảng 20–30 giây.
Tôi cũng không đo Alertmanager. Sau khi Prometheus chuyển cảnh báo sang firing, Alertmanager còn có group_wait (mặc định 30 giây), group_interval và repeat_interval — cộng thêm vào độ trễ tới lúc người trực thực sự nhận được thông báo.
Nghĩa là con số 38 giây trong bảng là độ trễ tới lúc cảnh báo chuyển trạng thái, không phải độ trễ tới lúc điện thoại rung. Khoảng cách giữa hai thứ đó thường lớn hơn cả for.
Thử ba mươi giây
Tính độ trễ phát hiện thật của hệ thống bạn:
p=http://localhost:9090
echo "--- chu ky ---"
curl -s $p/api/v1/status/config | python3 -c '
import sys,json,re
c=json.load(sys.stdin)["data"]["yaml"]
for k in ("scrape_interval","evaluation_interval"):
m=re.search(k+r":\s*(\S+)",c)
print(" ",k,"=",m.group(1) if m else "?")'
echo "--- canh bao va for cua chung ---"
curl -s $p/api/v1/rules | python3 -c '
import sys,json
for g in json.load(sys.stdin)["data"]["groups"]:
for r in g["rules"]:
if r["type"]=="alerting":
print(" %-40s for=%s" % (r["name"], r.get("duration",0)))' | head -15
Cộng ba con số cho từng cảnh báo. Nếu một cảnh báo quan trọng có tổng vượt quá thời gian mà sự cố tương ứng thường kéo dài, cảnh báo đó sẽ không bao giờ kêu — và bạn vừa tìm ra một khoảng mù.
Phần sau: cảnh báo ồn — đo tỉ lệ báo giả trên bốn cách đặt ngưỡng.