Benchmark Scale Dọc hay Ngang: Lựa chọn nào thực tế cho Production?

Một task thực tế mà thấy có thể giúp được nhiều bạn cùng câu hỏi nên noted lại viết bài, lâu lắm cũng không làm benchmark 😀

Anh em trong team lâu lâu than: “Sao API lúc nhanh lúc chậm anh nhỉ?”. Đối tác thì gửi log: “Thỉnh thoảng bị timeout”. Grafana không hề báo đỏ, nhưng p95, p99 cứ nhích lên đều đều, nhất là vào giờ cao điểm.

Kịch bản thì cũng khá quen thuộc: đa số request vẫn ổn, chỉ vài request bị kéo dài thời gian xử lý. Và chính vài request đó mới làm người dùng khó chịu, tỷ lệ retry bắt đầu tăng, rồi dẫn đến timeout lác đác. Theo kinh nghiệm mình tiến hành scale thử.

019cb4a1-a5c4-71cf-9205-72dee9184db0

Tới đây thì chúng ta thường có 2 lựa chọn quen thuộc:

  • Scale dọc: Nâng cấp cấu hình một instance (ví dụ: từ 2 vCPU lên 4 rồi 8 vCPU).
  • Scale ngang: Chia nhỏ tải ra nhiều instance (ví dụ: từ 1 lên 2, 4, 8…), đặt sau Load Balancer.

Đầu năm, xử lý những task còn dang dở nên theo dõi nhiều bác chia sẻ nên thuận tay benchmark luôn… (bài viết có tham khảo các format viết của các tác giả khác trên devops.vn nha)

Lưu ý nhỏ: Số liệu dưới đây dựa trên môi trường lab, thực tế của các bạn có thể sẽ khác. Tuy nhiên, xu hướng thường khá giống nếu chúng ta thực hiện đúng phương pháp và không dính những giới hạn từ hệ điều hành.

Bài toán mình đặt ra

Mình cũng từng thấy nhiều bài benchmark kiểu backend trả về OK siêu nhanh, chạy chung trên một máy và tắt hết các tác vụ nặng, xong kết luận cái này hơn cái kia gấp đôi. Kết quả đó nhìn cho vui chứ nhưng đem ra thực tế thì không áp dụng được mấy.

Vì vậy, mình chọn loại request mà các service thực tế thường xử lý:

  • JSON nhỏ, request/response tầm 1 tới 3 KB.
  • Ưu tiên sử dụng Keep-alive là chính.
  • App có xử lý thật: parse JSON, kiểm tra auth nhẹ, marshal response.
  • Có một nhịp gọi ra ngoài (hit Redis cache hoặc query DB rất nhẹ) để mô phỏng thực tế nhưng vẫn tập trung vào việc scale app.

Mình quan tâm bộ ba chỉ số rõ ràng: (RPS)[https://devops.vn/posts/rps-la-gi-trong-devops/], (error Rate)[https://devops.vn/posts/error-rate-la-gi-doc-trong-he-thong-web/], và đặc biệt là p95/p99. RPS đẹp mà p99 xấu hoặc error tăng thì kết quả đó coi như không đạt yêu cầu.

Để các bạn dễ hình dung về p99: nếu có 10,000 request thì cỡ 100 request nằm ở phần đuôi p99. Không nhiều, nhưng đủ làm người dùng nhớ lâu.

Setup môi trường

Mình tách 3 khối để tránh tình trạng kết quả bị ảnh hưởng do chạy chung (loopback):

  • Loadgen: Dùng wrk2 để bắn tải ổn định.
  • Load Balancer: Mình dùng HAProxy.
  • App instances: Các instance cấu hình giống hệt nhau.

Tất cả nằm trong cùng mạng nội bộ (LAN/VPC) để đảm bảo latency nội bộ dưới 1ms, tập trung đo chiến lược scale thay vì độ trễ đường truyền internet.

Nguyên tắc để có số liệu khách quan

Để tránh số liệu đẹp ảo, mình tuân thủ theo rule sau:

  • Tắt hoặc giảm tối thiểu access log (log quá nhiều sẽ gây nghẽn CPU khi benchmark)
  • Tăng ulimit -n, đảm bảo backlog đủ để không bị giới hạn bởi hệ điều hành.
  • Chạy nháp riêng, chỉ lấy số liệu khi hệ thống đã đi vào trạng thái ổn định
  • Nếu thấy latency đẹp mà error hoặc retry tăng thì coi như kết quả sai, phải xem lại.

Hai hướng mình đem ra so sánh

Scale dọc

  • 1 instance 2 vCPU
  • 1 instance 4 vCPU
  • 1 instance 8 vCPU

Scale ngang

  • 2 instances, mỗi instance 2 vCPU
  • 4 instances, mỗi instance 2 vCPU
  • 8 instances, mỗi instance 2 vCPU

Mình cân đối theo tổng vCPU ở các cặp chính, kiểu 1 con 8 vCPU so với 4 con 2 vCPU, vì khi tính toán chi phí nhiều người cũng cân nhắc như vậy. Case 8 con 2 vCPU mình giữ lại để xem chi phí overhead khi scale ngang lên nhiều, không coi nó là kèo cân bằng vCPU với mức 8 vCPU.

Kịch bản chạy

Steady load (Tải ổn định)

  • Tăng dần RPS tới khi chạm ngưỡng ổn định.
  • Warm up 15s.
  • Đo trong 60s.
  • Thu thập p95, p99, error rate, CPU, memory.

Mỗi cấu hình chạy 3 lần, lấy giá trị trung vị cho khách quan.

Spike (Tải tăng đột biến)

  • Baseline 8k RPS.
  • Đẩy vọt lên 25k RPS trong 60s.
  • Quan sát p99 phình ra thế nào và khả năng hồi phục ra sao.

Kết quả steady load

Mình chốt ngưỡng ổn định theo tiêu chí: p99 dưới 100 ms, error dưới 0.5%.

Scale dọc

Cấu hình Max stable RPS p95 p99 CPU app Ghi chú
1 instance 2 vCPU ~ 6.5k ~ 18 ms ~ 85 ms ~ 90 – 95% chạm trần CPU sớm
1 instance 4 vCPU ~ 12k ~ 16 ms ~ 78 ms ~ 85 – 90% hợp cho load vừa
1 instance 8 vCPU ~ 21k ~ 14 ms ~ 70 ms ~ 80 – 88% p99 ổn hơn, ít biến động

Scale ngang

Cấu hình Max stable RPS p95 p99 CPU tổng app CPU load balancer Ghi chú
2 instances 2 vCPU ~ 11k ~ 17 ms ~ 82 ms ~ 170 – 185% ~ 10 – 15% gần với 1 instance 4 vCPU
4 instances 2 vCPU ~ 20k ~ 16 ms ~ 76 ms ~ 340 – 380% ~ 18 – 25% gần với 1 instance 8 vCPU
8 instances 2 vCPU ~ 30k ~ 18 ms ~ 95 ms ~ 650 – 720% ~ 35 – 45% bắt đầu lộ overhead

CPU tổng mình cộng theo kiểu 100% tương đương 1 core bận. Nên ~ 650% nghĩa là tầm 6.5 core đang hoạt động.

Nhìn bảng steady load này mình thấy hai điểm khá rõ ràng:

  • Khi tổng tài nguyên compute tương đương, scale dọc và scale ngang có kết quả khá sát nhau, không phải cứ tăng số lượng instance là tự động tốt hơn.
  • Scale ngang lên nhiều con thì gánh nặng quản lý xuất hiện trước tiên ở p99 do load balancer xử lý nhiều hơn, nhiều kết nối hơn.

Spike test

Case A: 1 instance 8 vCPU

  • CPU app lên ~ 95 – 100% khá nhanh.
  • p99 vọt lên 180 tới 320 ms do phải xếp hàng chờ (queueing).
  • Error rate 0.8 tới 1.8%, có timeout rải rác.
  • Hết spike hồi phục nhanh, nhưng trong đoạn spike thì người dùng thấy rõ sự chậm trễ.

Case B: 4 instances 2 vCPU cố định, không autoscale

  • Tổng tài nguyên tương đương 8 vCPU.
  • p99 khoảng 160 tới 280 ms.
  • Error rate 0.5 tới 1.2%.

Case C: Autoscale từ 4 lên 8 instances 2 vCPU

  • Thời gian scale và sẵn sàng giả định 30 tới 60 giây.
  • 20 tới 30 giây đầu kết quả vẫn xấu vì chưa kịp tăng số lượng instance.
  • Khi tăng xong, p99 về khoảng 80 tới 120 ms, lỗi gần như bằng 0.

Đoạn này chốt một ý rất thực tế: autoscale chỉ phát huy tác dụng khi spike đủ dài để hệ thống kịp tăng số lượng instance và ổn định trở lại. Nếu spike quá ngắn thì vẫn sẽ có một khoảng thời gian hiệu năng không tốt, khó tránh được.

Những lỗi sai mình từng dính khi thực hiện benchmark

Nói đúng ra lần đầu mình thực hiện cũng fail ở vài điểm, ghi ra đây để các bạn tránh những sai số không đáng có nhé, một cách hiệu quả để tiết kiệm đấy :)):

  1. Quên tắt access log, dẫn đến số liệu tụt, CPU tăng, mình lại tưởng app yếu.
  2. Test backend quá đơn giản, kết quả đẹp một cách vô lý, nhìn là biết không thực tế.
  3. Không phân biệt giữa keep aliveshort connection, chỉ cần client thay đổi kịch bản làm kết quả đảo chiều hoàn toàn.
  4. Quên tăng ulimit -n, khi số lượng concurrency tăng là lỗi connect xuất hiện, suýt nữa mình đổ thừa cho hệ thống không chịu nổi tải.

Benchmark không khó. Khó là làm sao để kết quả benchmark phản ánh sát thực tế.

Những yếu tố làm thay đổi kết luận khi áp dụng vào production

Scale ngang mà không chú ý những điểm này là rất dễ gặp rắc rối:

  • Connection storm về DB hoặc Redis: tăng số instance là tăng số lượng kết nối. Nếu không có pooling và throttling tốt, điểm nghẽn sẽ chuyển sang DB rất nhanh.
  • Cache duplication: nhiều instance dẫn đến nhiều cache in-memory. Nếu tỷ lệ hit cache là quan trọng thì đôi khi scale dọc lại có lợi vì cache tập trung.
  • Phân phối traffic không đều: do keep-alive hoặc cơ chế hashing bị lệch, khiến vài instance gánh nặng hơn phần còn lại, dẫn đến p99 xấu dù tổng CPU vẫn còn dư.
  • GC pause hoặc nghẽn thread pool: CPU chưa đầy nhưng p99 vẫn tăng cao, vì hệ thống đang bị nghẽn ở các thành phần khác.

Chốt lại để áp dụng

Tóm lại tùy bài toán mà chúng ta sẽ chọn gì theo điều kiện thực tế.

Ưu tiên scale dọc khi:

  • Hệ thống ở quy mô nhỏ đến vừa, traffic ổn định.
  • Muốn vận hành đơn giản, giảm thiểu các điểm có thể gây lỗi.
  • Chưa có hệ thống autoscale hoàn thiện hoặc chưa muốn tăng thêm độ phức tạp cho hệ thống.

Chuyển sang scale ngang khi:

  • Cần đảm bảo tính HA.
  • Các đợt spike traffic xảy ra thường xuyên.
  • Đã làm chủ load balancer, health check và autoscale tốt.
  • Đảm bảo connection pooling và cache đã được kiểm soát tốt để không đẩy gánh nặng sang DB.

Thông tin nổi bật

Sự kiện phát trực tiếp​

Event Thumbnail

Báo cáo quan trọng

Article Thumbnail
Article Thumbnail
Chia sẻ bài viết:
Theo dõi
Thông báo của
0 Góp ý
Được bỏ phiếu nhiều nhất
Mới nhất Cũ nhất
Phản hồi nội tuyến
Xem tất cả bình luận

Tiêu điểm chuyên gia