Khi ứng dụng của bạn phát triển và có lượng request lớn, việc sử dụng một server không thể đáp ứng đủ request của người dùng. Cho dù server có thể rất mạnh nhưng nếu server đó sập thì ứng dụng của chúng ta cũng sẽ die hoàn toàn. Load balancing là một kỹ thuật phân phối các request đến nhiều máy chủ khác nhau, giúp cải thiện hiệu suất và khả năng chịu tải của hệ thống. Trong bài viết này, chúng ta sẽ tìm hiểu cách cấu hình và thực hành load balancing với NGINX.
1. Cấu Hình Môi Trường
Chúng ta sẽ sử dụng Docker để chạy ba container Node.js, mỗi container là một phiên bản của ứng dụng Node.js. Còn NGINX sẽ làm nhiệm vụ phân phối các request từ người dùng đến các container này – NGINX chúng ta cũng sẽ chạy trong docker luôn nhé.
1.1 Tạo ứng dụng Node.js đơn giản
Đầu tiên, tạo thư mục nginx-loadbalancing
, từ giờ chúng ta sẽ làm việc bên trong thư mục này. Tạo file app.js
như sau:
const http = require('http');
const port = 3000;
const requestHandler = (req, res) => {
res.end(`Hello from ${process.env.HOSTNAME}!`);
};
const server = http.createServer(requestHandler);
server.listen(port, '0.0.0.0', () => {
console.log(`Server running at http://localhost:${port}`);
});
1.2 Dockerize ứng dụng Node.js
Tạo file Dockerfile
trong thư mục gốc:
FROM node:20-alpine
WORKDIR /app
COPY . .
EXPOSE 3000
CMD ["node", "app.js"]
1.3 Tạo Dockerfile cho NGINX
Tạo thư mục nginx
, tạo file Dockerfile
bên trong thư mục nginx với nội dung như sau:
FROM nginx:alpine
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
1.4 Cấu Hình NGINX
Tạo file nginx.conf
trong thư mục nginx
với nội dung đơn giản là load các request về 1 server ở đây mình sẽ load về app1 – Có nghĩa là ban đầu chúng ta chưa áp dụng load balacing.
events {
worker_connections 1024;
}
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://app1:3000; # load về app1
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
1.5 Tạo Docker Compose File
Tạo file docker-compose.yml
trong thư mục gốc để quản lý các container dễ dàng hơn:
version: '3'
services:
app1:
build: .
environment:
- HOSTNAME=app1
ports:
- "3001:3000"
app2:
build: .
environment:
- HOSTNAME=app2
ports:
- "3002:3000"
app3:
build: .
environment:
- HOSTNAME=app3
ports:
- "3003:3000"
nginx:
build: ./nginx
ports:
- "80:80"
depends_on:
- app1
- app2
- app3
Ở đây, chúng ta chạy ba container http serrver node.js trên b3 cổng khác nhau: 3001, 3002 và 3003.
2. Tìm hiểu cấu hình NGINX cho các loại Load Balancing
Chúng ta sẽ cấu hình NGINX để làm nhiệm vụ phân phối tải giữa các container Node.js. Cấu hình NGINX sẽ phụ thuộc vào các thuật toán load balancing mà bạn muốn sử dụng.
1. Round Robin (Mặc Định)
Round Robin là thuật toán phân phối request đều đến các máy chủ backend. Đây là thuật toán mặc định của NGINX và rất đơn giản để cấu hình.
events {
worker_connections 1024;
}
http {
upstream app_servers {
server app1:3000;
server app2:3000;
server app3:3000;
}
server {
listen 80;
location / {
proxy_pass http://app_servers;
}
}
}
Khi sử dụng Round Robin, các request sẽ được phân phối theo chu kỳ giữa các server:
- request 1 → app1
- request 2 → app2
- request 3 → app3
- request 4 → app1
- request 5 → app2
- request 6 → app3
- …
2. Least Connections
Least Connections phân phối request đến server có ít kết nối nhất tại thời điểm đó.
events {
worker_connections 1024;
}
http {
upstream app_servers {
least_conn;
server app1:3000;
server app2:3000;
server app3:3000;
}
server {
listen 80;
location / {
proxy_pass http://app_servers;
}
}
}
Trong trường hợp này, khi có request đến, NGINX sẽ chọn server có ít kết nối hiện tại để xử lý request, giúp tránh tình trạng quá tải ở các server.
3. IP Hash
IP Hash là thuật toán phân phối request dựa trên địa chỉ IP của người dùng. Mỗi địa chỉ IP sẽ được ánh xạ đến một máy chủ cụ thể, giúp duy trì trạng thái phiên làm việc của người dùng – hiểu nhanh thì thằng này sẽ duy trì 1 client với 1 server có lợi trong việc keep alive socket connection. Khi sử dụng IP Hash, các request từ cùng một địa chỉ IP sẽ luôn được chuyển đến cùng một server, giúp duy trì session.
events {
worker_connections 1024;
}
http {
upstream app_servers {
ip_hash;
server app1:3000;
server app2:3000;
server app3:3000;
}
server {
listen 80;
location / {
proxy_pass http://app_servers;
}
}
}
4. Weight (Trọng Số)
Weight cho phép bạn chỉ định tỷ lệ phân phối tải cho các server backend, giúp phân phối tải không đồng đều giữa các server tùy thuộc vào khả năng xử lý của chúng.
events {
worker_connections 1024;
}
http {
upstream app_servers {
server app1:3000 weight=3;
server app2:3000 weight=1;
server app3:3000 weight=1;
}
server {
listen 80;
location / {
proxy_pass http://app_servers;
}
}
}
Trong cấu hình này:
- app1 có trọng số 3, có nghĩa là nó sẽ nhận gấp ba lần số request mà app2 và app3 nhận được.
- app2 và app3 có trọng số 1, mỗi server sẽ nhận số request bằng nhau.
4. Cấu Hình NGINX Plus (Load Balancing cao cấp)
Trong NGINX Plus, bạn có thể sử dụng các tính năng load balancing cao cấp mà không có trong phiên bản NGINX community. Dưới đây là một số tính năng đặc biệt mà chỉ có trong NGINX Plus:
- Active Health Checks:
- NGINX Plus có khả năng kiểm tra tình trạng sức khỏe của các server backend một cách chủ động. Nó sẽ liên tục kiểm tra tình trạng của các server và tự động loại bỏ các server không hoạt động khỏi nhóm backend.
- Session Persistence (Sticky Sessions):
- NGINX Plus hỗ trợ duy trì kết nối cho các phiên làm việc (session persistence) bằng cách sử dụng cookies hoặc dựa trên địa chỉ IP. Điều này đảm bảo rằng tất cả các request từ một người dùng trong một phiên sẽ luôn được chuyển đến cùng một server.
- Dynamic Reconfiguration:
- Với NGINX Plus, bạn có thể thay đổi cấu hình load balancing mà không cần phải khởi động lại NGINX. Điều này giúp đảm bảo rằng việc thay đổi không làm gián đoạn dịch vụ.
- Rate Limiting & Throttling:
- NGINX Plus cung cấp khả năng giới hạn tỷ lệ request (rate limiting) và kiểm soát lưu lượng (throttling) dựa trên các thông số như địa chỉ IP hoặc URL.
- Load Balancing with Least Time:
- Ngoài các thuật toán Round Robin và Least Connections, NGINX Plus còn hỗ trợ thuật toán Least Time (tính toán dựa trên thời gian phản hồi), giúp gửi request đến server có thời gian phản hồi nhanh nhất.
Lưu ý: Các tính năng này chỉ có sẵn trong NGINX Plus và không thể thực hành được với phiên bản NGINX cộng đồng (miễn phí), cần một giấy phép trả phí để sử dụng. nên ở bài viết này mình chỉ giới thiệu về 1 vài khái niệm của load balancing với NGINX Plus
5. Chạy Docker Compose
Sau khi cấu hình xong, chạy Docker Compose để xây dựng và khởi động các container:
docker-compose up --build -d
Lệnh này sẽ tạo ra ba container Node.js và một container NGINX, sau đó phân phối tải giữa các container Node.js theo thuật toán mà chúng ta đã chọn.
Đến bước này, nếu các bạn request vào http://localhost thì request sẽ luôn tải về app1, vì chúng ta chưa thay đổi nội dung file nginx.conf. Chúng ta sẽ bắt đầu test từng loại load balancing ở bước sau.
Vì chúng ta có 4 loại load balacing cần test, nên ở mỗi loại các bạn copy file cấu hình NGINX vào file nginx.conf trong thư mục nginx và chạy docker compose lại nhé.
6. Kiểm Tra Load Balancing
Để kiểm tra load balancing, bạn có thể tạo một script bash để gửi nhiều request đến NGINX.
6.1 Tạo file bash kiểm tra
Tạo file test.sh
với nội dung như sau:
#!/bin/bash
for i in {1..20}
do
curl http://localhost
echo
done
Xong xuôi chúng ta sẽ có cấu trúc folder như bên dưới.

6.2 Test
6.2.1 Test Round Robin
Tiến hành đổi content file nginx/nginx.conf thành loại Round Robin và chạy lệnh:
Chúng ta có kết quả như sau:
bash test.sh

Như vậy là các request đã được phân phối theo chu kỳ giữa các server
6.2.2 Test Least Connections
Tiến hành đổi content file nginx/nginx.conf thành loại Least Connections và chạy lệnh:
bash test.sh
Chúng ta có kết quả như sau:

Vì chỉ có 1 mình test vào NGINX nên request ở các server sẽ bằng nhau, với thuật toán Least Connections kết quả là NGINX sẽ phân phối đều và tuần tự cho các server
6.2.3 Test IP Hash
Tiến hành đổi content file nginx/nginx.conf thành loại IP Hash và chạy lệnh:
bash test.sh
Chúng ta có kết quả như sau:

Vậy là toàn bộ request từ máy mình chỉ được đưa đến 1 server duy nhất
6.2.4 Test Weight
Tiến hành đổi content file nginx/nginx.conf thành loại IP Hash và chạy lệnh:
bash test.sh
Chúng ta có kết quả như sau:

Như vậy app1 nhận được số lượng request gấp 3 lần app2 và app3, app2 và app3 có số lượng request bằng nhau
7. Kết Luận
Trong bài viết này, chúng ta đã tìm hiểu về các thuật toán load balancing trong NGINX: Round Robin, Least Connections, IP Hash, và Weight. Mỗi thuật toán có cách phân phối tải khác nhau và sẽ phù hợp với các tình huống khác nhau. Hy vọng bài viết này giúp bạn hiểu rõ hơn về cách cấu hình và áp dụng các thuật toán load balancing cho các ứng dụng thực tế của mình.
Leave a Reply