Mở đầu — vì sao bài này quan trọng
Khi bạn mới học k6, bạn thường viết mọi thứ vào trong file script: số VUs, thời gian chạy, các stages, cả những console.log để in kết quả. Điều đó ổn khi bạn đang thử nghiệm một mình trên máy. Nhưng đến khi bạn phải chạy test trong một pipeline CI/CD, hay khi sếp yêu cầu "chạy lại đúng test đó nhưng với 500 VUs thay vì 100", hoặc khi bạn cần đẩy kết quả vào một hệ thống giám sát để cả team cùng xem — thì việc phải sửa file, commit lại, rồi chạy lại trở nên phiền toái và dễ sai sót.
Đây chính là lúc CLI flags (các cờ dòng lệnh) và output options (tùy chọn xuất kết quả) của k6 phát huy sức mạnh. Chúng cho phép bạn tách phần cấu hình vận hành ra khỏi phần logic của test. Một script duy nhất, nhưng có thể chạy với hàng chục cấu hình khác nhau chỉ bằng cách thay đổi tham số ở dòng lệnh — không đụng đến một dòng code nào. Và kết quả có thể được xuất ra JSON, CSV, hay đẩy thẳng vào các hệ thống bên ngoài để phân tích sâu hơn.
Bài này tập trung riêng vào cách điều khiển k6 từ bên ngoài và cách lấy dữ liệu kết quả ra khỏi k6. Đây là kỹ năng bản lề: nó biến k6 từ một công cụ chạy tay thành một công cụ có thể tự động hóa và tích hợp vào quy trình làm việc thực tế của một đội QA chuyên nghiệp.
Khái niệm cốt lõi
Thứ tự ưu tiên của cấu hình trong k6
Trước khi nói về từng flag, bạn phải nắm một nguyên tắc nền tảng: k6 lấy cấu hình từ nhiều nguồn, và các nguồn này có thứ tự ưu tiên rõ ràng. Từ thấp đến cao:
- Giá trị mặc định của k6.
export const options = {...}trong file script.- Biến môi trường (ví dụ
K6_VUS,K6_DURATION). - Cờ dòng lệnh (ví dụ
--vus,--duration) — ưu tiên cao nhất.
vus: 10, nhưng khi chạy k6 run --vus 100 script.js, k6 sẽ dùng 100. Cờ dòng lệnh luôn "thắng". Đây là điều khiến CLI flags trở nên cực kỳ hữu ích: bạn có một cấu hình mặc định an toàn trong script, và ghi đè nó khi cần mà không phải sửa code.Các CLI flags phổ biến nhất
Nhóm flags điều khiển tải:
Ghi đè số VUs và thời gian chạy
k6 run --vus 100 --duration 5m script.jsViết tắt: -u thay cho --vus, không có tắt cho --duration
k6 run -u 50 --duration 30s script.js
Ghi đè cả một kịch bản ramp bằng --stage (viết tắt -s). Mỗi stage có dạng thời-gian:VUs-đích:
Tăng lên 50 VUs trong 30s, giữ 50 VUs trong 5 phút, rồi giảm về 0 trong 30s
k6 run --stage 30s:50,5m:50,30s:0 script.js
Lưu ý quan trọng: khi bạn dùng --stage, nó sẽ ghi đè toàn bộ mảng stages trong options, chứ không phải cộng thêm. Và --vus/--duration với --stage là loại trừ nhau — bạn dùng cái này thì đừng dùng cái kia cho cùng một lần chạy, nếu không k6 sẽ báo lỗi hoặc bỏ qua.
Nhóm flags truyền dữ liệu vào test:
Truyền biến môi trường vào script, đọc qua __ENV.BASE_URL
k6 run --env BASE_URL=https://staging.tiki.vn --env TOKEN=abc123 script.js
Trong script bạn đọc bằng __ENV.BASE_URL. Đây là cách sạch sẽ để cùng một script chạy được cho môi trường staging, UAT, production mà không hard-code URL.
Nhóm flags kiểm soát hành vi khi có lỗi:
Dừng ngay khi bất kỳ threshold nào bị vi phạm (rất hữu ích trong CI)
k6 run --fail-on-fail script.jsk6 mặc định: nếu threshold fail, exit code = 99. Có thể tinh chỉnh:
k6 run --no-thresholds script.js # bỏ qua đánh giá thresholds
k6 run --no-summary script.js # không in bảng tóm tắt cuối
Một flag rất hay bị bỏ quên là --quiet (-q) để giảm log, và --verbose (-v) để debug khi có gì đó không đúng. Còn --http-debug (hoặc --http-debug=full) in ra chi tiết từng request/response — cực kỳ giá trị khi bạn nghi ngờ script đang gửi sai payload.
Output options — lấy kết quả ra khỏi k6
Mặc định, k6 chỉ in một bảng tóm tắt (summary) ra terminal khi kết thúc. Bảng đó đẹp cho mắt người nhưng vô dụng cho máy. Cờ --out cho phép bạn xuất từng điểm dữ liệu (data point) ra định dạng khác:
Xuất toàn bộ metrics dạng JSON (mỗi dòng một data point)
k6 run --out json=result.json script.jsNén luôn để đỡ tốn dung lượng
k6 run --out json=result.json.gz script.jsXuất CSV
k6 run --out csv=result.csv script.jsĐẩy vào InfluxDB (chi tiết ở bài dashboard riêng)
k6 run --out influxdb=http://localhost:8086/k6 script.jsXuất nhiều nơi cùng lúc
k6 run --out json=result.json --out csv=result.csv script.js
Ngoài ra bạn có thể ghi bảng tóm tắt ra file bằng --summary-export:
k6 run --summary-export=summary.json script.js
File này chứa các giá trị tổng hợp (avg, min, max, p90, p95, p99...) dưới dạng JSON — rất tiện để một script khác đọc và quyết định "pass hay fail" trong pipeline.
Phân biệt hai loại output cần nhớ: --out xuất dữ liệu thô theo dòng thời gian (dùng cho phân tích chi tiết, vẽ đồ thị), còn --summary-export xuất con số tổng hợp cuối cùng (dùng cho quyết định pass/fail nhanh). Bạn thường dùng cả hai cho hai mục đích khác nhau.
Tình huống thực tế
Ví dụ 1: Team QA của Tiki chạy cùng một script cho ba môi trường
Đội QA tại một sàn thương mại điện tử lớn ở Việt Nam có một script checkout_flow.js mô phỏng luồng thanh toán. Ban đầu, mỗi bạn tester tự sửa URL trong file cho môi trường của mình, dẫn đến ba phiên bản script khác nhau trong ba nhánh Git — và một lần, có người vô tình chạy test tải nặng vào thẳng production vì quên đổi URL.
Sau sự cố đó, họ tái cấu trúc: mọi giá trị môi trường đều đọc qua __ENV. Giờ họ chạy:
Staging
k6 run --env BASE_URL=https://staging.tiki.internal --vus 50 --duration 3m checkout_flow.jsUAT với tải cao hơn
k6 run --env BASE_URL=https://uat.tiki.internal --vus 200 --duration 10m checkout_flow.js
Chỉ còn một file duy nhất. URL không bao giờ được hard-code, nên khả năng "bắn nhầm" vào production gần như bị loại bỏ (production được khóa bằng danh sách cho phép ở tầng CI riêng).
Bài học rút ra: tách cấu hình vận hành ra khỏi code không chỉ giúp gọn gàng, mà còn là một hàng rào an toàn. Một script + nhiều flags an toàn hơn nhiều script gần giống nhau.
Ví dụ 2: MoMo tinh chỉnh mức tải mà không cần sửa code trong đêm test
Một đội performance tại một ví điện tử phổ biến ở Việt Nam chuẩn bị cho đợt cao điểm cuối tháng. Họ có script mặc định trong options là 100 VUs. Trong buổi test đêm, người phụ trách hạ tầng liên tục yêu cầu tăng dần tải để quan sát khi nào hệ thống bắt đầu ngợp.
Thay vì sửa file và commit mỗi lần, kỹ sư test chỉ chạy lại với các flag khác nhau:
k6 run --stage 1m:100,3m:100,1m:0 payment.js
k6 run --stage 1m:300,3m:300,1m:0 payment.js
k6 run --stage 1m:500,5m:500,1m:0 payment.js --out json=run_500.json.gz
Mỗi lần chạy mất chưa đến 10 giây để cấu hình. Lần chạy 500 VUs, họ bật thêm --out json để giữ lại dữ liệu thô, vì đó là mức mà latency bắt đầu tăng vọt và họ cần phân tích kỹ sau. Đến gần sáng, họ có một loạt file JSON đánh số theo mức tải, dễ dàng đối chiếu.
Bài học rút ra: --stage, --vus, --duration biến k6 thành một "núm vặn" tải mà bạn điều chỉnh tức thời. Kết hợp với --out chọn lọc (chỉ bật khi cần), bạn vừa linh hoạt vừa không sinh ra hàng GB dữ liệu vô ích.
Ví dụ 3: Một startup fintech ở Singapore gác cổng CI bằng exit code
Một công ty fintech nhỏ ở Đông Nam Á muốn mỗi pull request phải qua một bài performance smoke test trước khi merge. Họ đặt thresholds trong script, rồi trong pipeline GitLab CI chạy:
k6 run \
--env BASE_URL=$STAGING_URL \
--vus 20 --duration 1m \
--summary-export=summary.json \
--quiet \
api_smoke.js
Điểm mấu chốt: khi bất kỳ threshold nào fail, k6 tự thoát với exit code 99. GitLab thấy exit code khác 0 nên đánh dấu job thất bại, chặn merge. Không cần viết thêm logic phân tích — chính k6 đã ra quyết định. File summary.json được lưu làm artifact để lập trình viên xem con số cụ thể mà không phải chạy lại.
Họ dùng --quiet để log CI gọn gàng, và --vus 20 nhẹ để không kéo dài pipeline. Với các test tải nặng thực sự, họ tách sang một scheduled job chạy ban đêm chứ không chặn từng PR.
Bài học rút ra: exit code của k6 là "hợp đồng" giữa nó và hệ thống CI. Hiểu rằng threshold fail → exit 99 giúp bạn dựng cổng chất lượng tự động mà không cần công cụ trung gian.
Hướng dẫn từng bước
Giả sử bạn có một script test.js với options mặc định khiêm tốn. Hãy đi qua quy trình vận hành nó bằng CLI.
Bước 1 — Viết script sẵn sàng cho việc ghi đè. Trong script, đọc mọi giá trị có thể thay đổi qua __ENV, và để options một giá trị mặc định an toàn:
export const options = {
vus: 10,
duration: '30s',
thresholds: {
http_req_duration: ['p(95)<500'],
http_req_failed: ['rate<0.01'],
},
};
const BASE = __ENV.BASE_URL || 'http://localhost:3000';
Bước 2 — Chạy thử nhẹ để xác nhận script đúng. Trước khi tải nặng, luôn chạy một smoke test 1 VU:
k6 run --vus 1 --iterations 1 --http-debug=full test.js
--http-debug=full giúp bạn thấy request/response thực tế, kiểm tra xem có gửi đúng header, đúng body không.
Bước 3 — Ghi đè tải cho lần chạy thật. Khi script đã đúng, mở tải:
k6 run --env BASE_URL=https://staging.example.com --stage 1m:100,3m:100,1m:0 test.js
Bước 4 — Bật output để lưu dữ liệu. Nếu cần phân tích sau, thêm --out và --summary-export:
k6 run \
--env BASE_URL=https://staging.example.com \
--stage 1m:100,3m:100,1m:0 \
--out json=run.json.gz \
--summary-export=summary.json \
test.js
Bước 5 — Kiểm tra exit code. Sau khi chạy, xem k6 báo pass hay fail:
echo $?
0 = mọi threshold đạt; 99 = có threshold bị vi phạm
Bước này là nền tảng cho việc tích hợp CI ở các bài sau — hiện tại chỉ cần bạn nắm rằng exit code chính là tín hiệu tự động.
Lỗi thường gặp & mẹo
Lỗi 1 — Dùng --stage cùng --duration và ngạc nhiên vì kết quả lạ. Hai cái này loại trừ nhau. --stage đã bao hàm thời gian trong từng chặng rồi. Chọn một trong hai, đừng trộn.
Lỗi 2 — Tưởng --out json tạo ra file JSON hợp lệ để đọc bằng JSON.parse. Không phải. File --out json là định dạng JSON Lines — mỗi dòng là một object JSON riêng (một data point hoặc metric). Bạn phải đọc từng dòng, không thể parse cả file như một mảng. Nếu muốn con số tổng hợp gọn, hãy dùng --summary-export thay vì cố phân tích file --out json khổng lồ.
Lỗi 3 — Bật --out json cho test tải rất nặng và làm đầy ổ đĩa. Một test 1000 VUs chạy 30 phút có thể sinh ra hàng GB data point. Luôn dùng đuôi .gz để nén, hoặc chỉ bật output thô cho những lần chạy bạn thực sự cần mổ xẻ.
Lỗi 4 — Quên rằng biến môi trường hệ điều hành không tự vào script. Trong k6, biến shell bình thường không tự động có trong __ENV (khác với Node.js). Bạn phải truyền tường minh qua --env, hoặc dùng tiền tố K6_ cho các option gốc của k6. Đây là điểm hay gây bối rối cho người mới.
Mẹo 1 — Đặt tên file output theo mức tải và thời điểm. Ví dụ run_500vus_2026-06-27.json.gz. Khi bạn chạy hàng chục lần trong một đêm, việc này cứu bạn khỏi mớ file result.json chồng lên nhau.
Mẹo 2 — Dùng k6 run --help thường xuyên. Danh sách flags khá dài và thay đổi theo phiên bản. --help là nguồn chính xác nhất cho version bạn đang dùng.
Mẹo 3 — Kết hợp --quiet cho CI, --http-debug cho debug cục bộ. Đừng để log debug tràn ngập trong pipeline; và đừng debug mà thiếu thông tin. Chọn đúng flag cho đúng ngữ cảnh.
Bài tập thực hành
- Ghi đè cơ bản. Lấy một script bất kỳ có
vus: 10, duration: '30s'trongoptions. Chạy nó bằng dòng lệnh sao cho thực tế chạy với 50 VUs trong 2 phút mà không sửa file. Xác nhận qua bảng tóm tắt rằng k6 đã dùng đúng giá trị bạn ghi đè.
- Tham số hóa môi trường. Sửa script để đọc
__ENV.BASE_URL. Chạy hai lần với hai URL khác nhau (ví dụ một public API test nhưhttps://test.k6.iovà một endpoint khác), chỉ bằng cách đổi--env.
- Kịch bản ramp qua CLI. Chạy script với
--stagetạo ra: tăng lên 30 VUs trong 20s, giữ 30 VUs trong 1 phút, giảm về 0 trong 20s. Đừng đụng vàooptions.stages.
- Xuất và so sánh output. Chạy cùng một test hai lần với hai mức tải, mỗi lần xuất
--out json=...gzvà--summary-export. Mở hai filesummary.json, so sánh p95 củahttp_req_duration. Ghi lại: khi tải tăng, p95 thay đổi thế nào?
- Đọc exit code. Đặt một threshold cố tình khó đạt (ví dụ
p(95)<1). Chạy test, rồi kiểm traecho $?. Xác nhận bạn nhận được exit code 99 và giải thích tại sao điều này hữu ích cho một pipeline tự động.
Tóm tắt
CLI flags và output options là thứ nâng k6 từ một công cụ chạy tay lên thành một công cụ vận hành được và tích hợp được. Điểm cốt lõi cần nhớ:
- Cờ dòng lệnh có ưu tiên cao nhất, ghi đè cả
optionstrong script. Điều này cho phép một script duy nhất chạy với vô số cấu hình. --vus,--duration,--stageđiều khiển tải;--envtruyền dữ liệu qua__ENV; nhớ rằng--stageloại trừ--duration.--out json/csv/influxdbxuất dữ liệu thô theo dòng thời gian (JSON Lines, nên nén.gz), còn--summary-exportxuất con số tổng hợp dùng cho quyết định pass/fail.- Exit code 99 khi threshold fail là "hợp đồng" với hệ thống tự động — nền tảng cho gác cổng chất lượng.
- Tách cấu hình khỏi code vừa linh hoạt vừa an toàn: nó ngăn những sai lầm như bắn tải nhầm vào production.