Product Management
Đăng nhập
ESC

Nhập từ khóa để tìm kiếm

↑↓ Di chuyển
Enter Mở
ESC Đóng

k6 advanced — xk6 extensions

Mở đầu — vì sao bài này quan trọng

Nếu bạn đã đi qua các bài trước về k6, bạn sẽ thấy k6 rất mạnh — nhưng bạn cũng đã kịp nhận ra một giới hạn: k6 core chỉ nói được vài "ngôn ngữ" giao thức. Cụ thể, bản k6 bạn tải về mặc định hỗ trợ tốt HTTP/1.1, HTTP/2, WebSocket và gRPC. Vậy nếu hệ thống của bạn không chỉ có HTTP thì sao?

Đây không phải câu hỏi lý thuyết. Trong thực tế nghề Performance Engineer ở Việt Nam, bạn sẽ liên tục gặp những "cửa" mà HTTP không mở được:

  • Một hệ thống fintech dùng Kafka làm xương sống truyền message giữa các service — bạn cần bơm tải trực tiếp vào Kafka, không phải qua REST.
  • Một sàn thương mại điện tử cần kiểm tra xem Redis cache có chịu nổi 12.12 không, mà Redis nói giao thức riêng, không phải HTTP.
  • Đội DevOps muốn ghi metric kết quả test thẳng vào một time-series database mà k6 chưa có output driver.
  • Bạn cần so sánh kết quả trả về với dữ liệu gốc trong PostgreSQL ngay trong lúc test để phát hiện lỗi dữ liệu dưới tải.
Câu trả lời của k6 cho tất cả những tình huống này là xk6 extensions — cơ chế cho phép bạn "lắp ráp" một bản k6 tùy biến, gắn thêm module viết bằng ngôn ngữ Go để nói được đúng giao thức bạn cần. Đây chính là ranh giới giữa một người "biết dùng k6" và một người "làm chủ k6" ở cấp độ nâng cao. Bài này sẽ dạy bạn tư duy đó, và quan trọng hơn là dạy bạn khi nào NÊN và khi nào KHÔNG NÊN đụng đến nó.

Khái niệm cốt lõi

xk6 là gì?

k6 được viết bằng Go. Điểm hay của Go là chương trình sau khi biên dịch (compile) sẽ ra một file nhị phân (binary) duy nhất. Điều này có nghĩa là bạn không thể "cắm nóng" một plugin vào k6 lúc chạy như kiểu cài package Python bằng pip. Thay vào đó, để thêm tính năng, bạn phải biên dịch lại một bản k6 mới đã bao gồm code của extension.

xk6 chính là công cụ dòng lệnh (CLI) làm việc "lắp ráp và biên dịch lại" đó cho bạn. Bạn ra lệnh cho xk6 rằng "hãy tạo cho tôi một bản k6 có kèm extension Kafka và extension SQL", và nó sẽ tải mã nguồn, gộp lại, biên dịch, rồi trả cho bạn một file k6 mới — trông giống hệt k6 gốc nhưng đã "mọc thêm tay chân".

Hãy nhớ công thức tinh thần này:

> k6 gốc + (các extension) → xk6 biên dịch → bản k6 tùy biến của riêng bạn.

Hai loại extension

Không phải extension nào cũng làm cùng một việc. Có hai họ chính bạn cần phân biệt rõ:

1. JavaScript extensions (mở rộng khả năng của script). Loại này thêm các module mới để bạn import và gọi trong file test JavaScript. Ví dụ: xk6-sql cho phép bạn viết import sql from 'k6/x/sql' rồi query database. Đây là loại phổ biến nhất và cũng là loại bạn dùng nhiều nhất.

2. Output extensions (mở rộng nơi ghi kết quả). Loại này không thêm hàm cho script mà thêm một "đích đến" mới cho metric. Ví dụ xk6-output-prometheus-remote cho phép bạn đẩy metric thẳng sang Prometheus. Bạn kích hoạt chúng bằng flag --out chứ không phải bằng import.

Bảng các extension phổ biến

Đây là những extension bạn sẽ gặp nhiều nhất trong công việc thật:

ExtensionMục đíchCách dùng trong script
xk6-sqlKết nối và query SQL database (MySQL, PostgreSQL, SQL Server...)import sql from 'k6/x/sql'
xk6-kafkaSản xuất (produce) / tiêu thụ (consume) message Kafkaimport { Writer, Reader } from 'k6/x/kafka'
xk6-redis (nay đã vào core dạng k6/experimental/redis)Thao tác với Redisimport redis from 'k6/experimental/redis'
xk6-browser (nay là k6/browser trong core)Điều khiển trình duyệt thật để test UIimport { browser } from 'k6/browser'
xk6-mqttTest giao thức MQTT (IoT, thiết bị)import { Client } from 'k6/x/mqtt'
xk6-output-prometheus-remoteGhi metric sang PrometheusFlag --out xk6-prometheus-remote
xk6-output-influxdbGhi metric sang InfluxDB v2Flag --out xk6-influxdb
xk6-fakerSinh dữ liệu giả (tên, email, số điện thoại...)import faker from 'k6/x/faker'
xk6-disruptorTiêm lỗi (fault injection) để test chaosimport { ServiceDisruptor } from 'k6/x/disruptor'
Một lưu ý cực kỳ quan trọng về sự tiến hóa: những gì hôm nay là extension, ngày mai có thể vào core. xk6-redisxk6-browser từng là extension riêng, nay đã được đưa thẳng vào k6 dưới namespace k6/experimental hoặc k6/browser. Vì vậy trước khi tốn công build một bản k6 tùy biến, hãy luôn kiểm tra xem tính năng đó đã có sẵn trong core chưa.

Nhận biết extension đáng tin cậy

Vì bạn đang biên dịch code của người khác vào công cụ test của mình, độ tin cậy là vấn đề sống còn. Hãy phân biệt:

  • Extension chính thức (do Grafana Labs — công ty đứng sau k6 — phát triển): thường bắt đầu bằng tên trong tổ chức grafana. Đây là loại an tâm nhất.
  • Extension cộng đồng: do các nhà phát triển bên thứ ba viết. Nhiều cái rất tốt (xk6-kafka, xk6-faker), nhưng bạn phải tự đánh giá: số sao GitHub, ngày commit gần nhất, có tương thích phiên bản k6 hiện tại không.

Tình huống thực tế

Tình huống 1 — Sàn TMĐT test Kafka trước mùa sale (bối cảnh Việt Nam)

Một công ty thương mại điện tử tầm trung ở TP.HCM — gọi là ShopViet — có kiến trúc microservices. Khi khách đặt hàng, service order không gọi trực tiếp service inventory, mà đẩy một message vào Kafka topic order.created, rồi các service khác consume. Trước đợt sale 12.12, đội QA lo lắng: nếu 50.000 đơn/phút dồn vào, liệu Kafka và các consumer có nghẽn không?

Vấn đề là họ không thể mô phỏng tải này chỉ bằng cách gọi HTTP vào API đặt hàng, vì như thế test bị "trộn" luôn cả tầng web, DB, và họ không cô lập được đúng khâu Kafka. Giải pháp: dùng xk6-kafka để bơm message thẳng vào topic với tốc độ có kiểm soát.

Đội build một bản k6 kèm xk6-kafka, viết script produce 50.000 message/phút vào order.created, đồng thời một scenario khác consume topic order.processed để đo độ trễ end-to-end. Kết quả: họ phát hiện khi vượt 38.000 msg/phút, độ trễ consumer nhảy từ 40ms lên 2,3 giây do consumer group chỉ có 3 partition. Họ tăng partition lên 12 trước ngày sale.

Bài học: xk6-kafka cho phép cô lập và tấn công đúng thành phần message-queue mà HTTP không chạm tới được. Nếu chỉ test qua HTTP, họ đã bỏ sót điểm nghẽn thật sự nằm ở tầng cấu hình partition.

Tình huống 2 — Fintech kiểm tra tính toàn vẹn dữ liệu dưới tải với xk6-sql

Một startup fintech ở Hà Nội — gọi là PayNhanh — cung cấp ví điện tử. Họ có một nỗi sợ đặc thù của ngành tài chính: dưới tải cao, liệu số dư trong DB có bị ghi sai do race condition không? Test HTTP thông thường chỉ kiểm tra API trả về 200 OK, nhưng "200 OK mà số dư sai" còn nguy hiểm hơn cả lỗi 500.

Họ dùng xk6-sql kết nối trực tiếp vào PostgreSQL. Trong script, sau mỗi loạt giao dịch nạp tiền, họ query thẳng bảng wallet để đối chiếu: tổng số tiền nạp qua API phải bằng chênh lệch số dư trong DB. Với 500 VU (virtual user) nạp tiền đồng thời, họ phát hiện cứ khoảng 1.200 giao dịch thì có 1 giao dịch số dư bị lệch — dấu hiệu của lỗi khóa lạc quan (optimistic lock) triển khai sai.

Bài học: xk6-sql biến bài performance test thành một bài kiểm tra "vừa nhanh vừa đúng". Con số 1/1.200 nhỏ, nhưng với fintech thì đó là lỗi chặn phát hành (blocker).

Tình huống 3 — Đội SRE và bài học "đừng build extension khi không cần"

Một đội SRE ở một công ty logistics muốn đẩy metric k6 sang InfluxDB. Một bạn kỹ sư trẻ định dùng xk6-output-influxdb, mất nửa ngày loay hoay cài Go, build bản tùy biến, rồi CI gãy vì runner không có Go. Người mentor ghé qua và hỏi một câu: "InfluxDB của mình là v1 hay v2?" — Hóa ra hệ thống dùng InfluxDB v1, mà k6 core đã hỗ trợ sẵn output InfluxDB v1 qua --out influxdb=..., không cần extension nào cả. Extension chỉ cần khi dùng InfluxDB v2.

Bài học: Trước khi build, luôn kiểm tra core đã có chưa. xk6 rất mạnh nhưng nó thêm chi phí vận hành: bạn phải quản lý một binary tùy biến, phải build lại khi nâng cấp k6, và CI/CD phức tạp hơn. Đừng dùng dao mổ trâu để cắt tiết gà.

Hướng dẫn từng bước

Hãy đi qua quy trình build một bản k6 tùy biến. Giả sử bạn cần cả xk6-sql (kèm driver PostgreSQL) và xk6-kafka.

Bước 1 — Cài Go. xk6 cần Go phiên bản 1.20 trở lên. Kiểm tra:

go version

Nếu chưa có, tải từ trang chủ Go và cài. Đây là điều kiện tiên quyết duy nhất bắt buộc.

Bước 2 — Cài công cụ xk6.

go install go.k6.io/xk6/cmd/xk6@latest

Sau lệnh này, binary xk6 sẽ nằm trong $GOPATH/bin (thường là ~/go/bin). Đảm bảo thư mục này có trong biến môi trường PATH.

Bước 3 — Build bản k6 tùy biến. Đây là bước trung tâm:

xk6 build \
  --with github.com/grafana/xk6-sql@latest \
  --with github.com/grafana/xk6-sql-driver-postgres@latest \
  --with github.com/mostafa/xk6-kafka@latest

Giải thích: mỗi --with là một extension. xk6 sẽ tải mã nguồn từng cái, gộp với k6 core, biên dịch. Xong xuôi, bạn có một file k6 mới ngay trong thư mục hiện tại. Bạn có thể ghim phiên bản k6 gốc bằng xk6 build v0.50.0 --with ... để đảm bảo tính lặp lại (reproducibility).

Bước 4 — Xác nhận extension đã được nhúng.

./k6 version

Lệnh này sẽ in ra không chỉ phiên bản k6 mà cả danh sách extension đã build vào — một cách kiểm tra nhanh rằng mọi thứ đã đúng chỗ.

Bước 5 — Viết và chạy script dùng extension. Ví dụ với xk6-sql:

import sql from 'k6/x/sql';
import driver from 'k6/x/sql/driver/postgres';

const db = sql.open(driver, 'postgres://user:pass@localhost:5432/mydb?sslmode=disable');

export function setup() { // chuẩn bị dữ liệu nếu cần }

export default function () { const rows = db.query('SELECT balance FROM wallet WHERE user_id = $1', 1); // kiểm tra kết quả dưới tải }

export function teardown() { db.close(); }

Điểm mấu chốt: bạn phải chạy bằng binary ./k6 vừa build, KHÔNG phải binary k6 cài toàn cục. Nếu chạy nhầm k6 gốc, dòng import sql from 'k6/x/sql' sẽ báo lỗi "module not found".

./k6 run test-sql.js

Bước 6 (thay thế) — Dùng Docker để khỏi cài Go. Nếu không muốn cài Go trên máy, dùng image builder chính thức:

docker run --rm -u "$(id -u):$(id -g)" -v "${PWD}:/xk6" \
  grafana/xk6 build \
  --with github.com/grafana/xk6-sql@latest

Cách này đặc biệt hữu ích trong CI/CD: bạn build binary một lần, lưu làm artifact, rồi các job test sau dùng lại.

Lỗi thường gặp & mẹo

Lỗi 1 — Chạy nhầm binary. Đây là lỗi phổ biến nhất. Bạn build ra ./k6 trong thư mục dự án nhưng gõ k6 run (không có ./), hệ thống lấy k6 toàn cục và báo không tìm thấy module k6/x/.... Mẹo: đổi tên binary tùy biến thành k6-custom hoặc dùng đường dẫn tuyệt đối để tránh nhầm.

Lỗi 2 — Xung đột phiên bản. Một extension cũ có thể không tương thích với k6 phiên bản mới nhất, gây gãy build với thông báo về Go module khó hiểu. Mẹo: ghim (pin) cả phiên bản k6 lẫn phiên bản extension bằng tag cụ thể thay vì @latest, ví dụ --with github.com/mostafa/xk6-kafka@v0.26.0. Điều này còn giúp CI của bạn ổn định, không "hôm nay chạy mai gãy".

Lỗi 3 — Quên rằng extension làm chậm và tốn RAM hơn. Một số extension (đặc biệt xk6-browser, xk6-sql) nặng hơn nhiều so với HTTP thuần. Cùng một máy, số VU tối đa bạn chạy được sẽ thấp hơn hẳn. Mẹo: đừng lấy con số throughput của test HTTP thuần làm chuẩn cho test có extension; hãy đo lại năng lực máy phát tải riêng cho từng loại.

Lỗi 4 — Cắm database/Kafka làm thắt cổ chai giả. Khi dùng xk6-sql hay xk6-kafka, nếu bản thân connection pool hoặc client cấu hình kém, bạn có thể đang đo giới hạn của công cụ test chứ không phải của hệ thống bị test. Mẹo: luôn chạy một baseline nhỏ để chắc rằng máy phát tải không phải là điểm nghẽn trước khi tin vào kết quả.

Lỗi 5 — Không kiểm tra core trước. Như tình huống 3 đã kể: nhiều tính năng (Redis, browser, gRPC, InfluxDB v1) đã có sẵn trong core. Mẹo vàng: mỗi khi định build extension, hãy tra tài liệu k6 mục k6/experimental trước. Tiết kiệm được rất nhiều thời gian.

Mẹo nâng cao — Cân nhắc tự viết extension. Nếu hệ thống của bạn dùng một giao thức nội bộ độc quyền (một số ngân hàng VN có protocol riêng cho core banking), bạn hoàn toàn có thể tự viết extension bằng Go theo template xk6-extension-example. Đây là "cửa" cuối cùng khi không extension công khai nào phù hợp — nhưng chỉ nên làm khi thật sự cần và có người biết Go trong đội.

Bài tập thực hành

  • Kiểm tra core trước. Trước khi làm gì, tra tài liệu k6 và liệt kê 3 tính năng từng là extension nhưng nay đã vào core. Điều này rèn phản xạ "kiểm tra core trước".
  • Build bản k6 đầu tiên. Cài Go và xk6, sau đó build một bản k6 kèm xk6-faker. Chạy ./k6 version và xác nhận thấy tên extension trong output.
  • Sinh dữ liệu giả dưới tải. Viết một script dùng xk6-faker sinh 100 bộ thông tin người dùng giả (tên, email, số điện thoại kiểu Việt Nam nếu locale hỗ trợ) và log ra. Quan sát xem việc sinh dữ liệu có làm giảm số VU tối đa so với test HTTP thuần không.
  • Tình huống Kafka (nâng cao, nếu có Kafka local). Dùng Docker chạy một Kafka local, build k6 kèm xk6-kafka, viết script produce 1.000 message vào một topic và một scenario consume lại. Đo độ trễ produce–consume.
  • Ra quyết định. Viết một đoạn 5–7 câu trả lời tình huống: "Đội bạn cần đẩy metric k6 sang Prometheus. Bạn sẽ dùng extension nào, hay core đã có sẵn? Chi phí vận hành của lựa chọn build extension là gì?" — Bài này rèn tư duy cân nhắc, thứ quan trọng hơn cả kỹ năng gõ lệnh.

Tóm tắt

k6 core mạnh nhưng chỉ nói được một số giao thức nhất định; khi bạn cần Kafka, SQL, MQTT, output tùy biến hay bất kỳ giao thức lạ nào, xk6 extensions là con đường mở rộng. Điểm cốt lõi cần khắc sâu:

  • xk6 hoạt động bằng cách biên dịch lại một bản k6 mới đã nhúng extension — không phải plugin cắm nóng lúc chạy như Python.
  • Có hai họ extension: JavaScript extension (thêm module để import) và output extension (thêm đích ghi metric qua --out).
  • Ưu tiên extension chính thức của Grafana; với extension cộng đồng, hãy đánh giá độ tin cậy trước khi build vào công cụ test.
  • Quy trình chuẩn: cài Go → cài xk6 → xk6 build --with ... → chạy bằng đúng binary vừa build.
  • Lỗi kinh điển nhất là chạy nhầm binary k6 toàn cục; và sai lầm chiến lược phổ biến nhất là build extension trong khi core đã có sẵn tính năng đó.
Nắm được xk6, bạn không còn bị giới hạn bởi những gì k6 "sinh ra đã có". Bạn có thể may đo một công cụ đo tải khớp chính xác với kiến trúc thật của hệ thống mình — và đó chính là dấu hiệu của một Performance Engineer trưởng thành.

Học xong bài này rồi? Tạo tài khoản miễn phí để lưu lại — lần sau vào là biết ngay đang dở ở đâu, và học hết khóa thì có chứng chỉ. Lưu tiến độ của tôi