# Bàn giao để khách tự trực: health endpoint, metric và cảnh báo

> Một health check viết sai có thể khởi động lại cả cụm chỉ vì database chập chờn vài giây, và người bị gọi dậy lúc nửa đêm sẽ là đội vận hành của khách chứ không phải bạn.

Bản gốc: https://fdetimes.net/vi/bach-khoa/thuc-hanh-health-endpoint-metric-canh-bao/

Database của khách chập chờn chừng ba mươi giây. Health check của dịch vụ bạn bàn giao có gọi database nên báo lỗi theo, và orchestrator lần lượt khởi động lại mọi instance. Chỉ một dependency trục trặc mà cả cụm thành sự cố.

Hướng dẫn Health Endpoint Monitoring trên Azure Architecture Center của Microsoft cảnh báo đúng tình huống này: liveness check phải "nông" để sự cố ở dependency không gây ra những lần restart không cần thiết. Lỗi kiểu này ít khi lộ ra lúc demo. Nó thường lộ ra sau khi FDE đã rời đi, lúc đội vận hành của khách một mình nhìn dashboard.

Vì thế, với FDE, bàn giao không dừng ở chỗ code chạy được. Đội của khách phải tự trực được dịch vụ đó. Phần dưới đây dựng ba lớp theo đúng thứ tự: health endpoint, chỉ số vận hành, rồi cảnh báo.

## Bạn sẽ dựng gì, và cần chuẩn bị gì?

Thử hình dung một dịch vụ tra cứu đơn hàng. Nó có một dependency bắt buộc là database và một dependency tuỳ chọn là cache gợi ý sản phẩm. Mất cache thì dịch vụ chậm hơn hoặc thiếu phần gợi ý, nhưng vẫn trả được thông tin đơn hàng.

Bạn cần một service nhỏ do chính bạn viết, bằng framework nào cũng được, và một môi trường dev để tắt bật dependency. Các đoạn mã bên dưới là **phác thảo giản lược** viết theo kiểu Python. Bạn hãy chuyển chúng sang framework và thư viện health check mình đang dùng.

## Bước 1: liveness chỉ trả lời "process còn sống không?"

Ví dụ của Microsoft tách hẳn hai endpoint. `/healthz/live` trả 200 miễn là process còn phản hồi. Kubernetes dùng liveness probe để phát hiện container đã rơi vào trạng thái hỏng rồi khởi động lại nó, nên endpoint này chỉ nên trả lời đúng một câu hỏi: có cần restart hay không.

```python
# Phác thảo giản lược
def live():
# KHÔNG gọi database, cache hay API ngoài ở đây
return 200, {"status": "alive"}
```

**Kiểm tra:** tắt database ở môi trường dev rồi gọi `/healthz/live`. Kết quả vẫn phải là 200. Nếu nó trả lỗi, bạn vừa tái hiện đúng kịch bản restart hàng loạt ở đầu bài.

## Bước 2: readiness kiểm tra dependency, nhưng phải biết cái nào quan trọng hơn

`/healthz/ready` là check "sâu". Theo Microsoft, endpoint này trả 200 khi ứng dụng phục vụ được request, trả 503 khi một check bắt buộc thất bại, và mỗi check dependency phải có timeout riêng. Hướng dẫn cũng nói rõ: dependency tuỳ chọn không được tham gia vào quyết định rút instance khỏi vòng phục vụ.

```python
# Phác thảo giản lược
REQUIRED = {"db": check_db}
OPTIONAL = {"recommend_cache": check_cache}

def ready():
for name, check in REQUIRED.items():
if not run_with_timeout(check, seconds=2):
return 503, {"status": "unhealthy"}
for name, check in OPTIONAL.items():
if not run_with_timeout(check, seconds=2):
return 200, {"status": "degraded"}
return 200, {"status": "healthy"}
```

Trạng thái `degraded` là thứ đáng học nhất ở bước này. Microsoft lưu ý rằng probe sâu nhắm vào một dependency chậm có thể gây sự cố dây chuyền. Cách giảm rủi ro họ đưa ra là dùng timeout, circuit breaker và báo trạng thái theo mức độ: dependency không quan trọng bị mất thì báo degraded, không báo unhealthy.

Con số 2 giây ở trên chỉ là ví dụ. Bạn chỉnh nó theo độ trễ thực tế của database bên khách.

**Kiểm tra:** tắt cache thì `/healthz/ready` phải trả 200 kèm `degraded`. Tắt database thì nó phải trả 503 trong khoảng thời gian bằng timeout, không được treo mãi.

## Bước 3: khoá endpoint lại, đừng chỉ giấu nó đi

Hướng dẫn của Microsoft nói thẳng: đặt endpoint ở một port lạ hay một đường dẫn khó đoán không phải là kiểm soát truy cập. Endpoint công khai chỉ nên trả thông tin tối thiểu. Phần chẩn đoán chi tiết, chẳng hạn dependency nào hỏng hay mất bao nhiêu mili giây, phải yêu cầu xác thực.

Ở site khách, việc này thường phải làm cùng đội bảo mật của họ. Bạn nên hỏi sớm xem probe nội bộ đi qua mạng nào và ai được xem phần chẩn đoán.

## Bước 4: chỉ số vận hành là bốn golden signal, không phải bốn chục biểu đồ

Health endpoint chỉ cho bạn ảnh chụp tình trạng ở thời điểm hiện tại. Microsoft phân biệt health monitoring với availability monitoring: availability theo dõi dịch vụ theo thời gian để tính ra thống kê uptime. Vì vậy đội của khách nên có cả hai, một thứ để biết lúc này dịch vụ có ổn không, một thứ để biết nó đã chạy ổn trong bao lâu.

Về chỉ số cần theo dõi, sách SRE của Google nêu bốn golden signal cho mọi dịch vụ có người dùng: latency, traffic, errors và saturation. Với dịch vụ tra cứu đơn hàng, đó là thời gian phản hồi, số request mỗi giây, tỷ lệ lỗi và mức độ đầy của tài nguyên, chẳng hạn connection pool.

Latency nên được tách theo từng bước. Microsoft khuyên đo độ trễ của từng bước trong một request và gắn số đo với đúng request đó để nhanh chóng tìm ra nút thắt. Khi tổng latency tăng, đội vận hành phải thấy ngay nguyên nhân nằm ở truy vấn database hay ở lần gọi cache.

Ngoài ra còn có synthetic user, tức một kịch bản giả lập người dùng đi theo chuỗi bước định sẵn, bên cạnh request tracing và log exception, warning. Với dịch vụ ví dụ, synthetic user có thể đăng nhập, tra một mã đơn mẫu rồi kiểm tra kết quả trả về.

## Bước 5: cảnh báo gọi người dậy thì phải kèm việc để làm

Đây là chỗ nhiều bản bàn giao hỏng nhất. Microsoft khuyên chỉ cảnh báo khi sức khoẻ của các luồng quan trọng thay đổi đáng kể, không cảnh báo theo từng lần probe thất bại, đồng thời chỉnh timeout và ngưỡng để không phản ứng với lỗi thoáng qua.

Chẳng hạn, luật "readiness thất bại liên tục quá vài phút" tốt hơn luật "readiness fail một lần". Con số cụ thể thì bạn chốt cùng khách.

Sách SRE của Google đặt ra hai yêu cầu: lần page nào cũng phải dẫn tới một hành động, và luật cảnh báo dành cho người phải dễ hiểu, thể hiện một lỗi rõ ràng. Một cách áp dụng là viết mỗi luật thành dữ liệu, trong đó có sẵn trường ghi việc đầu tiên cần làm:

```python
# Phác thảo giản lược: không theo cú pháp của công cụ cảnh báo nào.
# Bạn chuyển sang cú pháp của hệ thống giám sát khách đang dùng.
ALERTS = [
{
"name": "ready_unhealthy",
"level": "red",
"condition": "readiness trả 503 liên tục trong 5 phút",
"page": True,
"first_action": "Kiểm tra kết nối và trạng thái database",
},
{
"name": "ready_degraded",
"level": "yellow",
"condition": "readiness báo degraded liên tục trong 30 phút",
"page": False,
"first_action": "Ghi nhận, xử lý cache trong giờ hành chính",
},
]
```

Các mốc 5 phút và 30 phút chỉ là ví dụ. Nguyên tắc quan trọng hơn: luật nào bạn không điền được `first_action` thì luật đó chưa nên có `page: True`.

Microsoft bổ sung rằng hệ thống nên báo trong vòng vài giây khi có thành phần không khoẻ. Họ cũng đưa ra mô hình đèn đỏ, vàng, xanh, trong đó đèn vàng nghĩa là chỉ còn một phần chức năng hoạt động. Mô hình này khớp với ba trạng thái unhealthy, degraded và healthy ở Bước 2.

**Điểm mấu chốt:** Cảnh báo nào không kèm một việc cụ thể để làm thì đừng đánh thức ai dậy.

Đừng quên lớp bảo mật. Theo Microsoft, nhiều lần đăng nhập thất bại có thể là dấu hiệu tấn công brute-force, còn lượng request tăng vọt bất thường có thể là DDoS. Hai tín hiệu này nên có cảnh báo riêng, gửi tới đội phụ trách bảo mật của khách.

Sản phẩm bàn giao cuối cùng là một bảng như dưới đây, đặt ngay trong runbook:

| Cảnh báo | Ý nghĩa | Việc đầu tiên của người trực |
|---|---|---|
| Đỏ: readiness 503 kéo dài | Database bắt buộc không phản hồi | Kiểm tra kết nối và trạng thái database |
| Vàng: degraded kéo dài | Mất cache gợi ý, đơn hàng vẫn tra được | Ghi nhận, xử lý cache trong giờ hành chính |
| Latency bước truy vấn tăng vọt | Nút thắt nằm ở database | Xem các truy vấn chậm và connection pool |
| Đăng nhập thất bại tăng đột biến | Có thể là brute-force | Chuyển cho đội bảo mật |

**Kiểm tra:** đưa bảng cho một người bên khách chưa từng đọc code. Dòng nào khiến họ hỏi lại "rồi tôi làm gì?" thì dòng đó chưa nên dùng để gọi người trực dậy.

## Những lỗi hay gặp

Lỗi hay gặp nhất là dùng chung một endpoint cho cả liveness lẫn readiness. Khi đó endpoint duy nhất ấy buộc phải chạm vào dependency, và sự cố dependency nào cũng có thể dẫn tới restart, đúng điều mà khuyến nghị check nông của Microsoft muốn tránh.

Lỗi tiếp theo là coi mọi dependency như nhau, đến mức một cache phụ cũng rút được instance khỏi vòng phục vụ. Ngoài ra còn lỗi viết check không có timeout, khiến một database chậm kéo cả probe treo theo.

Về phía cảnh báo, lỗi hay gặp là page theo từng probe. Đội vận hành nhận vài lần báo động giả, quen dần rồi tắt thông báo, và đến khi có sự cố thật thì không còn ai để ý.

## Kỹ năng này thể hiện thế nào ở site khách và trong CV?

Ở site khách, việc nên làm đầu tiên là ngồi với đội vận hành, hỏi họ đang dùng công cụ giám sát nào và ai trực ca đêm. Sau đó bạn mới viết endpoint và luật cảnh báo cho khớp với hệ thống của họ. Bộ health check có tốt đến đâu mà không nối vào kênh trực của khách thì cũng không giúp được ai.

Trong CV, thay vì viết chung chung "có kinh nghiệm monitoring", hãy kể cụ thể: bạn đã tách liveness với readiness, thêm trạng thái degraded và viết runbook để đội khách tự trực được.

Thước đo một lần bàn giao tốt rất đơn giản: sau khi bạn rời đi, đêm đầu tiên có sự cố, đội của khách tự xử lý xong mà không cần gọi cho bạn.

**Thử ngay tuần này:**

- Mở một dịch vụ bạn đang làm, tìm health check hiện có và xem nó có gọi database hay API ngoài không. Nếu có, tách nó thành live và ready.
- Tắt thử một dependency tuỳ chọn ở môi trường dev, rồi xác nhận /healthz/ready trả về degraded chứ không phải 503.
- Viết danh sách luật cảnh báo cho dịch vụ đó, mỗi luật có trường 'việc đầu tiên cần làm', rồi đưa cho một đồng nghiệp chưa từng đụng vào code đọc thử.

## Nguồn

- [Health Endpoint Monitoring Pattern - Azure Architecture Center | Microsoft Learn](https://learn.microsoft.com/en-us/azure/architecture/patterns/health-endpoint-monitoring)

- [Best Practices for Monitoring and Diagnostics - Azure Architecture Center | Microsoft Learn](https://learn.microsoft.com/en-us/azure/architecture/best-practices/monitoring)

- [Monitoring Distributed Systems - Google SRE Book](https://sre.google/sre-book/monitoring-distributed-systems/)

- [Configure Liveness, Readiness and Startup Probes | Kubernetes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/)
