Mở đầu — vì sao bài này quan trọng
Bạn vừa chạy một bài load test kéo dài 30 phút với 2.000 virtual user. Test kết thúc, terminal in ra một cục summary: http_req_duration avg=340ms p95=1.2s, http_req_failed 2.13%. Bạn nhìn con số đó và... không biết chuyện gì đã xảy ra ở phút thứ 18 khi error rate bắt đầu leo thang. Con số tổng hợp cuối cùng che giấu toàn bộ câu chuyện.
Đây chính là lý do một dashboard performance tồn tại. Grafana không làm test của bạn nhanh hơn, cũng không tìm bug giúp bạn. Nhưng nó biến một chuỗi số liệu vô hồn thành một màn hình thời gian thực nơi bạn thấy latency dâng lên theo từng giây, thấy đúng khoảnh khắc hệ thống bắt đầu "gãy", và thấy nó tương quan với số user đang tăng như thế nào. Trong một buổi test có sếp và cả team dev ngồi cạnh, sự khác biệt giữa "để tôi export CSV rồi vẽ Excel sau" và "mọi người nhìn màn hình này, ngay bây giờ" là sự khác biệt giữa một kỹ sư performance nghiệp dư và một chuyên gia.
Bài này tập trung hoàn toàn vào việc dựng và cấu hình một Grafana dashboard phục vụ performance testing: hiểu kiến trúc stack, hiểu schema dữ liệu k6/JMeter ghi vào InfluxDB, import và tự tay xây panel, đọc dashboard đúng cách. Đây là bài "setup" thực chiến — không phải bài lý thuyết về đọc report (đó là việc của bài khác trong khóa).
Khái niệm cốt lõi
Stack observability cho load test
Toàn bộ pipeline gồm bốn mắt xích, dữ liệu chảy một chiều:
k6 / JMeter → InfluxDB → Grafana → kỹ sư
(sinh metric) (lưu trữ) (trực quan) (đọc & quyết định)
- k6 / JMeter là nguồn phát metric. Trong lúc chạy, chúng liên tục "bắn" các điểm dữ liệu (mỗi request tạo ra một hoặc nhiều điểm) ra ngoài qua giao thức của InfluxDB.
- InfluxDB là một time-series database — cơ sở dữ liệu chuyên lưu dữ liệu gắn với mốc thời gian. Nó nhận hàng chục nghìn điểm mỗi giây và cho phép truy vấn kiểu "cho tôi p95 của latency mỗi 5 giây trong 30 phút qua".
- Grafana là lớp trực quan hóa. Nó không lưu dữ liệu, chỉ query InfluxDB rồi vẽ đồ thị. Grafana kết nối tới InfluxDB thông qua một cấu hình gọi là data source.
- Kỹ sư ngồi trước dashboard, đọc xu hướng và ra quyết định.
InfluxDB schema mà k6 ghi ra
Muốn xây panel, bạn phải biết dữ liệu nằm ở đâu. k6 (với output InfluxDB v1) ghi ra các measurement — tương đương "bảng" trong SQL. Mỗi measurement ứng với một built-in metric của k6:
http_req_duration— tổng thời gian một request HTTP (đây là latency bạn quan tâm nhất).http_req_failed— tỉ lệ request lỗi (rate metric, giá trị 0 hoặc 1).http_reqs— bộ đếm số request (dùng để tính throughput / RPS).vus— số virtual user đang hoạt động tại mỗi thời điểm.http_req_waiting— thời gian chờ server phản hồi (TTFB), tách riêng khỏi thời gian truyền dữ liệu.iteration_duration,data_sent,data_received... và mọi custom metric bạn tự định nghĩa.
status (mã HTTP), method, name (tên request), url, scenario, và mọi tag bạn gắn thủ công. Nhờ tag bạn mới vẽ được đồ thị kiểu "p95 latency của riêng endpoint /checkout".JMeter, khi dùng Backend Listener kiểu InfluxDB, ghi ra một measurement chính là jmeter với các field như avg, pct90.0, pct95.0, countError, hit, và tag transaction (tên sampler). Schema khác k6 nên dashboard k6 và dashboard JMeter không dùng chung — đây là một nhầm lẫn kinh điển của người mới.
Dashboard là tập hợp các panel + biến
Một Grafana dashboard gồm nhiều panel (mỗi panel là một đồ thị/con số), mỗi panel chứa một hoặc nhiều query tới data source. Dashboard thường có thêm template variable — biến động như $testid cho phép bạn lọc toàn bộ dashboard theo một lần chạy test cụ thể, hay $environment để chuyển giữa staging và production.
Tình huống thực tế
Ví dụ 1 — Tiki: dashboard "cứu" buổi demo capacity trước ban lãnh đạo
Đội performance của một sàn thương mại điện tử lớn (gọi là "sàn T") chuẩn bị cho mùa sale, cần chứng minh hệ thống chịu được 5.000 concurrent user. Buổi test được lên lịch có cả CTO và trưởng phòng hạ tầng theo dõi.
Lần diễn tập đầu, kỹ sư chỉ chạy k6 và đọc summary cuối. Khi CTO hỏi "hệ thống bắt đầu chậm từ mức bao nhiêu user?", cả team im lặng vì summary chỉ có một con số p95 trung bình cho cả 30 phút. Họ nhận ra mình mù thời gian thực.
Trước buổi chính thức, họ dựng một Grafana dashboard: một panel vus (số user) chồng lên panel http_req_duration p95, cộng thêm panel http_req_failed. Trong buổi test thật, cả phòng nhìn thấy rõ: tới mốc ~3.800 VUs thì đường p95 bắt đầu cong lên từ 400ms tới 1,8s, và đúng lúc đó error rate nhích lên 1%. Kỹ sư chỉ tay vào màn hình: "Điểm gãy của phiên bản hiện tại là khoảng 3.800 user, ta cần thêm 2 node app trước sale." Quyết định hạ tầng được chốt ngay trong buổi họp.
Bài học: Giá trị lớn nhất của dashboard không phải là đẹp, mà là tương quan theo thời gian — chồng đường VUs lên đường latency cho phép trả lời câu "gãy ở mức nào" mà summary không bao giờ trả lời được.
Ví dụ 2 — Fintech Singapore: một data source cấu hình sai làm mất cả buổi test
Một công ty fintech ở Singapore dùng JMeter test cổng thanh toán. Họ copy nguyên một dashboard k6 nổi tiếng từ Grafana marketplace (dashboard ID 2587) rồi trỏ vào InfluxDB của mình. Kết quả: dashboard trắng trơn dù test đang chạy ầm ầm.
Nguyên nhân: dashboard đó query các measurement http_req_duration, vus... của k6, trong khi JMeter Backend Listener của họ ghi vào measurement jmeter với field pct95.0. Schema không khớp thì query không ra dòng nào. Họ mất gần một giờ tưởng InfluxDB hỏng, chạy đi kiểm tra network, firewall...
Cách họ phát hiện: dùng InfluxDB CLI gõ SHOW MEASUREMENTS và thấy chỉ có measurement jmeter, không có http_req_duration. Từ đó họ đổi sang import dashboard JMeter chuẩn (ID 5496) và mọi thứ hiện lên.
Bài học: Luôn kiểm chứng dữ liệu đã vào tới InfluxDB trước khi nghi ngờ Grafana, và luôn dùng đúng dashboard cho đúng công cụ. Dashboard k6 ≠ dashboard JMeter.
Ví dụ 3 — Startup giao đồ ăn: dùng biến $testid để so sánh trước/sau tối ưu
Một startup giao đồ ăn ở TP.HCM tối ưu database cho API /restaurants/nearby. Họ cần bằng chứng "trước và sau". Thay vì dựng hai dashboard, họ thêm một template variable $testid vào k6 bằng tag: --tag testid=before-index cho lần chạy đầu và testid=after-index cho lần sau.
Trong Grafana, mọi panel đều có WHERE testid =~ /$testid/. Nhờ đó ở góc trên dashboard xuất hiện một dropdown, chọn before-index thấy p95 = 2,1s, chọn after-index thấy p95 = 380ms — cùng một dashboard, cùng một layout, chỉ đổi dropdown. Slide báo cáo cho sếp chỉ cần hai ảnh chụp màn hình.
Bài học: Template variable biến dashboard tĩnh thành công cụ so sánh linh hoạt; gắn testid cho mỗi lần chạy là thói quen nên có ngay từ đầu.
Hướng dẫn từng bước
Dưới đây là quy trình dựng dashboard từ số 0 bằng Docker, cách nhanh và sạch nhất để thực hành.
Bước 1 — Dựng InfluxDB + Grafana bằng Docker. Tạo file docker-compose.yml:
version: "3"
services:
influxdb:
image: influxdb:1.8
ports: ["8086:8086"]
environment:
- INFLUXDB_DB=k6
grafana:
image: grafana/grafana:latest
ports: ["3000:3000"]
environment:
- GF_AUTH_ANONYMOUS_ENABLED=true
- GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
Chạy docker compose up -d. Lưu ý dùng InfluxDB 1.8 vì output InfluxDB của k6 (bản built-in) nói giao thức v1; InfluxDB 2.x có API khác, dễ gây lỗi cho người mới.
Bước 2 — Đẩy dữ liệu k6 vào InfluxDB. Chạy test với output trỏ về DB vừa tạo:
k6 run --out influxdb=http://localhost:8086/k6 script.js
Chuỗi k6 cuối URL chính là tên database. k6 sẽ tự tạo các measurement khi có dữ liệu.
Bước 3 — Xác minh dữ liệu đã vào (đừng bỏ qua bước này). Vào container InfluxDB và kiểm tra:
docker exec -it <influx_container> influx -database k6 -execute "SHOW MEASUREMENTS"
Bạn phải thấy http_req_duration, vus, http_reqs... Nếu trống, vấn đề nằm ở k6/network, chưa cần đụng tới Grafana.
Bước 4 — Thêm data source trong Grafana. Mở http://localhost:3000 → Connections → Data sources → Add → InfluxDB. Điền: Query language = InfluxQL, URL = http://influxdb:8086 (dùng tên service trong Docker network), Database = k6. Bấm Save & Test, phải thấy thông báo xanh.
Bước 5 — Import dashboard có sẵn để chạy nhanh. Vào Dashboards → New → Import, nhập ID 2587 (dashboard k6 phổ biến), chọn data source vừa tạo. Trong vài giây bạn có ngay dashboard đầy đủ VUs, request rate, response time percentiles.
Bước 6 — Tự xây một panel để hiểu bản chất. Import xong hãy tự tay tạo thêm một panel p95 để nắm cơ chế. New panel → chọn data source InfluxDB → chuyển sang chế độ query. Với InfluxQL, panel latency p95 điển hình:
SELECT percentile("value", 95) FROM "http_req_duration"
WHERE $timeFilter GROUP BY time($__interval) fill(null)
Trong đó $timeFilter và $__interval là biến Grafana tự thay theo khoảng thời gian bạn đang xem. Đặt đơn vị (unit) của panel là milliseconds để trục Y hiển thị đẹp.
Bước 7 — Thêm panel throughput và error rate. Throughput (RPS) từ http_reqs:
SELECT sum("value") FROM "http_reqs"
WHERE $timeFilter GROUP BY time(1s) fill(0)
Error rate từ http_req_failed — tính tỉ lệ điểm có value = 1.
Bước 8 — Thêm template variable $testid. Dashboard settings → Variables → New → kiểu Query, gõ SHOW TAG VALUES FROM "http_req_duration" WITH KEY = "testid". Sau đó sửa các query panel thêm AND "testid" =~ /^$testid$/. Nhớ chạy k6 kèm --tag testid=lan-01 để tag tồn tại.
Bước 9 — Sắp xếp bố cục theo thứ tự đọc. Đặt VUs và throughput ở hàng trên (bối cảnh tải), latency percentiles ở giữa (triệu chứng), error rate và status codes ở dưới (hậu quả). Bố cục này khớp với cách mắt người đọc từ nguyên nhân tới hậu quả.
Bước 10 — Lưu và bật auto-refresh. Save dashboard, đặt refresh 5s và time range Last 30 minutes để theo dõi live trong lúc test chạy.
Lỗi thường gặp & mẹo
- Dashboard trống nhưng test đang chạy. Theo thứ tự: (1)
SHOW MEASUREMENTSxem dữ liệu có vào InfluxDB không; (2) nếu có, kiểm tra data source trỏ đúng database chưa; (3) kiểm tra time range của dashboard có phủ khoảng test không (rất hay quên — dashboard đang xem "last 5 minutes" của tuần trước).
- Dùng nhầm dashboard k6 cho JMeter (và ngược lại). Schema khác nhau hoàn toàn. Nhớ: k6 → measurement
http_req_duration; JMeter → measurementjmeter, fieldpct95.0.
- Trỏ URL InfluxDB sai trong Docker. Bên trong Docker network phải dùng tên service (
http://influxdb:8086), không phảilocalhost.localhostchỉ đúng khi Grafana chạy ngoài Docker.
- Trục Y latency hiện số lạ (như 340000). Bạn quên set unit. Nếu k6 ghi latency bằng millisecond mà bạn để unit là "none" hoặc "seconds", số sẽ vô nghĩa. Đặt đúng unit
ms.
- Cardinality bùng nổ. Nếu bạn gắn URL đầy đủ (kèm ID động như
/user/12345) làm tag, InfluxDB sinh hàng triệu series, DB phình và query chậm. Mẹo: dùng URL grouping của k6 để/user/${id}thành mộtnamechung.
- Anonymous admin trên production. Cấu hình
GF_AUTH_ANONYMOUSở trên chỉ dành cho lab. Lên môi trường thật phải tắt và bật xác thực đàng hoàng.
- Mẹo vàng: đừng dùng
avglatency làm chỉ số chính. Luôn ưu tiên p95/p99 — trung bình che giấu đuôi chậm. Một panel tốt nên vẽ đồng thời p50, p95, p99 để thấy độ phân tán.
- Mẹo lưu trữ: annotation trong Grafana cho phép đánh dấu mốc "bắt đầu ramp-up", "spike bắt đầu" ngay trên đồ thị, rất hữu ích khi báo cáo lại sau này.
Bài tập thực hành
- Dựng stack. Dùng
docker-compose.ymlở trên, khởi động InfluxDB 1.8 + Grafana. Chạy một script k6 đơn giản (20 VUs, 2 phút, gọi một endpoint bất kỳ nhưhttps://test.k6.io) với--out influxdb=http://localhost:8086/k6.
- Xác minh schema. Vào InfluxDB CLI chạy
SHOW MEASUREMENTSvàSHOW TAG KEYS FROM "http_req_duration". Ghi lại các measurement và tag bạn thấy.
- Import + tự xây. Import dashboard ID 2587, sau đó tự tạo thêm một panel p95
http_req_durationbằng query InfluxQL, đặt đúng unitms.
- Thêm biến so sánh. Chạy test hai lần với
--tag testid=run-avà--tag testid=run-b(đổi số VUs giữa hai lần). Thêm template variable$testidvà chứng minh dashboard đổi số liệu khi bạn đổi dropdown.
- Chẩn đoán chủ động. Cố tình cấu hình sai data source (đổi database thành tên không tồn tại), quan sát dashboard trống, rồi tự đi qua checklist 3 bước ở mục "Lỗi thường gặp" để tìm ra lỗi. Mục tiêu là luyện phản xạ debug, không chỉ làm đúng ngay từ đầu.
Tóm tắt
- Stack quan sát performance là chuỗi bốn mắt xích: k6/JMeter → InfluxDB → Grafana → kỹ sư. Grafana chỉ trực quan hóa, mọi dữ liệu đến từ InfluxDB.
- Muốn xây panel phải biết schema: k6 ghi các measurement như
http_req_duration,http_reqs,vuskèm tagstatus,name,scenario; JMeter ghi measurementjmetervới fieldpct95.0. Dùng đúng schema cho đúng công cụ. - Quy trình dựng: Docker InfluxDB 1.8 + Grafana → chạy k6 với
--out influxdb→ xác minh dữ liệu vào DB → thêm data source InfluxQL → import dashboard 2587 → tự xây thêm panel p95/throughput/error → thêm$testid→ sắp bố cục theo mạch nguyên nhân–hậu quả. - Giá trị thật của dashboard nằm ở tương quan thời gian thực: chồng VUs lên latency để trả lời "gãy ở mức bao nhiêu user", điều summary cuối không làm được.
- Khi dashboard trống, debug theo thứ tự DB → data source → time range, và luôn dùng p95/p99 thay vì trung bình.