---
id: case-alert
title: "Alert không khớp với user"
shortTitle: "Alert lệch user"
track: cases
trackTitle: "Tình huống vận hành"
order: 6
level: important
status: published
estimatedMinutes: 30
prerequisites:
  - monitoring-grafana
  - monitoring-prometheus
next: case-rollback
reviewedAgainst: 2026-10-12
sources:
  - title: "Monitoring distributed systems"
    url: https://sre.google/sre-book/monitoring-distributed-systems/
  - title: "Prometheus query functions"
    url: https://prometheus.io/docs/prometheus/latest/querying/functions/
---

# Alert không khớp với user

Bài này gom hai kiểu sự cố. Kiểu thứ nhất: alert reo mà user vẫn dùng bình thường. Kiểu thứ hai: user không làm được gì mà chẳng có alert nào. Cả hai đều có chung một gốc: alert đang đo nhầm thứ.

## Để làm gì

Bạn cần quyết định alert này có đáng đánh thức người trực hay không, rồi sửa tín hiệu để lần sau nó bám theo triệu chứng mà user gặp.

## Gồm những gì

- Symptom và cause, theo cách sách Google SRE phân biệt.
- Vì sao một counter đang tăng không có nghĩa là hệ thống đang lỗi.
- Health check trả 200 trong khi dependency đã chết.

Bài không dựng thêm hệ monitoring nào, vì Prometheus và Grafana đã có bài riêng.

## Dùng khi nào

Điện thoại reo lúc nửa đêm vì CPU cao, bạn mở dịch vụ ra thì mọi request vẫn 200. Hoặc sáng ra mới biết đêm qua checkout lỗi cả tiếng, trong khi biểu đồ CPU vẫn phẳng lì.

Đừng tắt alert cho đỡ ồn khi chưa biết nó đang đo gì, vì tắt alert là mất luôn tín hiệu. Hãy sửa điều kiện hoặc đổi sang metric khác.

## Kiến thức cốt lõi

Sách SRE tách symptom khỏi cause. Symptom là thứ user cảm nhận được: lỗi, chậm. Cause là CPU, disk, một process chết. Alert dùng để đánh thức người trực nên gắn với symptom; cause để trên dashboard, dành cho lúc bạn đã mở máy ra điều tra. Nếu CPU cao mà latency và tỉ lệ lỗi không đổi, đó là việc xem trong giờ làm, không đáng một cuộc gọi lúc nửa đêm.

Với Prometheus, counter chỉ tăng. Giá trị `http_requests_total` nhảy lên không cho biết tốc độ; `rate(http_requests_total[5m])` mới là tốc độ. Alert đặt trên giá trị thô của counter sẽ reo mãi, vì con số đó không bao giờ giảm. Bài Prometheus đã nói điều này, và trong sự cố, đây thường là lý do khiến một alert có cảm giác "sai".

Health check là một request đặc biệt. Nếu health check không đụng tới database trong khi request thật của user thì có, load balancer sẽ giữ một máy đang bệnh trong cụm. User nhận 500, còn alert uptime vẫn xanh. Cách sửa là cho health check fail khi dependency bắt buộc chết, hoặc thêm một metric đếm lỗi trên chính các request của user.

Khi alert reo, điều tra theo thứ tự:

1. Viết một câu: user đang thấy gì. Câu này đi đầu vì nó là tiêu chuẩn để bạn đánh giá alert. Nếu chưa trả lời được, bạn chưa biết alert đúng hay sai.
2. Xem tỉ lệ 5xx và latency trong đúng cửa sổ thời gian của alert, bằng log hoặc metric của request user.
3. Đối chiếu với tín hiệu mà alert đang đo. Nếu hai bên không cùng kể một câu chuyện, đừng sửa production cho khớp alert. Hãy sửa alert cho khớp với user.

Alert đã được sửa đúng khi nó reo trong những khoảng thời gian user thật sự gặp lỗi, và im lặng khi user vẫn dùng bình thường.

## Lệnh và code mẫu

Bạn không cần server. Hãy đọc hai biểu thức sau:

```text
http_requests_total > 1000
rate(http_requests_total{status=~"5.."}[5m]) > 0.1
```

Biểu thức đầu gần như luôn đúng khi app đã chạy đủ lâu. Biểu thức sau đo tốc độ lỗi. Ngưỡng 0.1 chỉ là ví dụ; ngưỡng thật phải lấy từ lưu lượng thực tế của bạn, không lấy từ bài này.

Trong Grafana, đặt cạnh nhau ba panel trên cùng một khoảng thời gian: tỉ lệ 5xx, latency và CPU. Nếu ba đường không cùng kể một câu chuyện, alert của bạn đang chỉ vào cause chứ không phải symptom.

## Thực hành

1. Với mỗi biểu thức ở trên, chỉ ra biểu thức nào sẽ reo dù app vẫn khỏe.
2. Endpoint `/health` trả 200 mà không query gì. Viết ra user sẽ thấy gì khi Postgres chết, và alert uptime sẽ thấy gì.
3. Không đổi ngưỡng trên Grafana của công ty trong lúc làm bài.

## Kết quả mong đợi

Bạn chỉ ra được rằng alert trên counter thô là vô dụng. Bạn mô tả được cách một health check giả tạo che đi một dependency đã chết.

## Checklist

- [ ] Hỏi user đang thấy gì trước khi sửa ngưỡng.
- [ ] Alert ban đêm gắn với lỗi hoặc latency, không gắn riêng với CPU.
- [ ] Counter luôn đi qua `rate` hoặc `increase`, không so với một con số tuyệt đối khi counter tăng mãi.
- [ ] Health check đụng tới những dependency mà request của user cần.

## Lỗi thường gặp

**Nới ngưỡng CPU cho tới khi hết reo.** Lần sau CPU cao đi kèm timeout thật thì không còn ai nghe nữa. Hãy tách riêng alert theo symptom, còn CPU để trên dashboard.

**Thêm mười alert mới sau một sự cố.** Người trực sẽ tắt tiếng hết. Một alert "user không checkout được" có giá trị hơn mười alert cho mười nguyên nhân có thể xảy ra.

## Nguồn

1. [Monitoring distributed systems](https://sre.google/sre-book/monitoring-distributed-systems/) — symptom và cause.
2. [Query functions](https://prometheus.io/docs/prometheus/latest/querying/functions/) — `rate` trên counter.

## Tiếp theo

[Rollback sau deploy](./07-rollback.md)
