Làm chủ Load Test với k6: Từ script đơn giản đến tự động hóa CI/CD

Development tutorial - IT technology blog
Development tutorial - IT technology blog

Cú điện thoại lúc 2 giờ sáng và bài học đắt giá

Bạn đang ngủ say thì chuông điện thoại reo dồn dập. Dashboard báo đỏ rực: CPU server chạm đỉnh 100%, kết nối database bị nghẽn, và người dùng bắt đầu phàn nàn vì ứng dụng treo. Mắt nhắm mắt mở debug suốt 3 tiếng, bạn mới phát hiện ra thủ phạm là một API mới deploy hôm qua. Chỉ cần 50 người truy cập cùng lúc, toàn bộ hệ thống đã đổ vỡ hoàn toàn.

Đây không phải kịch bản phim kinh dị, mà là thực tế mình từng nếm trải. Sai lầm lớn nhất lúc đó là chỉ tập trung viết Unit Test cho logic mà bỏ quên kiểm thử hiệu năng (Load Testing). Để không lặp lại thảm cảnh đó, k6 chính là công cụ đắc lực mà mình muốn giới thiệu tới anh em.

Tại sao lại là k6?

Quên mấy cái UI cồng kềnh, nặng nề kiểu JMeter đi. k6 cho phép bạn viết kịch bản test bằng JavaScript. Điều này cực kỳ tiện lợi: bạn có thể quản lý code test ngay trong repository, thực hiện version control và tận dụng kỹ năng coding sẵn có.

1. Cài đặt k6 trong một nốt nhạc

Với anh em dùng Linux (Ubuntu/Debian), chỉ cần vài dòng lệnh quen thuộc:

sudo gpg -k
sudo gpg --no-default-keyring --keyring /usr/share/keyrings/k6-archive-keyring.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69
echo "deb [signed-by=/usr/share/keyrings/k6-archive-keyring.gpg] https://dl.k6.io/deb stable main" | sudo tee /etc/sources.list.d/k6.list
sudo apt-get update
sudo apt-get install k6

Dân chơi macOS thì còn nhanh hơn với Homebrew: brew install k6.

2. Script đầu tay: Xin chào API

Hãy thử tạo file test.js để gửi request vào API sản phẩm:

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

export default function () {
  http.get('https://api.example.com/products');
  sleep(1);
}

Chạy lệnh: k6 run test.js. Rất đơn giản, bạn vừa thực hiện 1 request mỗi giây. Nhưng thực tế thì hệ thống cần chịu tải phức tạp hơn thế nhiều.

Nâng cấp k6 thành “sát thủ” chịu tải

Để giả lập hàng ngàn người dùng ảo (Virtual Users – VUs) truy cập cùng lúc, chúng ta cần cấu hình Stages. Thay vì gõ tham số dòng lệnh dài dòng, mình hay đưa thẳng cấu hình vào code để dễ quản lý và tái sử dụng.

export const options = {
  stages: [
    { duration: '30s', target: 20 }, // Tăng dần lên 20 user trong 30 giây đầu (warm-up)
    { duration: '1m', target: 20 },  // Duy trì 20 user trong 1 phút để xem độ ổn định
    { duration: '10s', target: 0 },  // Giảm dần về 0 để kết thúc sạch sẽ
  ],
};

Cấu hình này giúp mô phỏng đúng hành vi thực tế: lượng truy cập thường tăng dần chứ ít khi ập đến đột ngột trong 1 giây.

Thiết lập ngưỡng an toàn (Checks và Thresholds)

Chỉ nhìn màn hình hiện chữ “Finished” là chưa đủ. Bạn cần biết API có trả về mã 200 không, hay response time có đạt yêu cầu kinh doanh.

import { check } from 'k6';

let res = http.get('https://api.example.com/products');
check(res, {
  'status is 200': (r) => r.status === 200,
  'p95 response time < 500ms': (r) => r.timings.duration < 500,
});

Đặc biệt quan trọng là Thresholds. Bạn có thể quy định: nếu 10% request thất bại hoặc 95% request (p95) chậm hơn 1 giây, k6 sẽ đánh dấu test fail. Đây là chốt chặn cuối cùng ngăn code lỗi lên production.

Giả lập kịch bản thực tế: Auth và Data Flow

Hầu hết API hiện nay đều yêu cầu đăng nhập. Dưới đây là cách mình xử lý luồng login lấy token rồi mới truy cập dữ liệu:

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

export default function () {
  const loginRes = http.post('https://api.example.com/login', {
    username: 'admin',
    password: 'password123',
  });
  const token = loginRes.json('token');

  const params = { headers: { Authorization: `Bearer ${token}` } };
  const res = http.get('https://api.example.com/private/data', params);
  
  check(res, { 'is status 200': (r) => r.status === 200 });

  // Giả lập user dừng lại đọc nội dung khoảng 1-4 giây
  sleep(Math.random() * 3 + 1); 
}

Mẹo nhỏ: Khi xử lý JSON phức tạp hoặc cần format dữ liệu nhanh để viết script, mình thường dùng toolcraft.app. Nó giúp tiết kiệm thời gian hơn nhiều so với việc loay hoay cài extension vào VS Code.

Đưa Load Test vào dây chuyền CI/CD

Đừng để đến lúc rảnh mới chạy test. Hãy tích hợp nó vào GitHub Actions để mỗi lần push code, hệ thống tự động kiểm tra hiệu năng. Nếu code mới làm API chậm đi, build sẽ fail ngay lập tức.

name: Performance Check
on: [push]
jobs:
  k6_test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Run k6
        uses: grafana/[email protected]
        with:
          filename: test.js

Kinh nghiệm thực chiến khi làm Load Test

  • Tuyệt đối không test trên Production: Dù tự tin đến đâu, hãy dựng môi trường Staging. Bạn không muốn vô tình tự ddos hệ thống của chính mình đâu.
  • Soi kỹ Database: Code API có thể tối ưu, nhưng 90% nút thắt nằm ở các câu query thiếu index. Hãy bật log slow query khi chạy k6 để tìm ra thủ phạm.
  • Dữ liệu phải thật: Đừng test mãi một user duy nhất. Hãy dùng file CSV chứa danh sách 1.000 user khác nhau để tránh việc database cache kết quả, gây ra số liệu ảo.
  • Quan sát tỷ lệ lỗi (Error Rate): Đôi khi hệ thống trả về HTTP 200 nhưng body lại báo lỗi logic. Phải check kỹ nội dung trả về để đảm bảo tính chính xác.

Chỉ với 30 phút setup k6, bạn có thể yên tâm kê gối ngủ ngon. Không còn nỗi lo bị dựng dậy lúc nửa đêm, không còn những pha sập server bất ngờ. Chúc anh em xây dựng được những hệ thống thực sự mạnh mẽ!

Share: