“Điểm mù” của hạ tầng
Tôi đã chứng kiến nhiều đội ngũ kỹ thuật dành hàng tuần để chau chuốt unit test, nhưng rồi hạ tầng lại sụp đổ ngay khi một đợt chiến dịch marketing mang về 2.000 người dùng. Đó là một cơn ác mộng lặp đi lặp lại: logic chức năng hoàn hảo, nhưng database connection pool lại “nghẽn” ở mức 500 request đồng thời. Theo truyền thống, load testing thường là một quy trình thủ công do đội QA thực hiện mỗi quý một lần. Đến khi phát hiện ra điểm nghẽn, mã nguồn đã thay đổi quá nhiều khiến việc sửa lỗi tiêu tốn tới 100 giờ refactor đắt đỏ.
Coi hiệu năng là việc “để sau rồi tính” sẽ tạo ra nợ kỹ thuật (technical debt) khổng lồ. Nếu một commit mới làm tăng 300ms độ trễ cho luồng thanh toán, bạn cần biết điều đó sau vài phút chứ không phải vài tuần. Đây chính là cốt lõi của “Shift-Left” performance testing. Bằng cách nhúng load test vào pipeline CI/CD, hiệu năng trở thành yếu tố bắt buộc giống như bảo mật hay kiểm tra build.
Tại sao k6 chiếm ưu thế trong kỹ thuật hiện đại
Việc lựa chọn công cụ thường phụ thuộc vào sự tiện dụng. Trong khi JMeter và Gatling là những cái tên kỳ cựu, giao diện nặng nề XML của JMeter mang lại cảm giác lỗi thời từ năm 2005, còn Gatling lại buộc lập trình viên phải bước vào hệ sinh thái Scala. k6 đã xóa bỏ rào cản này. Nó sử dụng JavaScript tiêu chuẩn để viết kịch bản, đồng thời tận dụng engine viết bằng Go để xử lý thực thi cường độ cao mà không làm quá tải CPU máy cục bộ.
Sức mạnh thực sự cho CI/CD nằm ở tính năng “Thresholds” của k6. Trong một bài test thủ công, bạn có thể quan sát dashboard Grafana và hy vọng mọi thứ ổn. Nhưng các pipeline cần một kết quả đạt/thất bại (pass/fail) rõ ràng. k6 cho phép bạn định nghĩa các tiêu chí khắt khe—ví dụ: “độ trễ ở phân vị thứ 95 (p95) phải dưới 400ms”—và trả về exit code khác 0 để dừng build nếu hiệu năng sụt giảm.
Các chỉ số cốt lõi cần theo dõi
- VUs (Virtual Users): Số lượng luồng đồng thời mô phỏng hành vi người dùng.
- Iteration: Mỗi lần một VU hoàn thành toàn bộ kịch bản test.
- http_req_duration: Tổng thời gian máy chủ phản hồi. Đây là chỉ số sức khỏe quan trọng nhất.
- http_req_failed: Tỷ lệ phần trăm các request trả về lỗi 4xx hoặc 5xx.
Xây dựng kịch bản k6 đầu tiên
Hãy bắt đầu từ quy mô nhỏ. Hãy nhắm mục tiêu vào môi trường staging hoặc container cục bộ trước. Chạy load test trực tiếp vào trang production mà không báo trước là con đường ngắn nhất dẫn đến sự cố on-call lúc 3 giờ sáng. Đầu tiên, hãy cài đặt k6 trên máy của bạn.
# Trên macOS
brew install k6
# Trên Linux (Debian/Ubuntu)
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/apt/sources.list.d/k6.list
sudo apt-get update
sudo apt-get install k6
Bây giờ, hãy tạo file load-test.js. Kịch bản này mô phỏng hành trình thực tế của người dùng: tăng dần từ 0 lên 50 người dùng ảo trong 30 giây, duy trì mức đỉnh đó trong hai phút, sau đó giảm dần về 0.
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
stages: [
{ duration: '30s', target: 50 }, // tăng dần lên 50 người dùng
{ duration: '2m', target: 50 }, // duy trì tải
{ duration: '20s', target: 0 }, // giảm dần về 0
],
thresholds: {
'http_req_duration': ['p(95)<400'], // 95% request phải có độ trễ dưới 400ms
'http_req_failed': ['rate<0.01'], // tỷ lệ lỗi phải dưới 1%
},
};
export default function () {
const res = http.get('https://staging-api.example.com/v1/products');
check(res, {
'trạng thái là 200': (r) => r.status === 200,
});
sleep(1);
}
Khối thresholds đóng vai trò như một người gác cổng tự động. Tôi đã thấy thiết lập này phát hiện ra vấn đề khóa database (database locking) chỉ xuất hiện khi có hơn 10 tác vụ ghi đồng thời. Nó là một tấm lưới an toàn giúp ngăn chặn mã nguồn chưa tối ưu tiếp cận trình duyệt của người dùng.
Tích hợp k6 vào GitHub Actions
Tự động hóa biến một lần kiểm tra đơn lẻ thành tiêu chuẩn của dự án. Hầu hết các nền tảng CI/CD đều hỗ trợ k6, nhưng GitHub Actions đặc biệt đơn giản. Hãy tạo một workflow tại .github/workflows/performance.yml để kích hoạt mỗi khi có pull request.
name: Kiểm tra lỗi hiệu năng (Performance Regression)
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
k6_load_test:
name: Chạy k6 Load Test
runs-on: ubuntu-latest
steps:
- name: Kiểm tra mã nguồn (Checkout)
uses: actions/checkout@v3
- name: Chạy test k6
uses: grafana/[email protected]
with:
filename: load-test.js
flags: --tag testid=github-actions-run
Với file YAML này, mỗi PR sẽ phải trải qua một đợt kiểm tra hiệu năng. Nếu một dependency mới hoặc một câu lệnh SQL phức tạp đẩy thời gian phản hồi p95 lên trên 400ms, GitHub Action sẽ thất bại. Điều này buộc các lập trình viên phải tối ưu mã nguồn trước khi có thể merge.
Phân tầng kiểm thử: Smoke, Load và Stress Test
Chạy một bài stress test lớn cho mỗi commit là việc làm tốn kém và chậm chạp. Hãy phân tầng chiến lược để giữ cho vòng lặp phát triển luôn nhanh chóng.
1. Smoke Test
Chạy bài test này trên mỗi commit. Nó sử dụng 2 người dùng ảo trong 60 giây để đảm bảo các endpoint /health và /api/v1/status không gặp lỗi 500. Đây là một bước kiểm tra nhanh tình trạng hệ thống.
2. Load Test
Thực hiện bài test này cho mỗi PR vào nhánh main. Nó mô phỏng mức tải đỉnh điểm hàng ngày thông thường—có lẽ khoảng 200 người dùng đồng thời. Điều này giúp phát hiện sự sụt giảm hiệu năng âm thầm tích tụ theo thời gian.
3. Stress Test
Kích hoạt bài test này trước các sự kiện lớn, chẳng hạn như ra mắt sản phẩm. Đẩy hệ thống lên 1.000% công suất bình thường để tìm ra điểm gãy. Load balancer thất bại trước, hay CPU của database chạm mức 100%? Việc biết được những giới hạn này giúp bạn không hoảng loạn khi đối mặt với lưu lượng truy cập thực tế tăng đột biến.
Giải mã dữ liệu
Khi pipeline thất bại, đừng hoảng loạn. k6 cung cấp một bản tóm tắt rõ ràng trong logs. Hãy kiểm tra các chỉ số http_req_duration trước tiên. Chỉ số p(99) cao thường chỉ ra các trường hợp biên (edge case) cụ thể hoặc các vòng lặp chưa tối ưu chỉ bị kích hoạt khi có tải lớn.
Để theo dõi dài hạn, hãy đẩy kết quả k6 vào Prometheus hoặc Grafana Cloud. Việc so sánh bản build hôm nay với bản build từ ba tháng trước sẽ tiết lộ ứng dụng của bạn đang nhanh hơn hay đang dần trở nên nặng nề.
Lời kết
Tự động hóa kiểm thử hiệu năng giúp loại bỏ việc đoán mò khi triển khai. Bạn không còn phải vừa làm vừa cầu nguyện, hay nơm nớp theo dõi mức sử dụng CPU mỗi khi rollout. Bằng cách thiết lập các ngưỡng rõ ràng, bạn thúc đẩy một văn hóa nơi tốc độ là trách nhiệm chung của toàn bộ đội ngũ kỹ thuật.
Hãy dành 10 phút ngay hôm nay để viết một smoke test cho API endpoint “đắt đỏ” nhất của bạn. Một khi đội ngũ thấy được những chỉ số đó ngay trong PR của họ, hiệu năng sẽ không còn là một nhiệm vụ “để mai tính” mà trở thành một phần cốt lõi của quy trình làm việc.

