Ba mươi ba phần trước của sê-ri này đều đo chi phí: log tốn bao nhiêu byte, trace tốn bao nhiêu CPU, bảng điều khiển tốn bao nhiêu mili giây. Phần này đo một thứ khác hẳn — thời gian. Cụ thể là khoảng cách giữa hai mốc mà mọi báo cáo sự cố đều phải ghi: lúc dịch vụ bắt đầu hỏng, và lúc người trực ban biết chuyện đó.

Khoảng cách ấy thường bị bàn như một chỉ số văn hoá. Nhưng phần lớn của nó là số học của đường ống cảnh báo — và số học thì đo được.

Duong di cua mot canh bao va do tre do duoc o tung chang

Bảng số liệu

Hệ đo dựng trong container dùng một lần: một dịch vụ giả phơi chỉ số dich_vu_hong bật tắt được qua HTTP, Prometheus thu thập mỗi 15 giây và đánh giá luật mỗi 15 giây, Alertmanager gom nhóm rồi gọi webhook, và một máy nhận webhook ghi đúng khoảnh khắc chuông reo. Phép đo: ghi mốc, bật hỏng, đợi chuông.

for group_wait Nhanh nhất Trung vị Chậm nhất Biên độ Số lần
0s 0s 6,3 s 14,6 s 20,0 s 13,7 s 8
0s 30s (mặc định) 49,7 s 50,3 s 55,5 s 5,8 s 5
1m 30s 95,4 s 105,4 s 107,7 s 12,3 s 4
2m 30s 160,7 s 162,6 s 175,1 s 14,4 s 3

Dòng cuối là cấu hình mà rất nhiều đội đang chạy thật — for: 2m để cảnh báo khỏi kêu vì một cú giật nhất thời, group_wait để nguyên mặc định. Nó cho 2 phút 43 giây, gấp 11,1 lần dòng đầu tiên.

Điều đáng nhớ

Có một sàn không chỉnh được. Dòng đầu bảng là cấu hình cực đoan: for: 0s, group_wait: 0s — vặn hết cỡ về phía nhanh. Nó vẫn mất trung vị 14,6 giây, và có lần mất 20,0 giây. Không có tuỳ chọn nào trong luật cảnh báo hay trong Alertmanager rút ngắn được khoảng này, vì nó không nằm ở đó: nó là scrape_interval cộng evaluation_interval. Sự cố xảy ra ngay sau một lần thu thập thì phải đợi hết 15 giây mới có mẫu, rồi có thể đợi thêm gần 15 giây nữa mới tới lượt đánh giá luật.

Ở cấu hình mặc định phổ biến nhất — không đặt for, để group_wait nguyên 30 giây — tổng là 50,3 giây, trong đó 14,6 giây là sàn không đụng tới được. Gần ba phần mười.

Các núm vặn cộng đúng như khai, sai số nằm trong một ô lưới. Đây là phép kiểm chứng tôi tính trước khi nhìn số:

Bước Khai báo thêm Trung vị thực tế tăng Lệch
thêm group_wait: 30s 30 s 35,7 s +5,7 s
thêm for: 1m 60 s 55,1 s −4,9 s
nâng lên for: 2m 60 s 57,2 s −2,8 s

Cả ba độ lệch đều nhỏ hơn 15 giây, tức là nằm gọn trong một ô của lưới đánh giá. Không có chi phí ẩn nào đáng kể; cái bạn khai là cái bạn nhận, cộng thêm phần làm tròn lên lưới.

Vì sao

Đường đi của một cảnh báo có sáu chặng (xem sơ đồ), nhưng chỉ hai chặng nghe lời bạn: forgroup_wait. Hai chặng đầu — chờ lần thu thập kế tiếp và chờ lần đánh giá luật kế tiếp, mỗi chặng 0 đến 15 giây — là nguồn của toàn bộ biên độ. Ở dòng đầu bảng, biên độ 13,7 giây gần đúng bằng một scrape_interval: cùng một sự cố, cùng một cấu hình, lặp lại tám lần cho ra từ 6,3 đến 20,0 giây, chỉ vì nó rơi vào chỗ khác nhau trên lưới. Nên "thời gian phát hiện" của một sự cố đơn lẻ gần như vô nghĩa: nó là một mẫu rút từ phân bố rộng bằng cả chu kỳ thu thập.

Cái mà chờ lâu mua lại cho bạn

Bảng trên chỉ có một mặt: for càng lớn thì càng chậm. Nhưng không ai đặt for: 2m vì thích chậm. Để đo mặt kia, tôi cho ba mức for chạy đồng thời trên cùng một Prometheus, cùng một chuỗi dữ liệu — ba luật khác nhau nhìn chung một chỉ số, nên chúng thấy y hệt nhau và khác biệt duy nhất là for.

Kịch bản: bốn sự cố giả (hỏng 30 giây rồi tự khỏi, cách nhau 30 giây), sau đó một sự cố thật (hỏng liên tục hơn ba phút).

Luật for Kêu vì sự cố giả Bắt được sự cố thật Tổng số lần chuông
Ngay 0s 4 lần 5
Cho1Phut 1m 0 lần 1
Cho2Phut 2m 0 lần 1

Ba lần chạy độc lập cho ra bảng giống hệt nhau, không lệch một con số nào.

Không đặt for thì trong bảy phút bạn nhận năm lần chuông, bốn trong đó là rác — 80% số lần bị đánh thức là cho chuyện tự khỏi trước khi bạn kịp mở laptop. Đó là cái giá thật của việc biết nhanh.

Nhưng con số đáng chú ý hơn nằm ở dòng giữa: for: 1m đã chặn sạch cả bốn sự cố giả, y hệt for: 2m, mà nhanh hơn 57 giây. Vì for không hoạt động theo kiểu càng dài càng sạch — nó chỉ cần dài hơn cơn giật. Cơn giật ở đây kéo dài 30 giây, nên mọi giá trị for vượt qua 30 giây đều cho kết quả như nhau, và mỗi giây đặt thêm quá mốc đó là một giây bạn trả mà không mua được gì.

Nên for không phải núm vặn theo cảm tính: hãy nhìn lịch sử của chính chỉ số đó, hỏi cơn giật vô hại dài nhất từng kéo bao lâu, đặt for nhỉnh hơn con số ấy, rồi dừng.

Nghĩa là gì trong thực tế

Chính sách leo thang thường viết kiểu "gọi người thứ nhất, sau 5 phút không ai nhận thì gọi người thứ hai". Con số 5 phút đó được tính từ lúc chuông reo, không phải từ lúc dịch vụ hỏng. Với for: 2mgroup_wait mặc định, người thứ hai bị gọi ở phút thứ 7 phút 43 giây kể từ lúc sự cố bắt đầu, chứ không phải phút thứ 5. Nếu chỉ tiêu dịch vụ của bạn nói "khôi phục trong 10 phút" thì gần 80% ngân sách ấy đã tiêu hết trước khi người thứ hai chạm vào bàn phím.

Ba điều rút ra được:

  • Đừng hạ scrape_interval để cảnh báo nhanh hơn. Nó tốn lưu trữ trên mọi chỉ số (phần 19 đã đo) mà chỉ mua lại vài giây. Cần nhanh thật thì tách một job chu kỳ ngắn cho vài chỉ số sống còn.
  • Hạ group_wait rẻ hơn hạ for nhiều. Nó cắt 30 giây mà không đánh đổi gì về nhiễu.
  • Tính thời gian phát hiện từ mốc của dữ liệu, đừng tính từ lúc chuông reo — chuông đã trễ hằng phút một cách có hệ thống.

Chỗ tôi không kết luận được

Bài này đo thời gian phát hiện, không đo thời gian sửa. Vế sau là người, và tôi không dựng được nó trong container. Điều duy nhất nói được là hai vế cộng vào nhau, và vế đầu — thứ ai cũng tưởng là tức thì — không hề nhỏ.

Số lần lặp không đều: 8 lần cho cấu hình nhanh nhất nhưng chỉ 3 lần cho for: 2m, vì mỗi lần đo mất gần ba phút — trung vị 162,6 giây nên đọc là "khoảng hai phút rưỡi tới ba phút". Và tất cả chạy trên mạng nội bộ Docker; Alertmanager thật còn phải gọi ra Slack hay PagerDuty qua Internet, chặng đó tôi không đo.

Bốn lần đo hỏng, và cái duy nhất tự tố cáo mình

Tôi đo hỏng bốn lần trước khi có bảng ở trên, và ba trong bốn lần cho ra những con số trông hoàn toàn bình thường.

Lần một tới lần ba: bốn tiến trình đo chạy chồng lên nhau. Mỗi lần thấy kết quả lạ, tôi khởi động lại phép đo mà không dừng lần trước. Cả bốn cùng tạo container tên promam, cùng xoá của nhau giữa chừng. Kết quả: cấu hình nhanh nhất cho 164,0 giây, còn for: 2m cho 7,9 giây. Dấu hiệu duy nhất là những dòng Conflict. The container name "/am" is already in use lẫn giữa output — tôi đã lướt qua chúng ba lần vì đang bận nghi ngờ Alertmanager. Bài học: khi phép đo cho số vô lý, việc đầu tiên là đếm xem có bao nhiêu bản của nó đang chạy, chứ không phải sửa cấu hình đối tượng đo.

Lần bốn: thời gian âm. Máy nhận webhook tích luỹ bản ghi suốt cả lần chạy và không bao giờ được xoá, trong khi mỗi cấu hình lại dùng lại đúng tên cảnh báo DichVuHong1..5. Nên cấu hình thứ hai vừa bắt đầu đã tìm thấy ngay bản ghi của cấu hình thứ nhất — sớm hơn mốc bắt đầu của nó. Ra −126,1 giây.

Đây là lỗi duy nhất trong cả bốn lỗi không thể trốn được: một khoảng thời gian âm là chuyện không tồn tại, nên nó tự chỉ vào chính mình. Ba lỗi kia đều nằm trong khoảng hợp lý và sẽ lặng lẽ đi thẳng vào bài viết nếu tôi không có một con số cắt ngang để đối chiếu.

Và một lần suýt lọt. Bộ số đầu tiên sạch có ba lần liên tiếp đúng 29,8 giây. Trùng khít ba lần là thứ không nên xảy ra với một đại lượng có biên độ 15 giây. Lý do: mỗi lần đo mất xấp xỉ một bội số của 15 giây, nên mốc bắt đầu của lần sau luôn rơi vào cùng một pha của lưới. Tôi đang lấy mẫu một điểm và tưởng là lấy mẫu cả phân bố. Sửa bằng cách chờ ngẫu nhiên 0–15 giây trước mỗi lần đo — và biên độ thật, 13,7 giây, mới hiện ra.

Thử ba mươi giây

Mở cấu hình Prometheus và Alertmanager của bạn, cộng ba con số: scrape_interval + for của cảnh báo quan trọng nhất + group_wait. Cộng thêm một scrape_interval nữa cho chặng đánh giá luật.

Đó là thời gian tối thiểu trước khi có người biết hệ thống của bạn đang hỏng. Đem so với con số phút trong chính sách leo thang, và với chỉ tiêu khôi phục bạn đã hứa với ai đó.