Monitoring và logging chi phí thấp
Pipeline trả về 200 chưa chắc còn đúng
Ở capstone phần 1, bạn đã train model, đăng ký phiên bản vào MLflow và deploy lên VPS rẻ. Container chạy, endpoint trả về 200. Nhưng health check chỉ chứng minh process còn sống, không chứng minh model còn chính xác hay hóa đơn còn trong tầm kiểm soát. Bài này gắn lên pipeline đó một lớp quan sát mỏng, đủ để bạn biết lúc nào cần can thiệp.
Sau bài này, bạn có thể:
- Liệt kê 4 nhóm tín hiệu bắt buộc phải theo dõi cho một model đang phục vụ.
- Đo drift bằng PSI trên summary statistics thay vì lưu toàn bộ request.
- Viết log có cấu trúc, lấy mẫu và xoay vòng để không đầy disk.
- Đặt ngưỡng cảnh báo ít mà đúng, tránh báo động giả.
Bốn nhóm tín hiệu, và nhóm thứ tư hay bị bỏ quên
Đừng giám sát mọi thứ. Chỉ giám sát những thứ dẫn tới một hành động cụ thể.
| Nhóm tín hiệu | Ví dụ | Nguồn | Chi phí thu thập |
|---|---|---|---|
| Drift dữ liệu | PSI theo từng feature | Tóm tắt request theo giờ | Rất thấp nếu lưu summary |
| Lỗi serving | Tỉ lệ 5xx, exception trace | Log lỗi 100% | Thấp |
| Độ trễ | p50, p95, p99 latency | Metric trong app | Thấp |
| Chi phí, tài nguyên | CPU, RAM, disk, egress | node_exporter, cron df |
Gần như bằng 0 |
Nhóm thứ tư là nhóm bị bỏ quên nhiều nhất: chính chi phí hạ tầng. Một model không lỗi, không drift, nhưng disk đầy vì log thì vẫn là sự cố.
Sơ đồ dòng chảy tối giản nên dựng:
flowchart TD
Req["📥<br/>Request"] --> Svc["🚀<br/>Serving"]
Svc --> Met["📊<br/>Metrics"]
Svc --> Log["📝<br/>Log mẫu"]
Met --> Store["🗄️<br/>Prometheus, MinIO"]
Log --> Store
Store --> Alert["🔔<br/>Cảnh báo"]
Alert --> Human["👤<br/>Người trực"]
Mọi thứ nằm trên chính VPS đó hoặc một VPS nhỏ thứ hai. Không cần dịch vụ trả tiền nào.
Drift: phát hiện sớm mà không cần giữ từng request
Drift là khi phân phối dữ liệu đầu vào lúc chạy khác với lúc train. Bạn không cần biết nhãn thật để phát hiện nó. Cách rẻ nhất là so sánh phân phối.
Chỉ số dễ dùng nhất là PSI (Population Stability Index), đo mức lệch giữa baseline và dữ liệu production:
$$ PSI = \sum_{i} (p_i - q_i),\ln!\left(\frac{p_i}{q_i}\right) $$
Với $p_i$ là tỉ lệ trong baseline, $q_i$ là tỉ lệ hiện tại của bin thứ $i$. Quy ước thực chiến: dưới 0.1 là ổn, 0.1 đến 0.25 là cảnh báo, trên 0.25 là drift nghiêm trọng, phải hành động.
Điểm mấu chốt về chi phí: bạn chỉ cần lưu summary theo cửa sổ thời gian, gồm histogram bin, mean, std, min, max, cùng số lượng mẫu. Mỗi feature vài chục con số mỗi giờ thay vì hàng triệu dòng raw. Baseline lấy từ dữ liệu gần lần train nhất, thường 7 đến 14 ngày, và lưu cố định trong MinIO dưới dạng JSON nhỏ.
Note: Drift không đồng nghĩa với model sai. Nó là tín hiệu để bạn xem lại. Đừng tự động retrain chỉ vì PSI vượt ngưỡng trong một giờ.
Bài tập nhanh: log của bạn đang ghi toàn bộ payload request, khoảng 1 triệu request mỗi ngày. Bạn cần làm gì trước khi bật đo drift?
Gợi ý đáp án
Đổi sang ghi summary theo giờ: với mỗi feature, đếm số mẫu rơi vào từng bin của histogram đã định nghĩa sẵn từ lúc train. Lưu một file JSON nhỏ mỗi giờ vào MinIO. Như vậy vẫn tính được PSI theo ngày hoặc theo tuần, nhưng dung lượng giảm từ gigabyte xuống kilobyte.
Lỗi serving và độ trễ: bắt bằng metric, không bằng mắt
Uptime 100% là chỉ số dễ gây tự mãn nhất. Một endpoint trả 200 với latency 4 giây là endpoint đang hỏng dưới góc nhìn người dùng.
Ba thứ cần có:
- Đếm lỗi theo mã trạng thái, tách 4xx (client sai) khỏi 5xx (hệ thống sai). Nhóm 5xx mới là thứ đánh thức bạn lúc 2 giờ sáng.
- Histogram latency, đọc p95 chứ không đọc giá trị trung bình. Trung bình che mất nhóm người dùng chậm.
- Đánh dấu
model_versiontrong mọi metric. Không có nó, bạn không biết bản deploy nào gây lỗi.
Với một VPS nhỏ, bạn không cần stack nặng. Một middleware vài chục dòng trong app, ghi ra /metrics theo định dạng Prometheus, đã đủ. Nếu muốn tối giản hơn nữa: một cron mỗi phút gọi endpoint, đo thời gian, ghi ra file CSV, và để script cảnh báo đọc file đó.
Log gọn nhẹ: có cấu trúc, có mẫu, có vòng đời
Log là khoản chi phí ẩn lớn nhất trong monitoring. Một dòng log JSON 1KB, nhân 1 triệu request mỗi ngày, là 1GB mỗi ngày, 30GB mỗi tháng, chưa tính tiền đọc lại và egress.
Ba quy tắc:
- Có cấu trúc: log dạng JSON với các trường cố định như
timestamp,request_id,model_version,latency_ms,status,prediction. Máy parse được thì mới truy vấn được. - Lấy mẫu: ghi 1 đến 5% request thành công, nhưng ghi 100% request lỗi. Lỗi là thứ bạn cần đủ mẫu để tái hiện.
- Có vòng đời: dùng
logrotatenén và xoay vòng hằng ngày, giữ 7 đến 14 ngày, đẩy bản nén lên MinIO với lifecycle policy tự xóa. Log cũ hơn hai tuần hiếm khi giúp ích điều tra.
Một mẹo nhỏ: đặt level đúng. INFO cho sự kiện nghiệp vụ, WARNING cho bất thường chưa gây lỗi (ví dụ payload thiếu trường và đã dùng giá trị mặc định), ERROR cho thứ làm request thất bại. Nếu mọi thứ đều INFO, log của bạn vô dụng.
Bài tập nhanh: trong bốn dòng log sau, dòng nào nên giữ, dòng nào nên hạ cấp hoặc bỏ?
A. {"level":"INFO","msg":"request ok","payload":"...2KB..."}
B. {"level":"ERROR","msg":"model load failed","model_version":"7"}
C. {"level":"INFO","msg":"health check ok"} (mỗi 5 giây)
D. {"level":"WARNING","feature":"age","msg":"out of range, clamped"}
Gợi ý đáp án
B giữ nguyên, đây là log quan trọng nhất. D giữ, nhưng gộp đếm theo giờ để tránh bùng nổ số dòng. A bỏ payload, chỉ giữ request_id, latency_ms, status. C hạ xuống mức DEBUG hoặc bỏ hẳn, vì health check mỗi 5 giây tạo ra hơn 17.000 dòng mỗi ngày mà không ai đọc.
Cảnh báo ít mà đúng
Nguyên tắc: mỗi cảnh báo phải kèm một hành động. Nếu bạn không biết sẽ làm gì khi nhận nó, đừng tạo nó.
Bộ ngưỡng khởi điểm hợp lý cho một VPS nhỏ:
- Tỉ lệ 5xx trên 2% trong 5 phút liên tục.
- p95 latency vượt 2 lần giá trị lúc baseline.
- PSI của feature quan trọng nhất vượt 0.25.
- Disk sử dụng trên 80%, RAM trên 85%.
- Không có log mới trong 10 phút (dấu hiệu process chết im lặng).
Trong Prometheus, thuộc tính for: 5m giúp loại bỏ nhiễu nhất thời. Với kênh gửi, Telegram hoặc email qua webhook là đủ và miễn phí. Nhóm cảnh báo cùng nguyên nhân vào một tin nhắn thay vì bắn 50 tin.
Những cạm bẫy khiến hóa đơn monitoring tăng gấp ba
- Log mọi thứ "để sau này cần". Không ai đọc lại. Hãy log những gì bạn biết cách truy vấn.
- Đo drift trên raw request. Dung lượng tăng tuyến tính, còn giá trị thông tin thì gần như không đổi so với summary.
- Đặt cảnh báo theo từng request. Một request lỗi không phải sự cố, một tỉ lệ lỗi kéo dài mới là sự cố.
- Để log và metric trên đĩa root của VPS. Disk đầy làm sập cả model lẫn MLflow. Tách volume hoặc đẩy sang MinIO.
- Không đặt retention. Đây là khoản phí âm thầm tăng mỗi ngày mà không có hóa đơn nào nhắc bạn.
- Quy tắc ngón tay: nếu lớp quan sát chiếm quá 10 đến 15% chi phí hạ tầng, nó đang làm việc quá sức so với nhu cầu của bạn.
Gói lại và mảnh ghép tiếp theo
Bốn điều mang theo: giám sát bốn nhóm tín hiệu và chỉ giám sát thứ dẫn tới hành động; đo drift bằng PSI trên summary statistics; log có cấu trúc, lấy mẫu 1 đến 5% cho request thành công và 100% cho lỗi, kèm retention 7 đến 14 ngày; và mỗi cảnh báo phải có ngưỡng rõ ràng cùng một việc cần làm.
Lớp quan sát này giờ đã có. Bài tiếp theo đi qua những cạm bẫy chi phí cụ thể cùng checklist vận hành để bạn biến các tín hiệu trên thành quy trình kiểm tra hằng tuần.
Front: PSI là gì? Back: Chỉ số đo mức lệch phân phối giữa dữ liệu baseline và dữ liệu production.
Front: Ngưỡng PSI thực chiến? Back: Dưới 0.1 ổn, 0.1 đến 0.25 cảnh báo, trên 0.25 là drift nghiêm trọng.
Front: Vì sao không lưu raw request để đo drift? Back: Dung lượng và egress tăng rất nhanh, trong khi summary statistics đã đủ để tính PSI.
Front: Cần lưu gì để tính drift rẻ? Back: Histogram bin, mean, std, min, max và số mẫu theo từng cửa sổ thời gian.
Front: Bốn nhóm tín hiệu cần giám sát? Back: Drift dữ liệu, lỗi serving, độ trễ, và chi phí hoặc tài nguyên hạ tầng.
Front: Metric nào phản ánh trải nghiệm người dùng tốt hơn latency trung bình? Back: p95 hoặc p99 latency, vì giá trị trung bình che mất nhóm request chậm.
Front: Tỉ lệ lấy mẫu log hợp lý? Back: 1 đến 5% với request thành công, 100% với request lỗi.
Front: Structured logging là gì? Back: Log dạng JSON với trường cố định như request_id, model_version, latency_ms để máy truy vấn được.
Front: logrotate dùng để làm gì? Back: Xoay vòng và nén log cũ theo ngày để disk không bị đầy.
Front: Retention log nên đặt bao lâu? Back: Khoảng 7 đến 14 ngày, sau đó nén và để lifecycle policy tự xóa.
Front: Alert fatigue là gì? Back: Quá nhiều cảnh báo khiến người trực bỏ qua cả những cảnh báo thật.
Front: Bộ đôi OSS tự host phổ biến cho metric và dashboard? Back: Prometheus thu thập metric, Grafana hiển thị dashboard.
Front: Dấu hiệu process chết im lặng? Back: Không có log mới trong một khoảng thời gian, dù health check bên ngoài vẫn gọi.
Điền nhanh để tự kiểm tra: Drift nghiêm trọng khi PSI vượt _____. Chỉ số đo lệch phân phối giữa baseline và production là _____. Với log, nên lấy mẫu _____% request thành công nhưng ghi _____% request lỗi. Để tránh disk đầy, đặt _____ log khoảng 7 đến 14 ngày và dùng _____ để nén, xoay vòng.