Quan sát hệ thống: log, metric, trace

Sê-ri 40 phần về giám sát và quan sát, đo chi phí thật của từng lớp công cụ.

40/40 phần đã đăng DevOps
1 Sự cố làm 0,2% request chậm gấp 56 lần mà p99 đứng yên ở 0,46 µs — chỉ p99.9 mới lộ ra 21,88 µs Mở sê-ri quan sát: tôi dựng một sự cố thật (0,2% request đi qua nhánh chậm) rồi soi bằng cả metric, log, trace và đo giá từng lăng kính. p99 mù hoàn toàn vì sự cố nằm gọn trong phần đuôi nó cắt bỏ; trung bình chỉ nhích 12%; ba trụ cột không thay thế nhau — metric nói CÓ, log nói request NÀO, trace nói chậm Ở ĐÂU. Phân vị bạn theo dõi quyết định tỷ lệ sự cố nhỏ nhất bạn thấy được. 01/09/2026 · 8 phút đọc 2 Cùng một dòng log: print tốn 0,18 µs, logging tốn 4,51 µs — và 94% khoản chênh không nằm ở chỗ ai cũng nhắm vào khi tối ưu Năm cách ghi cùng một dòng log trong Python, đo trên máy thật: khoảng cách 25 lần giữa print và logging, và khi bóc tách thì định dạng thời gian chỉ chiếm 17% — 94% chi phí biến vào bộ khung dựng LogRecord. Kèm hai phát hiện: lời gọi bị lọc bỏ rẻ tới 82 lần, nhưng chỉ khi bạn không dùng f-string. 01/09/2026 · 8 phút đọc 3 Log có cấu trúc JSON to hơn 50% khi chưa nén và chỉ 6% sau khi nén. Nhưng grep trên log chữ nhanh gấp 19 lần jq. 01/09/2026 · 6 phút đọc 4 Lấy mẫu log Lấy mẫu 1/10 làm p99 sập từ 414,9 xuống 119,9 ms — sai 71%, và sai đều ở cả ba lần đo. 01/09/2026 · 6 phút đọc 5 Thu thập log: agent và sidecar Thu thập một dòng log tốn 1,73 µs — đắt gấp ba lần chính việc ghi ra dòng đó. Và Vector ăn RAM gấp 10 lần Fluent Bit. 02/09/2026 · 6 phút đọc 6 Chỉ mục của Loki chỉ chiếm 0,42% dữ liệu — bí quyết là nó đánh chỉ mục nhãn, không đánh chỉ mục nội dung; và thư mục WAL suýt khiến tôi kết luận ngược hẳn 200.000 dòng log 30,75 MB vào Loki còn 5,40 MB trên đĩa (nén 5,69 lần), chỉ mục vỏn vẹn 0,42%. Đó là toàn bộ triết lý Loki gói trong một con số: trả tiền chỉ mục cho 15 luồng, không cho 30 MB văn bản. Nhưng thư mục WAL 32 MB suýt làm tôi kết luận Loki phình 1,22 lần thay vì nén 5,69 lần. 02/09/2026 · 9 phút đọc 7 Cùng 200.000 dòng log: Loki tốn 5,40 MB tìm 33 ms, Elasticsearch tốn 16,22 MB tìm 3 ms — và để mapping mặc định là ném đi 29,5% dung lượng lấy đúng kết quả cũ Tôi đẩy cùng một tệp log vào Loki và Elasticsearch rồi đo từng con số: ES trả lời nhanh gấp 13 lần, Loki tốn ít hơn 3 lần và nạp nhanh hơn. Nhưng phát hiện đắt nhất là mapping động mặc định của ES phí 29,5% dung lượng, nạp chậm 1,8 lần, lọc chậm 3,5 lần — cho y hệt kết quả. Đây là đánh đổi 'chỉ mục mọi thứ hay không' hiện ra bằng megabyte. 02/09/2026 · 9 phút đọc 8 logrotate báo 'thành công' rồi nuốt mất 4.317 dòng log — và không một dòng lỗi nào cảnh báo tôi Tôi cho một tiến trình ghi 300.000 dòng đánh số rồi chạy logrotate ở giữa. copytruncate làm mất 4.317 dòng, im lặng tuyệt đối; đổi tên file thì không mất dòng nào nhưng cả luồng log chảy vào chỗ không ai đọc. Hai cách xoay vòng, hai kiểu hỏng, không cái nào kêu lên. 02/09/2026 · 9 phút đọc 9 Bốn kiểu chỉ số đều ghi dưới nửa micro giây — nên chọn kiểu nào không phải chuyện hiệu năng, mà là chuyện một Histogram sinh gấp 9 lần số chuỗi so với Counter Tôi đo giá của Counter, Gauge, Histogram, Summary và thấy chỗ tính tiền không nằm ở đâu mình tưởng: cả bốn ghi dưới 0,5 µs, khoảng cách rộng nhất chỉ 2,53 lần. Cái thật sự đắt là số chuỗi thời gian — một Histogram sinh 18 dòng mỗi tổ hợp nhãn so với 2 của Counter, gấp 9 lần, và nó nhân lên chứ không cộng vào. Người thêm nhãn không phải người trả giá. 02/09/2026 · 8 phút đọc 10 Prometheus: mô hình kéo Một lần quét 50.000 nhãn chuyển 50,6 MB và mất 435 ms. Và khi mục tiêu chết, chuỗi biến mất ngay lập tức. 02/09/2026 · 5 phút đọc 11 Nhãn và bùng nổ chiều Thêm hai nhãn 'vô hại' biến 1.000 chuỗi thành 20.000. Và 50.000 giá trị nhãn ăn 1,3 GB RAM của Prometheus. 02/09/2026 · 5 phút đọc 12 Tỷ lệ request chậm tăng 10 lần: p50 nhích 3%, trung bình tăng 91%, p99 tăng 927% — vì sao trung bình trên dashboard của bạn nói dối Tôi cho tỷ lệ request chậm tăng dần trên một triệu mẫu thật và xem ba con số quen thuộc phản ứng ra sao. Kết quả: nếu theo dõi p50 bạn không thấy gì, nếu theo dõi trung bình bạn thấy quá muộn, chỉ p99 kêu đúng lúc. Và con số chính xác nhất Histogram cho bạn lại đúng là con số ít dùng nhất. 02/09/2026 · 9 phút đọc 13 avg(p99) trên bảng điều khiển của bạn báo 357 ms khi sự thật là 1.566 ms — nhẹ đi 4,4 lần đúng lúc một máy đang chết Bốn cách tính p99 trên cùng dữ liệu cho bốn kết quả. Nghịch lý: p90 sai 19,7% còn p99 chỉ sai 2,5%. Và avg(p99) — công thức có mặt trên vô số dashboard — báo nhẹ sự cố đi 4,4 lần đúng lúc bạn cần nó nhất, vì phân vị không cộng được. 02/09/2026 · 10 phút đọc 14 Ghi trước quy tắc và cảnh báo Quy tắc ghi trước làm truy vấn nhanh 35 lần. Và độ trễ phát hiện có công thức chính xác, kiểm chứng được. 02/09/2026 · 6 phút đọc 15 Cảnh báo ồn Ngưỡng tức thời báo giả 50 lần cho 3 sự cố thật. Nhưng lỗi không nằm ở quy tắc — nó nằm ở mẫu quá nhỏ. 02/09/2026 · 6 phút đọc 16 Dịch vụ vừa hết công suất mà độ trễ và lỗi vẫn hoàn hảo — client tăng 8 lần, thông lượng chỉ +8%, còn p99 gấp 15,6 lần Bốn tín hiệu vàng ai cũng nhắc, nhưng hiếm khi đo cạnh nhau trên cùng một sự cố. Tôi quét tải 1→64 client trên một dịch vụ 8 worker và đo cả bốn ở mỗi mức: saturation chạm trần ở đúng lúc độ trễ, lỗi và thông lượng còn đẹp — và định luật Little dự đoán độ trễ sau điểm bão hoà khớp trong 1,3%. Thứ tự bốn tín hiệu phản ứng mới là điều đáng nhớ. 02/09/2026 · 8 phút đọc 17 Cùng một sự cố, ba cách tính SLI cho ba kết luận trái ngược: vi phạm SLO, đạt SLO, và 'không có chuyện gì xảy ra' Tôi dựng cùng một sự cố hai lần rồi tính SLI ba cách. Theo request: vi phạm nặng. Theo cửa sổ: y hệt nhau dù số người dính khác 3,6 lần. Theo độ trễ trung bình: 100% đạt, mù hoàn toàn. Câu hỏi trung tâm của SLO không phải 'đặt bao nhiêu' mà là 'tính SLI thế nào'. 02/09/2026 · 9 phút đọc 18 40 panel làm tươi mỗi 10 giây tốn 12% một nhân cho mỗi người xem — và 'sum by' không làm Prometheus nhẹ đi một chút nào Mỗi bảng điều khiển đang mở là một chuỗi truy vấn lặp lại mà không con số chi phí nào hiện lên màn hình người đang nhìn. Tôi đo trên Prometheus 21.563 chuỗi: gộp bằng sum by chỉ cắt phần trả về chứ không cắt phần đọc (nhanh hơn vỏn vẹn 1,88 lần), cái nó thật sự cứu là trình duyệt; bước là đòn bẩy lớn nhất; và chi phí nhân thẳng với số người đang mở — mười người là hơn một nhân trọn vẹn. 02/09/2026 · 8 phút đọc 19 Giảm mẫu bằng trung bình để tiết kiệm đĩa đã xoá 84% chiều cao đỉnh của tôi — 939 ms còn 150 ms, và không gì trên màn hình cho biết Prometheus nén một mẫu còn 2,16 byte, và giãn chu kỳ thu thập tiết kiệm gần tuyến tính. Nhưng giảm mẫu bằng trung bình là phép biến đổi KHÔNG đảo ngược được: một đợt chậm 939 ms bị san còn 150 ms, biểu đồ vẫn có đường, chỉ là đỉnh biến mất và không ai biết. Cách giữ đỉnh mà vẫn tiết kiệm. 02/09/2026 · 9 phút đọc 20 svc-a mất 137 ms và trông như thủ phạm — nhưng nó chỉ tự làm 10 ms, kẻ ăn 101 ms nằm ở giữa chuỗi Một yêu cầu đi qua bốn dịch vụ. Nhìn từ ngoài, hai sự cố khác hẳn nhau chỉ là một con số. Metric ở dịch vụ đầu bó tay, còn trace chỉ thẳng vào svc-c: tự làm 101,45 ms trong khi ba chặng kia đều quanh 10 ms. Bài học: đọc trace theo cột 'tự làm', đừng theo cột 'tổng'. 02/09/2026 · 9 phút đọc 21 BatchSpanProcessor mặc định lặng lẽ vứt 62,5% số trace của tôi — và nghịch lý là dịch vụ càng nhanh càng mất nhiều opentelemetry-sdk thật, xuất OTLP, đo cả tốc độ lẫn số byte. Con số tốc độ đúng như dự đoán. Nhưng số span THỰC SỰ tới nơi thì không: hàng đợi mặc định 2.048 làm mất gần hai phần ba trace khi dịch vụ chạy nhanh, chỉ ghi đúng một dòng log. Không lỗi, không chỉ số. 02/09/2026 · 9 phút đọc 22 Một dịch vụ quên chuyển tiếp một header, và trace của tôi vỡ đôi — cộng lấy mẫu, tỷ lệ tìm thấy trace rơi 9,8 lần Cho đúng một chặng giữa chuỗi quên chuyển tiếp header traceparent, cái cây trace tách làm hai — mà không dịch vụ nào báo lỗi, cả hai mảnh đều 'khoẻ mạnh'. Cộng lấy mẫu 10%, tỷ lệ yêu cầu có trace đầy đủ rơi từ 9,93% xuống 1,01%. Mỗi chỗ gãy không cộng chi phí, nó nhân. 02/09/2026 · 9 phút đọc 23 Cùng ngân sách lưu trữ: lấy mẫu đầu giữ 10% trace lỗi, lấy mẫu đuôi giữ 100% — vì một bên quyết định trước khi biết kết quả Trace đầy đủ đắt, lấy mẫu là câu trả lời. Nhưng lấy mẫu KIỂU NÀO đổi hẳn giá trị: cùng chi phí lưu, lấy mẫu đầu 10% giữ 10,48% trace lỗi, lấy mẫu đuôi giữ trọn 100% — chênh 9,5 lần thứ bạn thật sự cần. Cái giá của lấy mẫu đuôi là 46,3 MB bộ nhớ đệm, lớn hơn ước tính 3,6 lần. 02/09/2026 · 9 phút đọc 24 du -sb báo thư mục Jaeger nặng 2.193 MB — sự thật là 16 MB, sai 133 lần vì một file thưa, và suýt làm cả bài đo sai 432 lần Jaeger vs Tempo trên cùng 20.000 trace: Tempo nhỏ hơn 3,3 lần, tìm theo dịch vụ nhanh hơn 8 lần, còn Jaeger lấy theo trace_id nhanh hơn. Nhưng con số đáng nhớ nhất là 133 — chênh lệch giữa ba cách đo cùng một dung lượng, và là lần thứ ba trong sê-ri du trên thư mục dữ liệu nói dối tôi. 02/09/2026 · 10 phút đọc 25 Trả thêm 59,8% dung lượng log để giảm số dòng phải đọc khi điều tra từ 48.000 xuống 4 — một đánh đổi hiếm khi rõ ràng đến thế Nhúng trace_id vào mỗi dòng log tốn thêm 59,8% dung lượng. Đổi lại: khi có sự cố, số dòng bạn phải lọc để tìm đúng chuyện đã xảy ra giảm 12.000 lần. Thời gian máy chạy gần như không đổi — cái thay đổi là thời gian của con người lúc đang cháy nhà. 02/09/2026 · 9 phút đọc 26 Gắn trace tự động đắt gấp 2,45 lần tự viết tay — và tôi vẫn khuyên bạn chọn nó, vì lý do không nằm trong bảng đo Bộ công cụ tự động đắt hơn tự viết span 2,45 lần và chỉ cho thêm đúng một thuộc tính. Nhìn bảng thì viết tay thắng rõ. Nhưng so chi phí là so sai chỗ: thứ phân biệt hai cách không phải nano-giây, mà là cái giá của một lần ai đó quên. 02/09/2026 · 9 phút đọc 27 Trace bảo truy vấn mất 0,060 ms, PostgreSQL bảo 0,007 ms — cả hai đều đúng, và 88% thời gian nằm ngoài cơ sở dữ liệu Span db.query nói một con số, pg_stat_statements nói một con số khác cho đúng truy vấn đó, và chúng lệch nhau gần chục lần. Không cái nào sai — chúng đo hai thứ khác nhau, và cái hiệu của chúng mới là con số quyết định việc tối ưu SQL của bạn có ích hay hoàn toàn vô nghĩa. 02/09/2026 · 9 phút đọc 28 Biểu đồ máy chủ ghi 0,673 ms trong khi người dùng chờ 151 ms tải trang — không ai nói dối, họ chỉ đang trả lời ba câu hỏi khác nhau Máy chủ tự báo 0,673 ms, người dùng thật chờ 151,255 ms — nó mù 99,6% thời gian. Tôi đo khoảng cách đó khi thêm độ trễ mạng, và phát hiện kích thước phản hồi chỉ lộ ra khi có độ trễ (chênh 2,2% trên mạng nhanh thành 128% khi thêm 50 ms), còn keep-alive tiết kiệm đúng một vòng khứ hồi. Ba con số trong bảng là ba đại lượng khác nhau, không phải ba mức chính xác của một đại lượng. 02/09/2026 · 9 phút đọc 29 Dịch vụ của tôi chết 5 phút lúc 2 giờ sáng và giám sát thụ động bỏ sót 54% số lần — vì lúc đó 305/500 phút không có một request nào Giám sát thụ động chỉ thấy lỗi khi có ai đó gặp lỗi. Tôi đo khoảng cách của nó so với kiểm tra tổng hợp: ban ngày hai cách ngang nhau, nhưng ban đêm — giờ không ai dùng — thụ động bỏ lọt hơn nửa số sự cố ngắn. Toàn bộ giá trị của synthetic check nằm ở đúng cái khoảng trống lưu lượng đó. 02/09/2026 · 9 phút đọc 30 Tôi đoán hàm này ăn 70% CPU — chính tay tôi viết nó — hai phép đo nói 90,7% và 98,3%; và py-spy đo được điều đó gần như miễn phí Lấy mẫu hồ sơ ngoài tiến trình ở 99 Hz gần như không tốn gì (−0,8%), nên bật được suốt ngày ở production; còn cProfile làm chương trình chậm gấp 2,06 lần và tự tạo sự cố hiệu năng. Nhưng bài học lớn nhất là con số tôi đoán sai về mã của chính mình — thời gian chạy không tỷ lệ với số dòng. 02/09/2026 · 9 phút đọc 31 Bật cả log, chỉ số và trace lấy đi 12,9% thông lượng — nhưng một lớp sinh 10,8 GB/ngày còn lớp kia chỉ 7,2 MB, chênh 1.540 lần Tôi cộng cả ba lớp quan sát trên cùng một dịch vụ và đo tổng: −12,9% thông lượng, +19,3% CPU, +45,8% bộ nhớ. Nhưng phát hiện quan trọng hơn con số tổng là mô hình chi phí — chỉ số giữ trạng thái gộp nên KHÔNG tỷ lệ với lưu lượng (1.312 byte dù 20 nghìn hay 20 triệu yêu cầu), còn log và trace ghi từng sự kiện nên phình tuyến tính. Cùng lượng việc, trace tốn gấp 1.540 lần chỉ số. 02/09/2026 · 9 phút đọc 32 Giữ chỉ số một năm không làm dashboard hằng ngày chậm đi một mili-giây — đòn bẩy chi phí thật không phải thời gian giữ, mà là chu kỳ thu thập Dung lượng tăng tuyến tính theo thời gian giữ, nhưng chi phí truy vấn thì DƯỚI tuyến tính — giữ lâu chỉ làm hoá đơn dài ra, không làm bảng điều khiển chậm. Và đòn bẩy tiết kiệm lớn nhất (15 lần) là chu kỳ thu thập, không phải rút thời gian giữ. Quyết định 'giữ bao lâu' hoá ra hoàn toàn là quyết định về giá trị, không phải kỹ thuật. 02/09/2026 · 9 phút đọc 33 Bốn cách viết truy vấn Prometheus sai mà không hề báo lỗi — một trong đó thổi con số lên gấp 3,44 lần, một cái giấu mất 75% chiều cao đỉnh Bốn lỗi bảng điều khiển tôi gặp thường xuyên nhất, đo bằng chính Prometheus: cửa sổ rate quá ngắn cho biểu đồ trống, rate(sum()) thay vì sum(rate()) cho con số cao 3,44 lần, vẽ counter thô, và step lớn hơn cửa sổ rate giấu 75% đỉnh. Điểm chung đáng sợ: không cái nào sinh ra lỗi — biểu đồ vẫn vẽ, truy vấn vẫn trả 200. 02/09/2026 · 8 phút đọc 34 Chuông trực ban reo sau 2 phút 43 giây kể từ lúc dịch vụ hỏng — và gần một phần ba khoảng đó không có nút nào chỉnh được Tôi đo khoảng cách giữa lúc dịch vụ bắt đầu hỏng và lúc người trực ban biết. Với cấu hình Prometheus phổ biến (for: 2m), chuông reo sau 2 phút 43 giây — gấp 11 lần cấu hình nhanh nhất. Và có một cái sàn 14,6 giây không luật nào rút ngắn được, vì nó không nằm ở đó. 02/09/2026 · 11 phút đọc 35 Trace tốn gấp 4.178 lần chỉ số mỗi ngày — nhưng ba trong sáu câu hỏi của báo cáo sự cố lại cần đúng cái nguồn rẻ nhất, và cần nó cũ nhất Ba ngày sau sự cố, bạn ngồi viết báo cáo — dữ liệu nào còn sống? Tôi tính ở 1.000 req/s bằng các hằng số đo trong sê-ri: chỉ số 0,01 GB/ngày, trace 43,45 GB/ngày (gấp 4.178 lần). Đặt cùng thời gian giữ cho cả ba là lãng phí 88%, và cái nghịch lý đẹp là nguồn rẻ nhất trả lời câu khó nhất — 'đã từng xảy ra chưa' — cần dữ liệu vài tháng tuổi. 02/09/2026 · 8 phút đọc 36 Mỗi pod trong Kubernetes thêm đúng 187 chuỗi chỉ số — kể cả pod không làm gì; và ba pod sống một phút để lại 4.580 chuỗi vĩnh viễn Bản thân cụm Kubernetes cũng là một nguồn phát chỉ số, và nó phát ngay cả khi bạn chưa triển khai gì. Tôi đo: 187 chuỗi/pod tuyến tính hoàn hảo, kể cả pod pause chẳng làm gì. Và một bất ngờ — xoá sạch một namespace để lại 4.580 chuỗi control plane không bao giờ co lại (nhưng nó bão hoà). 02/09/2026 · 10 phút đọc 37 Quan sát một binary không có mã nguồn: proxy tốn 33 µs mỗi request, eBPF tốn 1,4 µs — nhưng ba lần đo đầu của tôi đều cho eBPF 0%, vì probe không hề chạy Khi dịch vụ cần quan sát là một binary bạn không sửa được, còn hai đường: proxy sidecar hay eBPF. Proxy đắt hơn eBPF ~24 lần, nhưng cái 33 µs ấy là giá của ngữ nghĩa HTTP, không phải giá quan sát. Và bài học đắt nhất: một phép đo cho '0% chi phí' vì nó không hề chạy. 02/09/2026 · 9 phút đọc 38 Luật cảnh báo tôi viết để bắt sự cố im lặng hoàn toàn đúng lúc cần nhất — và nhịp tim vẫn đập thêm 3 phút sau khi Prometheus đã chết Nếu chính hệ giám sát chết, bạn biết bằng cách nào? Tôi giết lần lượt exporter, Prometheus, Alertmanager và đo cái nào thật sự kêu. Luật ngưỡng bạn viết (dich_vu_hong > 0) im lặng khi exporter biến mất; chỉ up==0 và absent() bắt được. Và dead man's switch qua Alertmanager có khoảng mù 3 phút. 02/09/2026 · 9 phút đọc 39 20.000 chỉ số Prometheus chỉ ăn 0,12% một lõi CPU — nhưng cái chết trước không phải RAM, mà là đĩa, sớm hơn 7,3 lần Tôi đo chi phí tài nguyên thật của Prometheus tự dựng: 20.000 chuỗi tốn 0,12% một lõi và 69 MB RAM. Nhỏ đến mức 'tự dựng tốn máy' là sai. Nhưng nếu bạn chọn máy theo RAM, bạn sẽ mua một cỗ chết vì hết đĩa khi mới dùng một phần bảy số RAM đã trả. 02/09/2026 · 9 phút đọc 40 Dựng cả một hệ giám sát từ số không tới chuông reo mất 11–20 giây — 39 phần còn lại chỉ để biết mình đang đo đúng thứ gì Bài chốt sê-ri quan sát: tôi đo thời gian từ 'chưa có gì' tới lúc cảnh báo thật sự reo (11–20 giây), gom bốn mươi phần thành một danh sách kiểm và một bảng hằng số ước lượng được, rồi rút ra kết luận thật của cả sê-ri — hầu hết lỗi quan sát không báo lỗi, chúng chỉ hiện ra ở một con số vô lý khi có sẵn con số khác để đối chiếu. 02/09/2026 · 9 phút đọc