Observability 5 — Grafana: dashboard & trực quan
Grafana là gì và vị trí trong stack
Trong các bài trước ta đã dựng Prometheus scrape metric (Kiến trúc Prometheus), viết truy vấn (PromQL) và gắn instrumentation vào ứng dụng (Instrumentation). Nhưng Prometheus tự nó chỉ có một giao diện đồ thị thô sơ dùng để debug query, không phải nơi để đội vận hành nhìn hệ thống suốt ngày. Grafana lấp đúng khoảng trống đó: nó là nền tảng trực quan hoá và dashboard đa nguồn (multi-source visualization) — đọc dữ liệu từ nhiều hệ thống, vẽ đồ thị, và cho phép ghép nhiều panel thành một trang giám sát.
Điểm cốt lõi cần nhớ ngay: Grafana không lưu trữ metric. Nó không phải một time-series database. Grafana chỉ là lớp query + render. Mỗi khi bạn mở dashboard, Grafana gửi truy vấn xuống data source (Prometheus, Loki, Postgres...) theo thời gian thực, nhận kết quả, rồi vẽ. Điều này có hệ quả thực tế: dashboard nặng = query nặng = tải lên Prometheus, chứ không phải tải lên Grafana. Khi dashboard chậm, thủ phạm gần như luôn là truy vấn tốn kém chứ không phải bản thân Grafana.
So sánh nhanh vai trò trong stack observability:
| Thành phần | Vai trò | Lưu dữ liệu? |
|---|---|---|
| Exporter / SDK | Sinh metric | Không |
| Prometheus | Scrape + lưu TSDB + đánh giá alert rule | Có (metric) |
| Loki | Lưu log | Có (log) |
| Grafana | Truy vấn + trực quan + dashboard + (tùy chọn) alerting | Không |
Data source: một Grafana, nhiều nguồn
Sức mạnh lớn nhất của Grafana là data source plugin: cùng một dashboard có thể trộn dữ liệu từ nhiều hệ thống khác nhau. Trong ngữ cảnh ngân hàng NCB, một trang giám sát cổng thanh toán điển hình có thể cần:
- Prometheus — metric hệ thống & ứng dụng: QPS, latency p99, tỷ lệ lỗi HTTP, CPU/RAM.
- Loki — log tập trung: xem log lỗi đúng khoảng thời gian latency tăng vọt.
- PostgreSQL — dữ liệu nghiệp vụ: số giao dịch thành công/thất bại theo phút lấy trực tiếp từ bảng nghiệp vụ.
- Elasticsearch — nếu log/audit trail được index ở đó (Elasticsearch).
Mỗi data source được cấu hình một lần (URL, xác thực, timeout) rồi tái sử dụng ở mọi panel. Mỗi loại data source có ngôn ngữ truy vấn riêng: Prometheus dùng PromQL, Loki dùng LogQL, Postgres dùng SQL, Elasticsearch dùng Lucene/DSL. Grafana không "dịch" giữa chúng — bạn viết đúng thứ tiếng của từng nguồn trong từng panel.
Một tính năng quan trọng là mixed data source: một panel có thể query nhiều nguồn cùng lúc (query A từ Prometheus, query B từ Postgres) rồi ghép lại bằng transformation. Rất hữu ích khi muốn phủ metric kỹ thuật (latency) lên số liệu nghiệp vụ (số giao dịch) trên cùng một trục thời gian.
Panel & visualization: chọn đúng loại
Đơn vị nhỏ nhất của dashboard là panel — một khung hiển thị kết quả của một hoặc nhiều truy vấn. Chọn sai loại visualization là lỗi phổ biến khiến dashboard "đẹp mà vô dụng". Dưới đây là các loại chính và khi nào dùng.
| Loại | Bản chất | Dùng khi | Ví dụ ngân hàng |
|---|---|---|---|
| Time series | Đồ thị đường theo thời gian | Xu hướng theo thời gian, so sánh nhiều series | QPS, latency p99, tỷ lệ lỗi theo phút |
| Stat | Một con số lớn + xu hướng nhỏ | Chỉ số tức thời quan trọng | Uptime %, số giao dịch/giây hiện tại |
| Gauge | Đồng hồ đo ngưỡng | Giá trị nằm trong dải min–max có ngưỡng | Mức sử dụng connection pool DB (0–100%) |
| Bar gauge | Thanh ngang so sánh nhiều mục | Xếp hạng, so sánh giá trị rời rạc | Latency theo endpoint, lỗi theo service |
| Table | Bảng dữ liệu | Cần đọc giá trị chính xác nhiều cột | Top 10 endpoint chậm nhất kèm p99, QPS |
| Heatmap | Mật độ theo thời gian × bucket | Phân bố latency thay đổi theo thời gian | Phân bố histogram latency thanh toán |
| State timeline | Dải trạng thái theo thời gian | Trạng thái rời rạc up/down, healthy/degraded | Trạng thái các service theo giờ |
Nguyên tắc chọn nhanh:
- Xu hướng theo thời gian → time series. Đây là loại dùng 70% thời gian.
- Một con số "sức khỏe" cần liếc là biết → stat (kèm màu ngưỡng).
- Giá trị trong dải có ngưỡng cứng (ví dụ pool tối đa 100 connection) → gauge.
- Phân bố (latency không chỉ có p99 mà cả hình dạng) → heatmap. Đừng dùng heatmap chỉ để trông "ngầu".
- Cần đọc số chính xác + nhiều chiều → table với transformation.
- Bật/tắt, khỏe/suy → state timeline, tốt hơn nhiều so với đường zigzag 0/1.
Xây panel từ truy vấn PromQL
Mỗi panel Prometheus chứa một hoặc nhiều query viết bằng PromQL. Grafana thêm vài biến ma thuật để đồng bộ query với khung hiển thị:
$__rate_interval— khoảng thời gian rate() được Grafana tính tự động dựa trên scrape interval và độ rộng bước hiển thị. Luôn ưu tiên dùng nó thay vì hardcode[5m]để tránh sai lệch khi zoom.$__interval— bước gom nhóm khớp với độ phân giải màn hình.- Time range của dashboard được tự động áp vào mọi query, bạn không viết
[range]cố định cho panel.
Ví dụ panel tỷ lệ lỗi HTTP (golden signal "errors") cho core-banking, dùng cho time series:
sum(rate(http_requests_total{service="core-banking", status=~"5.."}[$__rate_interval]))
/
sum(rate(http_requests_total{service="core-banking"}[$__rate_interval]))
Panel latency p99 (golden signal "latency"), tận dụng histogram như đã học ở bài PromQL:
histogram_quantile(
0.99,
sum by (le) (rate(http_request_duration_seconds_bucket{service="core-banking"}[$__rate_interval]))
)
Panel throughput (golden signal "traffic"), tách theo endpoint để bar gauge/table xếp hạng:
sum by (endpoint) (rate(http_requests_total{service="core-banking"}[$__rate_interval]))
Với các panel dùng legend nhiều series, đặt Legend là {{endpoint}} hoặc {{instance}} để nhãn đọc được thay vì cả cụm label dài.
Template variables: dashboard động
Template variable (biến mẫu) là thứ biến một dashboard tĩnh thành công cụ tái sử dụng. Thay vì tạo 20 dashboard cho 20 service, bạn tạo một dashboard với biến $service và chọn service từ dropdown ở đầu trang.
Các loại biến hay dùng:
- Query variable — giá trị lấy động từ chính data source. Ví dụ liệt kê mọi
job:hoặc liệt kê instance theo job đã chọn (biến phụ thuộc biến):label_values(up, job)label_values(up{job="$job"}, instance) - Custom variable — danh sách cố định do bạn gõ (ví dụ
prod,staging). - Interval variable — cho phép người dùng chọn độ mịn rate.
- Datasource variable — chuyển toàn dashboard sang một Prometheus khác (ví dụ giữa cụm DC1 và DC2).
Trong panel, biến được nội suy: $job, $instance. Với multi-value hoặc dùng trong matcher, dùng cú pháp regex-safe =~"$instance":
sum by (instance) (rate(http_requests_total{job="$job", instance=~"$instance"}[$__rate_interval]))
Một kỹ thuật mạnh là repeat panel/row theo biến: bật "Repeat" cho một panel theo $instance, Grafana sẽ tự nhân bản panel đó cho mỗi giá trị instance đang chọn. Một dòng cấu hình, một dashboard tự giãn nở khi cụm mở rộng — cực kỳ hữu ích khi số node core-banking tăng từ 3 lên 12 mà không cần sửa dashboard.
Annotations: đánh dấu sự kiện
Annotation là các vạch/dải dọc trên đồ thị đánh dấu một sự kiện tại thời điểm cụ thể. Đây là công cụ điều tra sự cố hiệu quả bậc nhất: khi latency tăng vọt lúc 14:03, một annotation "Deploy v2.4.1 lúc 14:02" trả lời ngay câu hỏi "chuyện gì xảy ra".
Nguồn annotation phổ biến:
- Manual — người vận hành thêm tay khi có sự kiện.
- Query-based — sinh tự động từ data source. Ví dụ đánh dấu mỗi lần triển khai bằng metric build info, hoặc mỗi lần alert firing.
- Từ CI/CD — pipeline gọi Grafana HTTP API để tạo annotation "deploy" mỗi khi release lên production.
Chồng annotation deploy lên dashboard golden signals cho phép kiểm chứng ngay tác động của mỗi lần release — nếu tỷ lệ lỗi nhảy lên đúng vạch deploy, bạn biết cần rollback.
Transformations, thresholds & màu
Transformation biến đổi dữ liệu sau khi query trả về, trước khi vẽ — chạy trong Grafana chứ không phải data source. Các transformation hay dùng:
- Reduce — thu mỗi series về một giá trị (last, max, mean) để đổ vào stat/table.
- Organize fields — đổi tên, ẩn, sắp lại cột trong table.
- Join by field — ghép kết quả nhiều query theo một cột chung (ví dụ ghép latency và QPS theo endpoint).
- Filter data by values — giữ lại hàng thỏa điều kiện.
Threshold định nghĩa ngưỡng để tô màu theo mức độ nghiêm trọng: xanh khi bình thường, vàng khi cảnh báo, đỏ khi nguy hiểm. Ví dụ tỷ lệ lỗi HTTP: < 1% xanh, 1–5% vàng, > 5% đỏ. Threshold không chỉ tô màu chữ số mà còn có thể tô nền panel, vẽ đường ngưỡng trên time series, đổi màu cung gauge.
Nguyên tắc màu: màu phải mang ngữ nghĩa nhất quán. Đỏ luôn là xấu, xanh luôn là tốt trên toàn bộ dashboard. Đừng dùng đỏ cho một series chỉ vì nó là màu mặc định thứ ba — người trực đêm sẽ hiểu nhầm.
Provisioning dashboard as-code
Dashboard bấm chuột tạo ra thì tiện, nhưng không tái lập được và không versioning. Trong môi trường ngân hàng có kiểm soát thay đổi, dashboard phải được quản lý as-code: định nghĩa bằng JSON, lưu trong Git, triển khai tự động.
Grafana lưu mỗi dashboard dưới dạng model JSON. Bạn có thể export JSON của một dashboard đã dựng bằng UI, đưa vào Git, rồi provision lại bằng file cấu hình. Cấu hình provisioning (đặt trong /etc/grafana/provisioning/dashboards/) trỏ Grafana tới thư mục chứa các file JSON:
# provisioning/dashboards/banking.yaml — minh hoạ (KHÔNG phải SQL)
apiVersion: 1
providers:
- name: 'banking-dashboards'
orgId: 1
folder: 'Core Banking'
type: file
disableDeletion: true
updateIntervalSeconds: 30
options:
path: /var/lib/grafana/dashboards
Data source cũng provision được tương tự, tránh phải cấu hình URL/credential bằng tay:
# provisioning/datasources/prometheus.yaml — minh hoạ
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
access: proxy
url: http://prometheus:9090
isDefault: true
Cấu trúc rút gọn của một dashboard JSON (minh hoạ, không chạy được, đã lược bỏ nhiều trường):
{
"title": "Core Banking — Golden Signals",
"templating": {
"list": [
{ "name": "job", "type": "query", "query": "label_values(up, job)" }
]
},
"panels": [
{
"type": "timeseries",
"title": "Tỷ lệ lỗi HTTP (5xx)",
"targets": [
{ "expr": "sum(rate(http_requests_total{status=~\"5..\"}[$__rate_interval])) / sum(rate(http_requests_total[$__rate_interval]))" }
],
"fieldConfig": {
"defaults": {
"unit": "percentunit",
"thresholds": { "steps": [
{ "color": "green", "value": null },
{ "color": "red", "value": 0.05 }
]}
}
}
}
]
}
Lợi ích của as-code: mỗi thay đổi dashboard đi qua pull request (review được), khôi phục được về bất kỳ phiên bản nào, và tái lập giống hệt trên staging/production. Đây cùng triết lý với Infrastructure as Code ở CI/CD và Kubernetes (Kubernetes production ops).
Tổ chức dashboard theo tầng
Một sai lầm kinh điển là nhồi 40 panel vào một dashboard "tất cả trong một". Kết quả là không ai nhìn được gì. Cách làm đúng là phân tầng theo mức độ chi tiết:
- Tầng overview — một dashboard duy nhất, mỗi service là một ô stat màu. Câu hỏi trả lời: "Có gì đang cháy không?". Người trực liếc 5 giây là biết.
- Tầng service — mỗi service một dashboard, tập trung golden signals / RED. Câu hỏi: "Service X có vấn đề ở tín hiệu nào?".
- Tầng chi tiết — đào sâu per-endpoint, per-instance, tài nguyên, log. Câu hỏi: "Vì sao service X hỏng?".
Liên kết giữa các tầng bằng data links / dashboard links: click một service ở overview nhảy thẳng sang dashboard service đó với biến $service đã set sẵn và giữ nguyên time range.
Nguyên tắc thiết kế panel:
- Đừng nhồi — mỗi dashboard trả lời một câu hỏi. Nếu phải cuộn nhiều màn hình, hãy tách.
- Tín hiệu quan trọng lên đầu — mắt đọc từ trên xuống. Errors/latency ở hàng đầu, chi tiết phụ ở dưới.
- Đơn vị đúng — đặt unit chuẩn (seconds, percent, ops/s, bytes). Panel latency ghi "0.087" mà không có unit là vô nghĩa; đặt unit
sđể hiển thị "87 ms". - Nhất quán time range & màu trên toàn dashboard.
- Đặt tên có nghĩa cho panel và biến — người trực đêm không phải đoán.
Golden signals / RED trên Grafana
Hai khung tiêu chuẩn để thiết kế nội dung dashboard:
- Golden Signals (Google SRE): Latency, Traffic, Errors, Saturation.
- RED (dành cho request-driven service): Rate (số request/giây), Errors (tỷ lệ lỗi), Duration (phân bố latency).
Một dashboard service ngân hàng chuẩn nên có tối thiểu: một panel time series Rate, một panel Errors (kèm threshold đỏ), một panel Duration (p50/p95/p99 hoặc heatmap phân bố), và một panel Saturation (CPU/RAM/connection pool). Bốn panel này trả lời 90% câu hỏi vận hành thường ngày.
Alerting: nhắc ngắn
Grafana có hệ alerting riêng (Grafana Managed Alerts) cho phép định nghĩa rule ngay trên panel/query, gửi qua nhiều kênh (email, Slack, Telegram, PagerDuty). Nó tách biệt với alerting của Prometheus (Alertmanager). Việc chọn dùng cái nào, cách thiết kế rule để tránh alert fatigue, cách định tuyến và im lặng (silence) sẽ được đi sâu ở bài 6 — Alerting. Nguyên tắc quan trọng nhất cần nhớ trước: dashboard để con người nhìn, alert để máy đánh thức con người — đừng nhầm hai vai trò.
Use case thực tế
Bối cảnh: Cổng thanh toán NCB xử lý ~1.500 giao dịch/phút giờ cao điểm. Đội SRE cần một dashboard giám sát để phát hiện sự cố trong dưới 1 phút và điều tra nhanh.
Thiết kế (phân tầng + RED + template variable):
- Biến
$env(custom:prod,staging) và$instance(query:label_values(up{job="payment-gateway", env="$env"}, instance)) đặt ở đầu dashboard. - Hàng 1 — Rate & Errors (time series):
- Rate:
sum(rate(http_requests_total{job="payment-gateway", env="$env"}[$__rate_interval])). - Error ratio với threshold
> 5%đỏ,> 1%vàng.
- Rate:
- Hàng 2 — Duration: panel p99
histogram_quantile(0.99, sum by (le) (rate(http_request_duration_seconds_bucket{job="payment-gateway"}[$__rate_interval])))+ một heatmap phân bố latency. - Hàng 3 — Saturation: connection pool DB dưới dạng gauge (ngưỡng 80% vàng, 95% đỏ), CPU/RAM per instance (repeat panel theo
$instance). - Annotation deploy: pipeline CI/CD gọi Grafana API tạo annotation mỗi lần release.
- Panel bổ trợ từ Postgres (mixed source): đối chiếu số giao dịch nghiệp vụ theo phút với QPS kỹ thuật — nếu QPS ổn mà số giao dịch thành công tụt, vấn đề nằm ở tầng nghiệp vụ chứ không phải hạ tầng. Truy vấn đối chiếu nhanh trên sandbox chỉ đọc:
-- ▶ Chạy được
SELECT date_trunc('minute', created_at) AS minute, kind, COUNT(*) AS n
FROM transactions
GROUP BY 1, 2
ORDER BY 1 DESC;
Vận hành: dashboard được export JSON, đưa vào Git, provision tự động lên cả staging và prod. Một sự cố thật: 14:03 error ratio nhảy lên 8% (panel đỏ), annotation cho thấy deploy v2.4.1 lúc 14:02 → rollback trong 3 phút, error ratio về xanh. Không có annotation, việc truy ra nguyên nhân sẽ mất hàng chục phút mò log.
Ghi nhớ
- Grafana không lưu metric — nó là lớp query + render đọc thời gian thực từ data source. Dashboard chậm = query nặng, tải lên Prometheus.
- Một Grafana đọc nhiều data source (Prometheus, Loki, Postgres, Elasticsearch), mỗi nguồn dùng đúng ngôn ngữ query riêng; mixed source ghép nhiều nguồn trong một panel.
- Chọn đúng panel: time series cho xu hướng, stat cho chỉ số tức thời, gauge cho dải có ngưỡng, table cho số chính xác nhiều cột, heatmap cho phân bố, state timeline cho trạng thái rời rạc.
- Dùng
$__rate_intervalthay vì hardcode[5m]trong PromQL của panel để tránh sai khi zoom. - Template variable (
$job,$instance) + repeat panel biến một dashboard tĩnh thành công cụ động, tự giãn nở theo cụm. - Annotation đánh dấu deploy/sự kiện là công cụ điều tra sự cố mạnh nhất — chồng lên golden signals để thấy tác động release ngay.
- Threshold + màu nhất quán (đỏ = xấu, xanh = tốt) và đơn vị đúng là ranh giới giữa dashboard dùng được và dashboard gây hiểu nhầm.
- Provision as-code (JSON trong Git): versioning, review qua PR, tái lập giống hệt trên các môi trường.
- Tổ chức phân tầng overview → service → chi tiết, mỗi dashboard trả lời một câu hỏi, tín hiệu quan trọng lên đầu.
- Thiết kế nội dung theo golden signals (Latency, Traffic, Errors, Saturation) hoặc RED (Rate, Errors, Duration).
- Dashboard để nhìn, alert để đánh thức — chi tiết alerting ở bài 6.
Bài viết liên quan
Continuous Integration/Delivery/Deployment, cấu trúc pipeline, ví dụ GitHub Actions và chiến lược release.
Container vs máy ảo, image/layer, Dockerfile, volume, network, Docker Compose và best practices.
Vì sao cần orchestration; Pod, Deployment, Service, Ingress; scaling, self-healing và cấu hình.
Bản chất DevOps: phá bỏ rào cản Dev–Ops, vòng lặp vô tận, CALMS, và vì sao tự động hoá.