Mở đầu — vì sao bài này quan trọng
Hãy tưởng tượng bạn vừa chạy một bài load test cho hệ thống thương mại điện tử. Kết quả trả về: throughput đạt 1.200 request/giây, error rate 4%, và p95 latency là 2,3 giây — vượt xa ngưỡng SLA 800ms. Sếp hỏi bạn một câu đơn giản: "Tại sao chậm? Nghẽn ở đâu?". Và bạn chết lặng. Bạn chỉ có những con số ở phía client — tức là những gì trình tạo tải (JMeter hoặc k6) đo được từ bên ngoài. Bạn hoàn toàn mù mờ về chuyện gì diễn ra bên trong hệ thống: request đi qua bao nhiêu service, service nào ngốn thời gian, có query database nào chạy 1,8 giây hay không, garbage collection có đang "đóng băng" JVM hay không.
Đây chính là ranh giới giữa load testing và observability. Load test cho bạn dữ liệu về hành vi hệ thống dưới tải — bao nhiêu nhanh, bao nhiêu lỗi. Nhưng nó không cho bạn insight về nguyên nhân. Muốn biết "vì sao", bạn cần nhìn vào bên trong hệ thống trong lúc test đang chạy. Đó là vai trò của APM (Application Performance Monitoring) và rộng hơn là observability.
Một performance engineer giỏi không chỉ biết bấm nút chạy test và đọc con số p95. Anh ta chạy load test đồng thời với việc quan sát APM, rồi ghép hai bức tranh lại: "Ở giây thứ 40, throughput từ client tụt xuống, và cùng lúc đó APM cho thấy connection pool tới database cạn kiệt, các request phải xếp hàng chờ 1,5 giây." Đó là cách bạn tìm ra nguyên nhân gốc thay vì đoán mò. Bài này sẽ dạy bạn triết lý và công cụ để làm được điều đó.
Khái niệm cốt lõi
Observability là gì, khác gì với monitoring truyền thống
Monitoring truyền thống trả lời những câu hỏi bạn đã biết trước: "CPU có quá 80% không?", "RAM còn bao nhiêu?". Bạn dựng sẵn dashboard cho những chỉ số bạn nghĩ là quan trọng. Vấn đề là trong hệ thống phức tạp, sự cố thường đến từ những góc bạn không ngờ tới.
Observability (khả năng quan sát) là khả năng suy ra trạng thái bên trong của hệ thống chỉ từ dữ liệu nó phát ra ra ngoài — mà không cần biết trước bạn sẽ hỏi câu gì. Nó cho phép bạn đặt những câu hỏi mới trong lúc điều tra: "Cho tôi xem tất cả request chậm hơn 2 giây, đến từ endpoint /checkout, của user hạng VIP, trong 5 phút vừa qua." Observability thường được xây trên ba trụ cột (three pillars):
- Metrics (số liệu): dữ liệu dạng số theo thời gian — CPU, số request/giây, latency p95, số lỗi. Nhẹ, rẻ để lưu, tốt để cảnh báo và nhìn xu hướng.
- Logs (nhật ký): bản ghi sự kiện dạng văn bản — "user 123 đăng nhập lúc 10:03", "query timeout". Giàu chi tiết nhưng nặng và khó tổng hợp.
- Traces (dấu vết phân tán): theo dấu một request duy nhất khi nó đi xuyên qua nhiều service. Đây là trụ cột quan trọng nhất cho performance testing trong kiến trúc microservices.
Distributed tracing — trái tim của APM cho performance
Trong hệ thống microservices, một request "Đặt hàng" có thể đi qua: API Gateway → Order Service → Inventory Service → Payment Service → Notification Service. Nếu tổng thời gian là 2,3 giây, thời gian đó rơi vào đâu?
Distributed tracing giải quyết chính xác câu hỏi này. Mỗi request được gán một trace ID duy nhất. Mỗi chặng dừng trong hành trình (mỗi lần gọi service, mỗi query database) được ghi lại thành một span — có thời điểm bắt đầu, thời điểm kết thúc, và span cha. Ghép tất cả span của cùng một trace ID lại, bạn có một biểu đồ dạng thác nước (waterfall) cho thấy chính xác: Order Service mất 120ms, nhưng nó chờ Payment Service 1.900ms, và Payment Service chờ một API bên ngoài (cổng thanh toán) 1.850ms. Nguyên nhân gốc lộ ra ngay lập tức.
APM tools — bức tranh công cụ
APM là các nền tảng thương mại/mã nguồn mở gói sẵn cả ba trụ cột observability, thêm khả năng tự động đo (auto-instrumentation) code của bạn. Dưới đây là các lựa chọn phổ biến năm 2026:
| Tool | Mô hình giá | Điểm nổi bật |
|---|---|---|
| New Relic | Từ ~$0 (100GB/tháng free), sau đó ~$0,30–0,55/GB dữ liệu; user full ~$99/tháng | APM đầy đủ, dashboard mạnh, dễ onboard. Định giá theo dung lượng dữ liệu nên dễ đội chi phí khi chạy load test lớn |
| Datadog | ~$31/host/tháng (APM), tính thêm theo span-ingested | Rất mạnh về hạ tầng + APM + log hợp nhất, phổ biến ở startup có tiền |
| Dynatrace | Enterprise, theo host-hour + GiB | AI (Davis) tự tìm nguyên nhân gốc, mạnh cho hệ thống rất lớn |
| Elastic APM | Mã nguồn mở, tự host miễn phí (trả tiền nếu dùng Elastic Cloud) | Tích hợp ELK stack, kiểm soát chi phí tốt, hợp với team có sẵn Elasticsearch |
| Grafana Tempo + Prometheus | Mã nguồn mở, tự host | Traces (Tempo) + metrics (Prometheus), ghép với Grafana. Rẻ, linh hoạt, phổ biến ở VN |
| SigNoz | Mã nguồn mở (OpenTelemetry-native), có SaaS | Thay thế Datadog chi phí thấp, dựng trên ClickHouse |
| Jaeger | Mã nguồn mở, miễn phí | Chuyên tracing, nhẹ, kinh điển cho môi trường Kubernetes |
OpenTelemetry — chuẩn hóa để không bị khóa vào một nhà cung cấp
Điểm mấu chốt bạn phải nhớ trong 2026: OpenTelemetry (OTel) đã trở thành chuẩn công nghiệp để phát metrics, logs và traces. Thay vì cài SDK riêng của từng vendor (New Relic agent, Datadog agent...), bạn instrument code một lần bằng OTel, rồi cấu hình gửi dữ liệu tới bất kỳ backend nào. Điều này cực kỳ quan trọng cho các team VN vốn nhạy cảm về chi phí: bạn có thể bắt đầu với Grafana Tempo miễn phí, sau này chuyển sang Datadog mà không phải sửa lại code.
Tình huống thực tế
Ví dụ 1 — Tiki mùa sale: con số client "nói dối"
Một team QA tại sàn TMĐT lớn ở TP.HCM (bối cảnh giả định dựa trên các sàn kiểu Tiki/Sendo) chạy load test bằng k6 mô phỏng 10.000 người dùng đồng thời cho luồng checkout trước đợt sale. k6 báo về: error rate 6%, p95 latency 3,1 giây. Ban đầu team đổ lỗi cho database vì "database luôn là thủ phạm".
Nhưng khi họ mở Datadog APM song song với lần chạy test, bức tranh khác hẳn. Trace waterfall cho thấy: Order Service chỉ tốn 90ms xử lý logic, query database trung bình 40ms — hoàn toàn khỏe mạnh. Thời gian bị nuốt ở một chỗ bất ngờ: mỗi request checkout gọi Inventory Service để kiểm tra tồn kho, và Inventory Service gọi tuần tự tới 8 warehouse service, mỗi cái 200ms. Đó là 1,6 giây thuần túy vì gọi tuần tự thay vì song song.
Bài học: Con số ở client (p95 = 3,1s) chỉ nói "chậm", không nói "chậm ở đâu". APM chỉ ra điểm nghẽn là một anti-pattern kiến trúc (sequential fan-out), không phải database. Team sửa thành gọi song song, p95 tụt xuống 900ms. Nếu chỉ nhìn con số client, họ đã tối ưu nhầm chỗ suốt một tuần.
Ví dụ 2 — Ngân hàng số VN: memory leak chỉ lộ ra khi ghép trace với metrics
Một ngân hàng số tại Hà Nội chạy soak test (test bền bỉ) trong 6 giờ cho API chuyển tiền. JMeter báo error rate ổn định ở 0,5% trong 4 giờ đầu, rồi từ giờ thứ 5 error rate tăng dần lên 12%. Chỉ nhìn JMeter, họ biết "có gì đó sập theo thời gian" nhưng không rõ cái gì.
Họ dùng New Relic. Biểu đồ metrics của JVM cho thấy heap memory tăng đều theo hình răng cưa nhưng đáy của mỗi chu kỳ GC cứ cao dần — dấu hiệu kinh điển của memory leak. Đồng thời, khi lọc các trace lỗi, họ thấy các span "database connection" bắt đầu timeout đúng vào lúc GC pause kéo dài hơn 2 giây (major GC vì heap gần đầy). New Relic Error Analytics gom nhóm lỗi và chỉ ra một object cache không bao giờ được giải phóng.
Bài học: Một mình load test cho bạn triệu chứng (error rate tăng theo thời gian). Một mình metrics cho bạn nghi ngờ (heap tăng). Nhưng ghép trace + metrics + error grouping trong APM mới cho bạn chẩn đoán (cache leak gây GC pause gây timeout). Đây là lý do vì sao ta cần cả hai công cụ, không phải một.
Ví dụ 3 — Startup logistics: tự host để tiết kiệm chi phí observability khi load test
Một startup giao vận ở Đông Nam Á có ngân sách hạn hẹp. Họ cần APM để phân tích performance nhưng không kham nổi hóa đơn Datadog vì mỗi lần load test đẩy hàng chục GB traces. Giải pháp: instrument toàn bộ service bằng OpenTelemetry, gửi traces vào Grafana Tempo và metrics vào Prometheus, tất cả tự host trên một VM. Trong lúc chạy k6, họ dùng chính Grafana làm màn hình chung — vừa xem kết quả k6 (đẩy qua Prometheus remote write) vừa xem trace của hệ thống trên cùng một dashboard.
Điểm hay: họ dùng cơ chế sampling — chỉ lưu 100% các trace bị lỗi hoặc chậm hơn 1 giây, còn trace bình thường chỉ lấy mẫu 5%. Nhờ vậy dung lượng lưu trữ giảm 90% mà vẫn giữ được đúng những trace cần điều tra.
Bài học: Observability không nhất thiết phải đắt. Với OpenTelemetry + stack mã nguồn mở + sampling thông minh, một team nhỏ ở VN hoàn toàn có thể có năng lực chẩn đoán ngang tầm doanh nghiệp lớn, với chi phí gần bằng không.
Hướng dẫn từng bước
Đây là quy trình chuẩn để tích hợp APM/observability vào một buổi performance test:
Bước 1 — Instrument hệ thống trước khi test. Cài agent APM hoặc OpenTelemetry SDK vào tất cả service trong đường đi của request. Với Java/Spring, thường chỉ cần thêm một java agent (-javaagent:opentelemetry-javaagent.jar) là auto-instrument được HTTP, JDBC, cache. Với Node.js, thêm gói @opentelemetry/auto-instrumentations-node. Kiểm tra bạn thấy được trace end-to-end trên môi trường staging trước.
Bước 2 — Đảm bảo môi trường test giống production. APM data chỉ hữu ích nếu môi trường phản ánh thực tế: cùng cấu hình connection pool, cùng số instance, cùng phiên bản. Test trên môi trường "yếu hơn thật" sẽ cho bạn điểm nghẽn giả.
Bước 3 — Bật correlation giữa load test và trace. Cho JMeter/k6 gửi kèm một header như traceparent hoặc một custom header (X-Test-Run-Id) trong mỗi request. Nhờ đó bạn lọc được đúng các trace sinh ra bởi bài test của mình, tách khỏi traffic thật (nếu có).
Bước 4 — Chạy load test và quan sát song song. Mở dashboard APM cùng lúc bấm nút chạy. Đừng chờ test xong mới xem. Bạn muốn thấy diễn biến theo thời gian: điểm nghẽn thường xuất hiện đúng vào một mốc tải cụ thể (ví dụ khi vượt 5.000 VU).
Bước 5 — Đọc theo trục thời gian, ghép ba lớp dữ liệu. Khi thấy client latency tăng vọt ở một thời điểm, quay lại đúng thời điểm đó trên APM: xem trace waterfall (span nào lâu nhất), xem metrics (CPU/heap/connection pool có bão hòa không), xem logs (có exception, timeout không).
Bước 6 — Khoanh vùng theo phương pháp USE và RED. Với mỗi tài nguyên, kiểm tra USE (Utilization – Saturation – Errors): CPU dùng bao nhiêu %, hàng đợi có bị bão hòa không, có lỗi không. Với mỗi service, kiểm tra RED (Rate – Errors – Duration): số request/giây, tỉ lệ lỗi, thời gian đáp ứng. Hai khung này giúp bạn khoanh vùng có hệ thống thay vì đoán mò.
Bước 7 — Truy ngược tới nguyên nhân gốc và ghi lại. Từ span chậm nhất, đào xuống: là do query thiếu index? Do lock database? Do gọi API bên ngoài? Do GC pause? Ghi lại phát hiện kèm ảnh chụp trace làm bằng chứng — đây là đầu vào cho các bài phân tích điểm nghẽn và viết báo cáo.
Lỗi thường gặp & mẹo
Lỗi 1: Chỉ nhìn con số client, bỏ qua bên trong. Đây là sai lầm phổ biến nhất. Một p95 cao có thể do database, do GC, do network, do một API bên ngoài — con số client không phân biệt được. Luôn mở APM song song.
Lỗi 2: Bật full tracing 100% khi load test tải lớn — và tự làm chậm hệ thống. Việc thu thập trace bản thân nó tốn tài nguyên. Nếu bạn ghi 100% trace ở 10.000 VU, overhead của agent có thể làm sai lệch chính kết quả test (observer effect). Mẹo: dùng tail-based sampling — lấy hết trace lỗi/chậm, chỉ lấy mẫu nhỏ trace bình thường.
Lỗi 3: Instrument thiếu — có "khoảng mù" trong trace. Nếu một service không được instrument, span của nó biến mất khỏi waterfall, và bạn sẽ thấy một khoảng thời gian trống bí ẩn. Kiểm tra mọi mắt xích, đặc biệt là các message queue (Kafka, RabbitMQ) và cronjob nền.
Lỗi 4: Nhầm lẫn correlation với causation. APM cho thấy CPU cao cùng lúc latency cao — không có nghĩa CPU là nguyên nhân. Có thể cả hai đều là hậu quả của một thread pool bị nghẽn. Hãy dùng trace để xác nhận dòng chảy nhân quả thực sự.
Mẹo vàng: Đồng bộ đồng hồ (clock sync qua NTP) giữa máy chạy load test và các server. Nếu lệch giờ, việc ghép timeline giữa k6 và APM sẽ sai và bạn sẽ điều tra nhầm khoảng thời gian.
Mẹo về chi phí: Nếu dùng APM tính tiền theo dung lượng dữ liệu (New Relic, Datadog), hãy cẩn thận với load test lớn — một buổi test có thể ngốn hết hạn mức tháng. Cân nhắc tự host OpenTelemetry cho môi trường test, chỉ dùng SaaS cho production.
Bài tập thực hành
- Dựng stack observability tối thiểu: Cài một ứng dụng demo microservices (ví dụ OpenTelemetry Demo App). Cấu hình gửi traces vào Jaeger hoặc Grafana Tempo. Xác nhận bạn thấy được trace end-to-end của một request.
- Ghép load test với trace: Viết một kịch bản k6 đơn giản bắn 50 VU vào ứng dụng demo trong 2 phút. Trong lúc chạy, mở APM và tìm 3 request chậm nhất. Với mỗi request, xác định span nào chiếm nhiều thời gian nhất và ghi lại giả thuyết về nguyên nhân.
- Tạo điểm nghẽn nhân tạo và phát hiện nó: Cố tình thêm một
sleep(500ms)vào một service, hoặc bỏ một index database. Chạy lại load test và dùng APM chứng minh điểm nghẽn — chụp ảnh trace waterfall trước và sau khi sửa.
- So sánh chi phí: Ước lượng dung lượng traces sinh ra bởi bài test 50 VU của bạn. Tính xem nếu chạy ở 5.000 VU trong 1 giờ với New Relic (~$0,35/GB), hóa đơn sẽ là bao nhiêu, và sampling 10% giúp tiết kiệm được bao nhiêu.
Tóm tắt
Load test cho bạn dữ liệu về hành vi hệ thống dưới tải; APM và observability cho bạn insight về nguyên nhân. Bạn cần cả hai — chạy đồng thời và ghép lại theo trục thời gian mới tìm ra điểm nghẽn thật thay vì đoán mò.
Ba trụ cột của observability là metrics, logs, và traces, trong đó distributed tracing (với trace ID và span ghép thành waterfall) là công cụ mạnh nhất để định vị nghẽn trong microservices. Thị trường APM 2026 trải rộng từ SaaS đắt tiền nhưng tiện (New Relic, Datadog, Dynatrace) tới stack mã nguồn mở tiết kiệm (Grafana Tempo + Prometheus, SigNoz, Jaeger). Chìa khóa để không bị khóa vào một nhà cung cấp là instrument bằng OpenTelemetry.
Quy trình chuẩn: instrument trước → môi trường giống production → correlate load test với trace → quan sát song song lúc chạy → đọc theo timeline bằng khung USE/RED → truy ngược tới nguyên nhân gốc kèm bằng chứng. Và luôn nhớ hai cạm bẫy lớn nhất: đừng chỉ tin con số client, và cẩn thận với overhead cùng chi phí của việc thu thập trace ở tải cao. Một performance engineer thực thụ không sợ câu hỏi "nghẽn ở đâu" — vì anh ta đã có sẵn cả dữ liệu lẫn insight để trả lời.