Làm chủ Supervisor cho Node.js: Cluster Mode, Zero-downtime Deployment & Kết hợp Nginx
Chào anh em đã quay lại với chương cuối cùng trong series chuỗi bài về Supervisor và Node.js! Ở hai bài trước, chúng ta đã nắm bắt lý do tại sao một ứng dụng Express hay NestJS trên Production bắt buộc cần Supervisor bảo kê, cách viết một file cấu hình .conf chuẩn chỉnh bằng lệnh trực tiếp /usr/bin/node [cite: 19].
Trong bài học cuối cùng này, chúng ta sẽ giải quyết bài toán lớn nhất của Node.js: Tận dụng đa nhân CPU (Multi-core) bằng numprocs, thực hiện Zero-downtime Deployment và cấu hình Nginx Reverse Proxy làm cân bằng tải.
1. Giải bài toán Đơn luồng (Single-threaded) của Node.js với Supervisor numprocs
Như anh em đã biết, bản chất Node.js chạy trên một luồng đơn (Single-threaded event loop). Nếu server của bạn có 4 nhân CPU (4 cores), việc chỉ chạy một tiến trình Node.js độc lập sẽ bỏ phí 75% tài nguyên phần cứng của hệ thống.
Mặc dù Node.js có cung cấp module cluster sẵn trong core, việc tự viết code quản lý cluster đôi khi khá rắc rối và khó debug. Với Supervisor, chúng ta có thể tận dụng tham số numprocs kết hợp với biến môi trường %(_process_num_)s để scale ứng dụng ra nhiều tiến trình chạy song song cực kỳ đơn giản!
Cấu hình .conf chạy đa tiến trình trên nhiều Port:
Hãy chỉnh sửa lại file cấu hình của bạn tại /etc/supervisor/conf.d/my-node-app.conf:
[program:my-node-app]
process_name=%(program_name)s_%(process_num)02d
command=/usr/bin/node /var/www/my-node-app/dist/main.js
directory=/var/www/my-node-app
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=3
redirect_stderr=true
stdout_logfile=/var/www/my-node-app/logs/supervisor.log
environment=NODE_ENV="production",PORT="300%(process_num)02d"
stopwaitsecs=10
🔍 Điểm ăn tiền ở cấu hình này:
numprocs=3: Supervisor sẽ tự động sinh ra 3 tiến trình con chạy song song:my-node-app_00,my-node-app_01,my-node-app_02.PORT="300%(process_num)02d": Biến đổi linh hoạt cổng mạng cho từng tiến trình!- Tiến trình
_00sẽ chạy ở cổng3000. - Tiến trình
_01sẽ chạy ở cổng3001. - Tiến trình
_02sẽ chạy ở cổng3002.
- Tiến trình
Bằng cách này, chúng ta tận dụng triệt để 3 worker độc lập trên các cổng khác nhau. Công việc tiếp theo là đặt một Nginx Reverse Proxy ở phía trước để phân phối traffic.
2. Cấu hình Nginx làm Load Balancer phân phối traffic
Để khách hàng truy cập vào một tên miền duy nhất (ví dụ: api.myproject.com) mà vẫn điều phối được request vào 3 cổng (3000, 3001, 3002) do Supervisor quản lý, chúng ta cấu hình block upstream trong Nginx:
upstream node_cluster {
# Định nghĩa danh sách các cổng mà Supervisor đang quản lý
server 127.0.0.1:3000;
server 127.0.0.1:3001;
server 127.0.0.1:3002;
}
server {
listen 80;
server_name api.myproject.com;
location / {
proxy_pass http://node_cluster;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
Nginx sẽ đóng vai trò phân bổ đều các request theo thuật toán Round-robin (hoặc Least Connections) vào các tiến trình Node.js đang chạy ngầm.
3. Chiến lược Zero-downtime Deployment cho Node.js với Supervisor
Khi anh em thực hiện CI/CD hoặc pull code mới lên server, việc restart ứng dụng Node.js nếu làm không khéo sẽ khiến các request đang xử lý dở bị ngắt kết nối đột ngột (connection reset).
Khác với PHP (với lệnh queue:restart), Node.js không có cơ chế queue worker ngầm tương tự ở cấp độ framework thuần, nhưng chúng ta có thể kết hợp Supervisor để restart tuần tự hoặc tận dụng tập lệnh quản lý.
Quy trình chuẩn khi Deploy code mới:
- Build code mới: Chạy
npm run buildđể cập nhật thư mụcdist/. - Reload Supervisor gracefully:
Thay vì tắt cái rụp, bạn có thể gọi lệnh reload của
supervisorctlđể nó thực hiện việc đóng tiến trình cũ và kích hoạt tiến trình mới:sudo supervisorctl restart my-node-app:*
💡 Mẹo: Để đạt mức Zero-downtime tuyệt đối (không rớt request nào) khi scale nhiều tiến trình (
numprocs=3), trong script CI/CD bạn có thể viết vòng lặp restart từng tiến trình một (restart my-node-app:my-node-app_00, chờ 3 giây, rồi restart tiếp_01...). Lúc đó Nginx sẽ tự động điều hướng traffic sang các tiến trình đang chạy khỏe mạnh còn lại trong cụmupstreamtrong lúc một tiến trình đang khởi động lại!
Tổng kết Series Node.js & Supervisor
Chúc mừng anh em đã đi đến cuối chặng đường khám phá Supervisor cho Node.js [cite: 17]!
- Bài 1 đã giúp chúng ta hiểu rõ vai trò của Supervisor cấp hệ thống so với PM2/Nodemon [cite: 18].
- Bài 2 hướng dẫn thực chiến cấu hình
.confan toàn, chuyên nghiệp bằng đường dẫn tuyệt đối củanode[cite: 19]. - Bài 3 nâng cấp hệ thống với Multi-core (
numprocs), chia cổng linh hoạt và kết hợp Nginx Load Balancer chuẩn mô hình Enterprise.
Hy vọng series này sẽ giúp các ứng dụng Express/NestJS của anh em "bất tử" trên môi trường production, chịu tải cao và vận hành cực kỳ trơn tru.
Đừng quên bấm Upvote / Bookmark bài viết và để lại bình luận nếu anh em có thắc mắc gì trong quá trình setup nhé. Hẹn gặp lại anh em ở các series kỹ thuật tiếp theo trên Viblo!
All rights reserved