kiến trúc observability với grafana
Tung Bui Thanh (TungDaDev) (updated 6 months ago)11 mins read

Vận hành một hệ thống phân tán với hàng chục microservices trên Kubernetes giống như đang trực phòng cấp cứu: không nhìn thấy nhịp tim, huyết áp của bệnh nhân thì không xử lý được gì.
Bây giờ Grafana không còn chỉ là "công cụ vẽ biểu đồ cho đẹp" nữa. Nó là chỗ nhìn tập trung (Single Pane of Glass) của cả nền tảng Observability.
Vậy mà nhiều team vẫn dính mấy lỗi quen thuộc:
- Nhồi hàng trăm biểu đồ CPU, RAM rời rạc vào một Dashboard, nhưng không biết hệ thống đang "sống hay chết" với người dùng cuối.
- Bị Alert Fatigue đánh thức nửa đêm vì mấy cảnh báo vô nghĩa (ví dụ: CPU lên 80% trong 10 giây dù ứng dụng vẫn xử lý bình thường).
- Đưa label có độ biến thiên cao (High Cardinality như
userId,orderId) vào Prometheus, làm sập bộ nhớ máy chủ giám sát. - Metrics, Logs, Traces không liên kết với nhau, nên điều tra một lỗi 500 phải mở tới 5 công cụ.
Dưới đây là kiến trúc Grafana cho production: hệ sinh thái LGTM Stack, các phương pháp giám sát (4 Golden Signals, RED, USE), cách tính Error Budget Burn Rate (SLO) và quản lý Dashboard as Code.
# hệ sinh thái hợp nhất: bộ tứ lgtm stack
Một hệ thống Observability Cloud Native không chạy riêng lẻ từng mảnh. Grafana Labs gom thành bộ LGTM Stack (Loki cho log, Grafana để hiển thị, Tempo cho trace, Mimir cho metric), cộng thêm Pyroscope cho profiling, để điều tra lỗi liền một mạch, gần như chỉ cần vài cú click:
# quy trình điều tra sự cố một chạm
- Phát hiện (Metrics): Bạn thấy một đỉnh nhọn trên biểu đồ
P99 Latencycủa service thanh toán trong Grafana. - Lần vết (Traces): Nhờ Exemplar, trên biểu đồ có một chấm nhỏ đại diện cho request chậm nhất. Click vào chấm đó, Grafana mở luôn Tempo Trace Waterfall và chỉ ra bước gọi HTTP sang cổng thanh toán bên thứ ba mất 28 giây.
- Chi tiết (Logs): Bấm nút "Logs for this Span", Grafana tự lọc các dòng log trong Loki có cùng
trace_idvà hiện ra:SocketTimeoutException: Read timed out. - Tối ưu code (Profiling): Nếu thấy CPU tăng cao, mở Pyroscope Flamegraph là biết ngay hàm Regex parsing trong Java đang chiếm 85% CPU cycles.
TIP
Nên bật Exemplar trong Prometheus/Mimir và OpenTelemetry SDK. Exemplar gắn trace_id vào một datapoint cụ thể trên biểu đồ P99/P95, nên bạn nhảy thẳng từ biểu đồ sang trace waterfall mà không phải copy ID bằng tay. Với Prometheus, exemplar là tính năng phải bật bằng cờ --enable-feature=exemplar-storage, và app phải xuất metric theo định dạng OpenMetrics thì exemplar mới đi kèm.
# phương pháp luận thiết kế dashboard: red, use & 4 golden signals
Dashboard tốt không phải cái có nhiều biểu đồ nhất, mà là cái giúp kỹ sư quyết định hành động nhanh nhất.
# red method cho service apis
- Rate (Tốc độ): Số lượng request/giây mà dịch vụ đang phục vụ:
sum(rate(http_server_requests_seconds_count{application="$service"}[5m])) - Errors (Lỗi): Tỷ lệ phần trăm các request bị lỗi (HTTP 5xx):
sum(rate(http_server_requests_seconds_count{application="$service", status=~"5.."}[5m])) / sum(rate(http_server_requests_seconds_count{application="$service"}[5m])) * 100 - Duration (Thời lượng/Độ trễ): Thời gian phản hồi ở các phân vị P50, P95 và P99 (đừng dùng Average, vì nó che mất các đỉnh trễ cục bộ). Lưu ý Spring Boot/Micrometer mặc định không xuất bucket histogram cho
http.server.requests, phải bậtmanagement.metrics.distribution.percentiles-histogram.http.server.requests=truethì metric_bucketdưới đây mới có:histogram_quantile(0.99, sum(rate(http_server_requests_seconds_bucket{application="$service"}[5m])) by (le))
# use method cho hạ tầng
- Utilization: Thời gian tài nguyên bận rộn (CPU Usage %, RAM Usage %).
- Saturation: Mức độ công việc bị dồn ứ không thể xử lý ngay (CPU Load Average, ThreadPool Queue Depth, Disk I/O Queue).
- Errors: Số lượng lỗi xảy ra ở tầng thiết bị (Network Packet Drops, Disk Write Errors).
# kiến trúc cảnh báo chuẩn sre: multi-window multi-burn-rate
Nhiều hệ thống giám sát đặt cảnh báo kiểu sơ sài thế này: IF cpu_usage > 85% FOR 5m THEN alert("CPU High")
Cách này dẫn tới Alert Fatigue: kỹ sư nhận hàng trăm tin Slack mỗi ngày, quen dần rồi bỏ qua luôn tin cảnh báo thật khi hệ thống sập.
# triết lý sre của google: cảnh báo dựa trên triệu chứng
Người dùng không quan tâm CPU của bạn chạy 10% hay 90%. Họ chỉ quan tâm: "Ứng dụng có chạy được không?" (Errors) và "Có nhanh không?" (Latency).
Vì vậy cảnh báo nên dựa trên SLI (Service Level Indicator) và tốc độ tiêu thụ ngân sách lỗi (Error Budget Burn Rate):
SLO nội bộ thường đặt chặt hơn SLA ký với khách (ở đây 99.9% so với 99.5%), để còn khoảng đệm xử lý trước khi vi phạm hợp đồng. Burn rate là tốc độ đốt ngân sách so với mức "vừa đủ dùng hết trong 30 ngày": burn rate 14.4 trong 1 giờ nghĩa là đốt 14.4 × 1/720 = 2% ngân sách tháng. Con số 14.4 lấy từ Google SRE Workbook.
# công thức alerting đa cửa sổ
Để bớt cảnh báo ảo (False Positives) mà vẫn bắt được sự cố nghiêm trọng trong vài phút, SRE Workbook của Google kiểm tra kép trên hai cửa sổ thời gian. Cửa sổ dài (1 giờ) đảm bảo đã đốt đủ nhiều ngân sách mới báo; cửa sổ ngắn (5 phút, bằng 1/12 cửa sổ dài) đảm bảo lỗi vẫn đang diễn ra, nên sự cố hết thì cảnh báo cũng tắt nhanh thay vì kêu thêm cả tiếng:
# Prometheus alerting rule (Alertmanager nhận và định tuyến cảnh báo đi Slack/PagerDuty)
- alert: HighErrorBudgetBurnRate
expr: |
(
# Cửa sổ ngắn 5 phút: Burn Rate > 14.4x
sum(rate(http_server_requests_seconds_count{status=~"5.."}[5m]))
/ sum(rate(http_server_requests_seconds_count[5m])) > (1 - 0.999) * 14.4
)
and
(
# Cửa sổ dài 1 giờ: đã đốt đủ 2% ngân sách, không phải một cú giật ngẫu nhiên
sum(rate(http_server_requests_seconds_count{status=~"5.."}[1h]))
/ sum(rate(http_server_requests_seconds_count[1h])) > (1 - 0.999) * 14.4
)
for: 2m
labels:
severity: critical
team: core-platform
annotations:
summary: 'Hệ thống đang đốt 2% Error Budget của cả tháng chỉ trong vòng 1 giờ!'
runbook_url: 'https://wiki.company.internal/runbooks/high-error-rate'Workbook còn khuyên thêm một cặp cửa sổ cho mức nhẹ hơn: burn rate 6 trên cửa sổ 6 giờ và 30 phút (đốt 5% ngân sách), cũng gọi người dậy; còn burn rate 1 trên 3 ngày và 6 giờ (đốt 10%) thì chỉ tạo ticket, sáng ra xử lý.
# hiểm họa bùng nổ cardinality trong promql
Cardinality là số chuỗi thời gian duy nhất (Unique Time Series) sinh ra từ các tổ hợp label.
Total Time Series = (Số giá trị Label 1) x (Số giá trị Label 2) x ... x (Số giá trị Label n)# thảm họa thực tế & cách khắc phục
Một lập trình viên thêm userId hoặc orderUuid vào metric đo lường:
// THẢM HỌA CARDINALITY LÀM SẬP PROMETHEUS!
registry.counter("order.created",
"user_id", order.getUserId(), // Có 10,000,000 users khác nhau!
"order_id", order.getId().toString() // Có hàng triệu đơn hàng!
).increment();Khi đẩy lên Prometheus:
- 10,000,000 time series mới được tạo trong RAM của Prometheus.
- Mỗi time series tốn bộ nhớ cho chunk và index băm.
- Hậu quả: Prometheus sập vì OutOfMemory (OOMKilled), Grafana không query được biểu đồ nào.
WARNING
Đừng đưa các trường ngẫu nhiên hoặc có số giá trị gần như vô hạn như userId, orderId, email, token, timestamp vào label của Prometheus. Cần điều tra theo user thì đẩy chúng vào Span Attributes của Tempo hoặc nội dung log dạng JSON trong Loki. Lưu ý Loki cũng index theo label y như Prometheus, nên userId phải nằm trong nội dung log (hoặc structured metadata), không được làm label của Loki.
# dashboard as code & gitops provisioning
Với hàng trăm microservices, ngồi click tay tạo dashboard trên web là cách làm rất dễ hỏng việc:
- Ai đó vô tình sửa nhầm là hỏng dashboard production.
- Có lỗi thì không rollback được.
- Dựng cụm Kubernetes mới ở region khác lại phải click lại từ đầu.
# triển khai dashboard as code với file provisioning
Cấu hình tự nạp Dashboard từ Git (/etc/grafana/provisioning/dashboards/provider.yaml):
apiVersion: 1
providers:
- name: 'core-microservices'
orgId: 1
folder: 'Services'
type: file
disableDeletion: true # Ngăn chặn việc ai đó xóa nhầm dashboard từ Web UI
updateIntervalSeconds: 30 # Quét lại thư mục mỗi 30s; file từ Git phải được đồng bộ xuống đây (CI, git-sync, ConfigMap...)
allowUiUpdates: false # Khóa tính năng sửa trên UI: Bắt buộc commit qua Git!
options:
path: /var/lib/grafana/dashboards/json# ma trận so sánh các nền tảng giám sát
| Tiêu chí | Grafana OSS (Self-Hosted) | Datadog (SaaS) | New Relic (SaaS) |
|---|---|---|---|
| Quyền Sở Hữu Dữ Liệu | 100% On-Premise / Private Cloud | Lưu trên hạ tầng của vendor (chọn region) | Lưu trên hạ tầng của vendor (US/EU) |
| Chi Phí Dự Toán | Cố định theo chi phí hạ tầng (VM/K8s) | Rất tốn kém (Tính theo Host + Custom Metrics) | Tính theo GB dữ liệu nạp vào và số user |
| Độ Tùy Biến (Customization) | Không giới hạn (Mọi Data Source, Custom Plugins) | Bị giới hạn trong hệ sinh thái Datadog | Bị giới hạn |
| Khả Năng Tự Động Hóa | Quản lý 100% qua GitOps / Jsonnet / Terraform | Hỗ trợ Terraform | Hỗ trợ Terraform |
# tổng kết
Grafana không chỉ để vẽ biểu đồ, nó đi cùng cách làm việc của SRE hiện đại:
- LGTM: nối Metrics, Logs, Traces và Profiles thành một luồng điều tra liền mạch.
- Chuẩn giám sát: RED Method cho API, USE Method cho hạ tầng, cảnh báo theo Error Budget Burn Rate (SLO).
- Kỷ luật dữ liệu: tránh bẫy High Cardinality và coi Dashboard như code, quản lý bằng GitOps.
Chỉ là những ghi chép cá nhân với hy vọng mang lại chút giá trị. Nếu thấy hữu ích, đừng ngại chia sẻ cho bạn bè & đồng nghiệp nhé!
Happy coding 😎 👍🏻 🚀 🔥.