Phần trước đo khoảng cách giữa thứ máy chủ thấy và thứ người dùng chịu. Bài này hỏi câu tiếp: nếu không có người dùng nào thì sao? Giám sát thụ động — mọi thứ sê-ri này đã đo cho tới giờ — chỉ nhìn thấy lỗi khi có ai đó gặp lỗi. Kiểm tra tổng hợp tự gọi dịch vụ theo lịch. Câu hỏi là khoảng cách giữa hai cách lớn tới đâu, và ở đâu.
Bảng số liệu
Mô phỏng một ngày với lưu lượng có hình dạng thật, 500 lần chạy cho mỗi ô. Giám sát thụ động báo động khi thấy 3 request lỗi; kiểm tra tổng hợp tự gọi mỗi phút.
Lưu lượng theo giờ (yêu cầu mỗi phút):
| Khung giờ | Trung vị | Số phút không có request nào |
|---|---|---|
| 00h–06h | 0 | 305 / 500 |
| 06h–09h | 39 | 0 / 500 |
| 09h–18h | 656 | 0 / 500 |
| 18h–23h | 89 | 0 / 500 |
Sự cố kéo dài 5 phút:
| Bắt đầu lúc | Thụ động phát hiện sau | Thụ động bỏ sót | Tổng hợp |
|---|---|---|---|
| 02:00 | 3 phút | 54% | 0 phút, bỏ sót 0% |
| 04:00 | 3 phút | 54% | 0 phút, bỏ sót 0% |
| 23:00 | 3 phút | 52% | 0 phút, bỏ sót 0% |
| 08:00 / 14:00 / 20:00 | 0 phút | 0% | 0 phút, bỏ sót 0% |
Sự cố kéo dài 15 phút:
| Bắt đầu lúc | Thụ động | Tổng hợp |
|---|---|---|
| 02:00 | 4 phút, bỏ sót 1% | 0 phút, bỏ sót 0% |
| 04:00 | 5 phút, bỏ sót 2% | 0 phút, bỏ sót 0% |
| 23:00 | 5 phút, bỏ sót 1% | 0 phút, bỏ sót 0% |
Điều đáng nhớ
Ban ngày, hai cách ngang nhau. Cả hai phát hiện trong vòng 0 phút, không bỏ sót lần nào. Ở 656 yêu cầu mỗi phút, ngưỡng 3 lỗi bị vượt gần như tức thì.
Ban đêm thì khác hẳn: thụ động bỏ sót 54% số sự cố 5 phút. Hơn một nửa số lần, dịch vụ chết rồi sống lại mà không có báo động nào kêu. Lý do nằm ở hàng đầu bảng: lúc 2 giờ sáng, 305 trong 500 phút không có một request nào. Không có request thì không có lỗi để đếm.
Sự cố dài hơn thì cuối cùng cũng bị bắt, nhưng chậm. Với sự cố 15 phút, thụ động phát hiện sau 4–5 phút và chỉ bỏ sót 1–2%. Đủ request tích luỹ để vượt ngưỡng — nhưng đó là bốn tới năm phút dịch vụ chết mà không ai biết.
Kiểm tra tổng hợp không nhanh hơn ban ngày. Nó cũng 0 phút, y như thụ động. Toàn bộ giá trị của nó nằm ở đúng một chỗ: giờ không ai dùng.
Vì sao
Giám sát thụ động đo lỗi, và lỗi là một sự kiện cần hai thứ: dịch vụ hỏng, và có ai đó chạm vào nó. Thiếu vế thứ hai thì không có gì để đếm. Đây không phải hạn chế của công cụ mà là bản chất của phương pháp — Prometheus, Datadog hay bất cứ thứ gì khác đều mù như nhau trong khoảng trống lưu lượng.
Kiểm tra tổng hợp lật ngược quan hệ đó: nó tự tạo ra vế thứ hai. Cứ mỗi phút nó gọi dịch vụ một lần, nên xác suất phát hiện chỉ phụ thuộc vào chu kỳ kiểm tra chứ không phụ thuộc vào việc có ai đang dùng hay không.
Con số 54% bỏ sót có nguồn gốc số học đơn giản. Sự cố kéo 5 phút; ban đêm mỗi phút có trung bình khoảng nửa request. Trong 5 phút đó, kỳ vọng khoảng 2,5 request đi qua — dưới ngưỡng 3 lỗi. Khoảng một nửa số lần mô phỏng, tổng cộng không đủ 3 request để vượt ngưỡng, và sự cố kết thúc trong im lặng.
Điều đáng nói là hạ ngưỡng xuống không cứu được. Ngưỡng 1 lỗi sẽ bắt được nhiều hơn, nhưng ban ngày ở 656 request mỗi phút thì một lỗi lẻ là chuyện bình thường và báo động sẽ kêu suốt. Ngưỡng phải đủ cao cho giờ cao điểm, và chính điều đó làm nó mù trong giờ thấp điểm.
Nghĩa là gì trong thực tế
- Nếu dịch vụ của bạn có khoảng trống lưu lượng, kiểm tra tổng hợp không phải thứ xa xỉ. Nó là thứ duy nhất nhìn thấy đêm hôm. Với dịch vụ nội bộ chỉ dùng trong giờ hành chính, khoảng trống đó là hai phần ba thời gian.
- Đặt chu kỳ kiểm tra theo thời gian phát hiện bạn chấp nhận được. Kiểm mỗi phút cho thời gian phát hiện tối đa một phút. Kiểm mỗi năm phút thì sự cố ba phút vẫn có thể lọt.
- Đừng chỉ kiểm trang chủ. Kiểm tra tổng hợp chỉ đi những đường bạn viết ra. Một đường đăng nhập hỏng trong khi trang chủ vẫn 200 là trường hợp mà kiểm tra tổng hợp làm bạn yên tâm sai.
- Đừng bỏ giám sát thụ động. Ban ngày nó ngang bằng, và nó thấy được thứ mà kiểm tra tổng hợp không bao giờ thấy: các đường mà người dùng thật đi qua, với dữ liệu thật, ở trạng thái thật.
- Kiểm tra tổng hợp từ nhiều vùng địa lý còn thấy được thứ ở phần trước: mạng, DNS, chứng chỉ, định tuyến — những thứ mà cả máy chủ lẫn giám sát thụ động đều không thấy vì chúng đứng ở phía bên kia của vấn đề.
Chỗ tôi không kết luận được
Đây là mô phỏng, không phải hệ thống thật. Tôi sinh lưu lượng theo phân bố tôi chọn rồi áp hai luật phát hiện lên. Phần đo được là số học của việc phát hiện; nó không nói gì về độ trễ thật của đường ống báo động, về thời gian gom cửa sổ, hay về việc ai đó có thực sự nhìn vào cảnh báo lúc 2 giờ sáng hay không.
Hình dạng lưu lượng là tôi đặt vào. Một dịch vụ toàn cầu không có khoảng trống ban đêm — lúc nào cũng là ban ngày ở đâu đó — và với dịch vụ đó, phần lớn kết luận trong bài này không áp dụng. Ngược lại, dịch vụ nội bộ của một công ty một quốc gia còn có khoảng trống rộng hơn của tôi, kể cả cuối tuần.
Ngưỡng 3 lỗi là con số tôi chọn tuỳ ý. Nó quyết định trực tiếp tỷ lệ bỏ sót 54%. Ngưỡng khác cho số khác; điều không đổi là luôn tồn tại một ngưỡng đủ cao để im lặng ban ngày và đủ cao để mù ban đêm, vì cùng một ngưỡng phải phục vụ cả hai.
Tôi không đo chi phí của kiểm tra tổng hợp. Nó thêm lưu lượng, thêm dòng log, thêm dữ liệu vào chỉ số — và ở dịch vụ có lưu lượng thấp, những request tổng hợp có thể trở thành phần lớn số liệu, làm lệch chính các biểu đồ mà nó bảo vệ. Đó là một phép đo tôi chưa làm.
Lần đo đầu tiên của tôi cho kết quả vô dụng. Tôi cho sự cố kéo 30 phút và lưu lượng đêm 0–3 request mỗi phút; kết quả là thụ động phát hiện sau 0–2 phút và chỉ bỏ sót 0,1%. Kết luận khi đó sẽ là "kiểm tra tổng hợp gần như không thêm gì" — đúng với thí nghiệm đó và sai với thực tế.
Vấn đề là tôi đã dựng một thí nghiệm quá dễ: sự cố dài gấp sáu lần và lưu lượng đêm cao gấp sáu lần so với con số thật. Sửa cả hai — sự cố 5 phút, lưu lượng đêm Poisson với trung bình 0,5 — thì tỷ lệ bỏ sót nhảy từ 0,1% lên 54%. Đây là lần thứ hai trong sê-ri tôi phải dựng lại thí nghiệm vì nó không thể cho thấy điều đang tìm; lần trước là bảng điều khiển 400 chuỗi.
Thử ba mươi giây
docker run --rm python:3.12-slim python - <<'EOF'
import random
random.seed(21)
NGUONG, LAN = 3, 2000
def thu(lam_moi_phut, dai_su_co):
"""Xac suat giam sat thu dong PHAT HIEN duoc su co."""
bat = 0
for _ in range(LAN):
thay = sum(max(0, int(random.gauss(lam_moi_phut, lam_moi_phut)))
for _ in range(dai_su_co))
if thay >= NGUONG: bat += 1
return 100 * bat / LAN
print("%-22s %14s %14s" % ("luu luong/phut", "su co 5 phut", "su co 15 phut"))
for lam in (0.5, 2, 10, 100):
print("%-22s %13.0f%% %13.0f%%" % ("%.1f req/phut" % lam, thu(lam,5), thu(lam,15)))
EOF
Cột đầu là lưu lượng lúc thấp điểm của bạn. Nếu dòng tương ứng không phải 100%, đó là tỷ lệ sự cố mà giám sát thụ động sẽ bắt được — phần còn lại đi qua trong im lặng.