k6: Công cụ dễ dàng và hiệu quả cho load testing (Phần 1)

k6 hỗ trợ load testing với ngôn ngữ để viết test là JavaScript. Công cụ này nổi bật bởi tính đơn giản và hiệu năng mà nó mang lại. Hãy cùng tìm hiểu về k6 qua bài viết này nhé.

Upload image

Giống như bất cứ sản phẩm chất lượng nào trên thị trường, một trang web trong quá trình phát triển luôn phải trải qua rất nhiều bài kiểm tra. Một trong số đó là bài test cực kỳ quan trọng mang tên load testing – kiểm thử tải.

Để thực hiện load testing, ta cần phải quan tâm tới nhiều thứ, từ việc mô phỏng một số lượng user cùng lúc truy cập, mô phỏng lượng user thay đổi theo thời gian tới việc theo dõi, ghi lại và tính toán các con số, cùng vô vàn những thứ phức tạp khác. Chính vì vậy, k6 được sinh ra để giúp việc thực thi những chuyện đó trở nên dễ dàng và tối ưu nhất cho các developer hay tester.

k6 là gì?

k6 tên đầy đủ là Grafana k6, là một công cụ hỗ trợ load testing được phát triển bởi Grafana Labs và cộng đồng, nó là dự án mã nguồn mở và có thể mở rộng. k6 hỗ trợ tốt cho mô hình CI/CD, dễ dàng tích hợp vào các CI/CD tools như Jenkins, Azure Pipelines.

k6 được phát triển bằng Go tuy nhiên test script được viết bằng JavaScript ES6, điều này giúp chúng ta dễ dàng tiếp cận và sử dụng. k6 cũng phát triển dịch vụ k6 Cloud mạnh mẽ, với nhiều tính năng đặc biệt như tạo test script với giao diện thân thiện, chạy test với số lượng lớn cùng nhiều tính năng khác.

Bây giờ chúng ta hãy cùng bắt tay vào cài đặt, tìm hiểu và trải nghiệm nhé!

1. Cài đặt k6 OSS

Với Windows, chạy command với Windows Package Manager:

winget install k6

Hoặc nếu không muốn chạy command bạn có thể tải file .msi tại đây.

Với hệ điều hành MacOS, Linux, Ubuntu, mời bạn tham khảo cách cài đặt cụ thể tại đây.

2. Một vài thành phần quan trọng của test script và giải thích test output

Chúng ta sẽ cùng nhau tìm hiểu về k6 dựa vào việc chạy test script đơn giản đầu tiên trên local nhé.

Trước tiên, hãy tạo một file script-test-1.js có nội dung như sau:

import http from 'k6/http';
import { sleep } from 'k6';

export const options = {
  vus: 10,
  duration: '30s',
};

export default function () {
  http.get('http://test.k6.io');
  sleep(1);
}

Để chạy test, các bạn cần truy cập tới đường dẫn chứa file và chạy command:

k6 run [File Name]

Kết quả cho ra như sau:

Upload image

Vậy là chúng ta vừa mới load test một trang web bằng k6 rồi đấy. Bây giờ cùng mình tìm hiểu về cả test script lẫn test output nhé.

Những khái niệm cơ bản

Cùng xem lại script này, nó là một file JavaScript thuần mà thôi.

Trong file script, ta đã export hai thành phần cơ bản của k6:

  • Default function: mỗi test script luôn phải export một default function, nó mô tả công việc mà mỗi VUs (Virtual Users) làm và lặp đi lặp lại trong suốt quá trình test.
  • Options: định nghĩa test-run behavior, cấu hình của k6 có thể nằm ở nhiều nơi, và khi có cùng một giá trị cấu hình được đặt ở nhiều nơi thì k6 sẽ lấy giá trị ở nơi có độ ưu tiên cao hơn, cụ thể các bạn tham khảo tại đây.

Ví dụ, chúng ta cấu hình khi chạy command line:

k6 run --vus 20 script-test-1.js

Khi đó, options ở bên ngoài command sẽ đè lên options ở bên trong script.

Upload image

Lúc này, options 20 VUs ở bên ngoài đã override lại 10 VUs ở bên trong, test sẽ thực thi theo options: 20 VUs + duration 30s.

Có rất nhiều options cho k6, nhưng trước tiên mình muốn giới thiệu về hai options là VUsduration.

  • VUsvirtual users – một khái niệm quan trọng trong load testing và k6. Đúng như tên gọi của nó thì virtual users chính là số lượng người dùng ảo mà k6 tạo ra để mô phỏng quá trình truy cập vào web của mình. VUs trong k6 về cơ bản là các vòng lặp while(true) chạy song song.
  • Duration chính là thời gian thực thi của test này.

Như vậy, với script như trên thì công việc của nó là mô phỏng 10 users cùng truy cập liên tục vào trang http://test.k6.io trong vòng 30s, có nghỉ 1ms giữa 2 lần liên tiếp của mỗi user nhờ câu lệnh sleep(1).

Có một options nữa cũng rất hay dùng đó là iterations, nó được hiểu là tổng số lần các VUs thực thi default function.

export const options = {
  vus: 10,
  iterations: 100,
};

Ví dụ với options trên, 10 VUs sẽ chia nhau chạy sao cho đủ 100 lần công việc trong default function là kết thúc test. Chú ý là thời gian hoàn thành mỗi vòng lặp của mỗi VUs có thể khác nhau nên không có nghĩa là một VUs sẽ chạy đúng 10 vòng lặp.

Có một câu hỏi thú vị mà mình muốn chia sẻ với các bạn ở phần này là:

Có thể có tối đa bao nhiêu Virtual Users cho mỗi test script như thế này?

Câu trả lời là tùy thuộc vào phần cứng mà script này đang hoạt động trên đó nhé. Mỗi scripts đơn giản chỉ ngốn tầm 1MB memory, trong khi các script khác khi phức tạp có thể ngốn đến nhiều hơn 5MB memory cho mỗi VUs. Do đó, nếu chọn một con số VUs vượt quá khả năng đáp ứng của phần cứng máy, cụ thể đây mình cho chạy 10000 VUs với script trên ở local thì máy mình sẽ bị chiếm hết 100% CPU, 99% RAM cùng một loạt warning như sau.

Upload image

Trên thực tế, trong quá trình chạy test nếu bạn gặp phải những warning hay error, nó có thể đến từ nhiều nguyên nhân khác nhau (do phần cứng, network, hay môi trường test,…). Việc này làm giảm đi tính chân thực của kết quả test, cũng như tiêu tốn những công sức và tài nguyên không cần thiết. Do đó, người ta khuyên chúng ta nên làm smoke test trước để kiểm tra tính đúng đắn của test script và hệ thống trên một lượng nhỏ VUs, qua đó ước lượng và tiến đến làm những bài test tiếp theo như load test hay stress test.

Ngoài ra, k6 có ra một dịch vụ gọi là k6 Cloud – một phiên bản trả phí của k6 nhưng cũng giới hạn một vài tính năng miễn phí. Dịch vụ này phù hợp cho những dự án enterprise, cần thực thi test phức tạp với số lượng lớn VUs cùng nhiều thứ khác nữa. Đó chính là cái Cloud mình nói tới ở đầu bài.

Ok, một phút quảng cáo đã hết, chúng ta cùng tiếp tục vào vấn đề chính là khám phá cái test output thôi.

Result output - Kết quả đầu ra

Trước tiên, chúng ta xem thử các con số trong test result này được sinh ra như thế nào nhé.

k6 tạo tải cho web của bạn, sau đó, nó đo lường kết quả của hệ thống theo thời gian thực (real-time) dựa vào những kết quả trả về, do đó đa số các số liệu trong report ở dạng thống kê, đồ thị sẽ thay đổi theo thời gian. Report mà chúng ta thấy ở console chính là số liệu đo ở thời điểm vừa kết thúc thử nghiệm (end-of-test summary), cùng với các thông số đặc trưng của bên xác suất thống kê như:

  • Average (avg): giá trị trung bình
  • Minimum (min): giá trị nhỏ nhất
  • Maximum (max): giá trị lớn nhất
  • Medium (med): trung vị, tức là giá trị ở giữa sau khi sắp xếp các kết quả lại
  • Percentiles (p): bách phân vị, ví dụ như http_req_duration có thông số p(90)=384.33ms thì có nghĩa là có 90% các request có duration nhỏ hơn 384.33ms

Nếu muốn, các bạn cũng có thể thử cài và tìm cách xem những phiên bản report có màu mè hay đồ thị ở link này.

Giờ thì hãy cùng nhau tìm hiểu các thành phần của test output này nhé.

Upload image

Các thành phần trong đó là:

  • Execution: môi trường mà test đang thực thi, nó có thể là local hoặc cloud, ở đây ta đang chạy ở local.

  • Script: tên của file script.

  • Output: chính là output cho cả quá trình test, được đưa vào file .csv hoặc .json. Một lưu ý nhỏ là file output này là cho cả quá trình test, không phải là lúc vừa test xong nên có thể sẽ hơi dài nha.
    Command để xuất output là:

    k6 run --out json=test_results.json script.js
    

    hoặc

    k6 run --out csv=test_results.csv script.js
    

    Mọi người có thể tham khảo thêm ở đây: CSV JSON

Còn lại là các thông số để theo dõi và đánh giá kết quả của quá trình test, các bạn có thể tham khảo chi tiết ý nghĩa của từng loại tại đây.

3. k6 test lifecycle

Từ test script đơn giản ở phần trước, chúng ta thấy là có một options và một default functions định nghĩa hành động test. Vậy ngoài hai phần trên thì một test script có thể có những thành phần nào? Những thành phần đó sẽ đóng vai trò gì trong quá trình chạy test? Hãy cùng nhau tìm hiểu về vòng đời của một k6 test để trả lời những câu hỏi trên nhé.

Test script của k6 gồm bốn thành phần: init (bắt buộc), setup, VU code (bắt buộc) và teardown.

// 1. init code
export function setup() {
  // 2. setup code
  const data = '';
  return data;
}

export default function (data) {
  // 3. VU code
}

export function teardown(data) {
  // 4. teardown code
}
Thành phầnMục đíchVí dụSố lần gọi
init (required)Import modules, load files, định nghĩa options, khai báo các hàm (hàm tự định nghĩa hoặc các hàm đặc biệt như handleSummary())Import thư viện, khai báo options.1 lần cho mỗi VUs
setup (optional)Setup, cung cấp data cho tất cả VUsLấy dữ liệu từ một trang web khác về để cho vào body trong http request ở VUs code.1 lần cho cả quá trình test
VU code (required)Mô phỏng công việc của từng VUGửi http requests, validate responseTùy thuộc vào options
teardown (optional)Hậu xử lý data của setup, dừng các test environmentValidate kết quả của setup, gửi thông tin rằng test đã hoàn thành.1 lần cho cả quá trình test

Như vậy, đầu tiên k6 sẽ khởi tạo tất cả VUs với init code, sau đó trước khi thực thi VU code, k6 nhờ setup code chuẩn bị data để dùng trong các VUs, cuối cùng sau khi đã chạy xong tất cả các iterations hay kết thúc duration, teardown code sẽ chịu trách nhiệm xử lý data (postprocess) trước khi kết thúc test.

4. Tạm kết

Tới đây thì bài viết cũng khá dài rồi và chúng ta vừa làm quen cũng như biết được cấu trúc cơ bản của k6. Tuy nhiên, để làm việc với k6 thì vẫn cần phải biết thêm một vài khái niệm khác như checks, thresholds, scenarios,… Do đó, mình xin để những phần đó ở bài viết tiếp theo.

Hy vọng bài viết này sẽ giúp các bạn có thiện cảm và một khởi đầu thuận lợi với tool k6.

Tham khảo Thư Viện k6

Atekco - Home for Authentic Technical Consultants