Logo

Personal Ops Runbook

Personal runbook covering infrastructure operations for Cloud, Kubernetes, OpenStack, and Ceph environments. Includes deployment and teardown procedures, node management, cluster monitoring setup, and incident response workflows compiled from day-to-day operational work. Intended strictly for personal reference — configurations and scripts are environment-specific and not guaranteed to work as-is elsewhere.

1. Tổng quan

Bài viết này giải thích cách Nginx phân bổ request khi dùng weight trong upstream, áp dụng trực tiếp cho cấu hình bạn đưa ra. Ta sẽ đi từ nguyên lý → tính toán tỷ lệ → sơ đồ logic/vật lý → ví dụ → các yếu tố làm kết quả “lệch” so với lý thuyết → lời khuyên thực hành → kết luận.

2. Cách Nginx phân bổ với weight

  • Mặc định, Nginx dùng round-robin có trọng số: mỗi server có weight (mặc định = 1).
  • Xác suất nhận request xấp xỉ tỉ lệ thuận với weight của server đó, giả sử tất cả đều “up” và không phải retry.
  • Port khác nhau vẫn được coi là server khác nhau trong cùng upstream.

3. Tính tỷ lệ cho cấu hình của bạn

Cấu hình:

upstream backend_hcm03_vstorage_vngcloud_vn {
    server 10.166.23.8:8040 weight=8;
    server 10.166.23.9:8040 weight=5;
    server 10.166.23.5:8030;   # mặc định weight=1
    server 10.166.23.6:8030;   # mặc định weight=1
    server 10.166.23.7:8030;   # mặc định weight=1
}

Tổng trọng số = 8 + 5 + 1 + 1 + 1 = 16

Tỷ lệ lý thuyết:

  • 10.166.23.8:8040 → 8/16 = 50%
  • 10.166.23.9:8040 → 5/16 ≈ 31.25%
  • 10.166.23.5:8030 → 1/16 ≈ 6.25%
  • 10.166.23.6:8030 → 1/16 ≈ 6.25%
  • 10.166.23.7:8030 → 1/16 ≈ 6.25%

Khi 1 server “down”, phần trăm còn lại sẽ được phân phối lại cho các server “up” theo trọng số của chúng.

4. Sơ đồ logic và vật lý

4.1. Sơ đồ logic phân phối (round-robin có weight)

Client -> Nginx (proxy)
          |
          +--> [S1 10.166.23.8:8040] (w=8)  ~~~ 50%
          +--> [S2 10.166.23.9:8040] (w=5)  ~~~ 31.25%
          +--> [S3 10.166.23.5:8030] (w=1)  ~~~ 6.25%
          +--> [S4 10.166.23.6:8030] (w=1)  ~~~ 6.25%
          +--> [S5 10.166.23.7:8030] (w=1)  ~~~ 6.25%

4.2. Sơ đồ vật lý (các đích upstream trên 2 port khác nhau)

        +--------------------+
        |     Nginx LB       |
        +--------------------+
           |   |   |   |   |
           |   |   |   |   +----> 10.166.23.7:8030
           |   |   |   +--------> 10.166.23.6:8030
           |   |   +------------> 10.166.23.5:8030
           |   +----------------> 10.166.23.9:8040
           +--------------------> 10.166.23.8:8040

5. Một vài ví dụ ngắn

5.1. Ví dụ đổi trọng số để “dồn tải”

server 10.166.23.8:8040 weight=12;
server 10.166.23.9:8040 weight=3;
server 10.166.23.5:8030 weight=1;
server 10.166.23.6:8030 weight=1;
server 10.166.23.7:8030 weight=1;
# Tổng = 18  -> ~66.7%, 16.7%, 5.6%, 5.6%, 5.6%

5.2. Ví dụ “backup” để chỉ nhận khi các server chính lỗi

server 10.166.23.8:8040 weight=8;
server 10.166.23.9:8040 weight=5;
server 10.166.23.5:8030 backup;
server 10.166.23.6:8030 backup;
server 10.166.23.7:8030 backup;
# S3–S5 chỉ nhận lưu lượng khi S1/S2 không phục vụ được.

5.3. Ví dụ “down” tạm thời loại khỏi vòng

server 10.166.23.7:8030 down;
# Nginx bỏ qua S5, tỉ lệ phân bổ tính lại trên 4 server còn lại.

6. Những yếu tố làm tỷ lệ thực tế khác lý thuyết

  • Kết nối keepalive / HTTP/1.1: Một client với ít kết nối dài có thể “dính” mãi vào một server → số request quan sát được lệch.
  • Kích thước/độ nặng request: weight chỉ cân theo số request, không cân theo CPU/RAM/thời gian xử lý. Một server có thể nhận 31% request nhưng gánh >50% CPU nếu các request “nặng”.
  • Retry / proxy_next_upstream: Khi upstream trả lỗi/timed out, Nginx có thể gửi lại sang server khác → làm lệch tỉ lệ.
  • Health check: Bản OSS có passive health check qua max_failsfail_timeout; active health check đầy đủ cần NGINX Plus hoặc module ngoài. Khi 1 server bị coi là “down”, lưu lượng dồn sang các server “up”.
  • Thuật toán khác:

  • least_conn ưu tiên server có ít kết nối đang mở (vẫn hỗ trợ weight).

  • ip_hash bỏ qua weight để cố định client→server (thường dùng cho sticky session).
  • Gộp port: Port khác nhau không ảnh hưởng thuật toán, nhưng khác port thường đồng nghĩa khác dịch vụ/cấu hình backend, có thể có hiệu năng khác nhau.

7. Biến thể thuật toán ngắn gọn

  • Mặc định (round-robin có weight): phù hợp khi các request tương đối đồng đều.
  • least_conn (ít kết nối nhất): hợp khi request kéo dài/không đều; thêm least_conn; trong upstream và vẫn dùng weight để “bẻ lái” tỉ lệ.
  • ip_hash: giữ phiên bám theo IP client; không dùng được weight có ý nghĩa phân bổ.

8. Lời khuyên

  • Chọn weight theo năng lực thực tế (CPU, số worker, I/O). Ví dụ: máy 16 vCPU đặt weight ≈ gấp đôi máy 8 vCPU.
  • Nếu tải/độ dài request không đều, cân nhắc least_conn; thay vì round-robin thuần.
  • Dùng max_failsfail_timeout để loại server “có vấn đề” nhanh hơn:

nginx server 10.166.23.8:8040 weight=8 max_fails=3 fail_timeout=10s; * Muốn “đánh thức” server mới thêm dần tải, hãy tăng weight theo từng bước nhỏ thay vì nhảy số lớn ngay. * Theo dõi bằng log/người quan sát ở Nginx và backend (status, metrics) để so sánh tỷ lệ lý thuyết vs thực tế; nếu lệch nhiều, kiểm tra keepalive, retry, lỗi 5xx/timeout. * Với nhu cầu sticky session thực sự, dùng ip_hash hoặc giải pháp sticky ở layer ứng dụng; chấp nhận từ bỏ lợi ích của weight. * Tách pool theo đặc thù port/dịch vụ nếu hành vi và tài nguyên khác biệt, rồi dùng map hoặc location để định tuyến vào pool phù hợp.

9. Kết luận

weight trong upstream giúp bạn phân bổ request theo tỉ lệ mong muốn, với điều kiện mọi server hoạt động bình thường. Với cấu hình của bạn, tỉ lệ lý thuyết là 50% / 31.25% / 6.25% / 6.25% / 6.25%. Tuy nhiên, thực tế có thể lệch do keepalive, retry, sức nặng request, health check… Vì vậy, hãy đo đạc, chỉnh weight theo năng lực thật của backend, và cân nhắc least_conn hoặc cơ chế sticky phù hợp với mô hình lưu lượng của bạn.