Triển khai Node.js lên Ubuntu 22.04: Từ “chạy thử” đến Production-ready với PM2 & Nginx

Ubuntu tutorial - IT technology blog
Ubuntu tutorial - IT technology blog

Vấn đề: Cơn ác mộng “sập app” lúc nửa đêm

Kịch bản này chắc hẳn nhiều anh em đã gặp: Bạn vừa hoàn thành một ứng dụng Node.js tuyệt vời. Bạn SSH vào server, gõ lệnh node app.js và thấy mọi thứ mượt mà trên port 3000. Bạn yên tâm tắt terminal đi ngủ, nhưng sáng hôm sau thức dậy là hàng chục email báo lỗi: “Website không thể truy cập!”.

Chạy Node.js bằng lệnh trực tiếp chỉ dành cho lúc test nhanh. Trên môi trường Production, mọi thứ khắc nghiệt hơn nhiều. Chỉ cần server tự reboot sau khi bảo trì, hoặc code gặp một lỗi logic gây tràn bộ nhớ (memory leak), website của bạn sẽ “ngỏm” ngay lập tức nếu không có cơ chế tự phục hồi.

Phân tích: Tại sao cách chạy thông thường lại dễ “tạch”?

Node.js chạy trên cơ chế single-thread. Nghĩa là nếu một request gây ra lỗi chưa được xử lý (unhandled exception), toàn bộ tiến trình sẽ dừng lại. Thêm vào đó, Node.js không có khả năng tự khởi động cùng hệ thống. Nếu VPS của bạn bị restart, ứng dụng sẽ nằm im lìm cho đến khi bạn vào gõ lệnh thủ công.

Mở trực tiếp port 3000 cho người dùng cũng là một sai lầm lớn về bảo mật. Bạn cần một “lá chắn” phía trước để xử lý HTTPS, nén dữ liệu Gzip và điều phối traffic. Đó là lý do chúng ta cần một quy trình chuyên nghiệp hơn.

Giải pháp: Bộ ba Ubuntu + PM2 + Nginx

Để hệ thống chạy ổn định, mình luôn ưu tiên công thức: PM2 quản lý tiến trình, Nginx làm Proxy ngược và Certbot cấp SSL miễn phí. Cách này giúp app không chỉ sống dai mà còn dễ dàng mở rộng (scale) khi lượng user tăng đột biến.

Bước 1: Cài đặt Node.js qua NVM

Đừng cài Node.js trực tiếp từ kho apt của Ubuntu vì phiên bản ở đó thường rất cũ và thiếu ổn định. Kinh nghiệm của mình là luôn dùng NVM (Node Version Manager). Công cụ này giúp bạn chuyển đổi giữa các phiên bản Node chỉ trong một nốt nhạc.

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash
source ~/.bashrc
nvm install --lts
node -v # Kiểm tra xem đã lên bản LTS chưa

Bước 2: Quản lý ứng dụng bằng PM2

PM2 không chỉ là trình quản lý tiến trình đơn thuần. Tính năng Cluster Mode của nó giúp tận dụng tối đa các nhân CPU của server. Mặc định Node.js chỉ chạy trên 1 nhân, nếu server bạn có 4 nhân mà không dùng Cluster Mode thì bạn đang lãng phí 75% tài nguyên phần cứng.

Cài đặt PM2 toàn cục:

npm install pm2 -g

Hãy kích hoạt Cluster Mode để PM2 tự động chia tải dựa trên số nhân CPU hiện có:

pm2 start app.js -i max --name "my-app"

Để quản lý chuyên nghiệp hơn, bạn nên tạo file ecosystem.config.js. File này giúp kiểm soát các biến môi trường (environment variables) cực kỳ hiệu quả:

module.exports = {
  apps : [{
    name: "my-app",
    script: "./app.js",
    instances: "max",
    exec_mode: "cluster",
    env_production: {
      NODE_ENV: "production",
      PORT: 3000
    }
  }]
}

Đặc biệt, đừng quên lệnh “thần thánh” giúp app tự khởi động lại sau khi server bị crash hoặc reboot:

pm2 startup
# Copy và chạy dòng lệnh mà PM2 hiển thị trên màn hình
pm2 save

Bước 3: Cấu hình Nginx làm Reverse Proxy

Lúc này app đang chạy nội bộ ở port 3000. Chúng ta sẽ dùng Nginx để đón khách ở port 80/443 và đẩy về port 3000 này. Cách làm này giúp che giấu cấu trúc bên trong và tăng tốc độ xử lý các file tĩnh.

Tạo file cấu hình cho site:

sudo nano /etc/nginx/sites-available/my-app

Dán nội dung dưới đây và thay yourdomain.com bằng domain của bạn:

server {
    listen 80;
    server_name yourdomain.com;

    location / {
        proxy_pass http://localhost:3000;
        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;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

Kích hoạt cấu hình và kiểm tra lỗi cú pháp trước khi restart:

sudo ln -s /etc/nginx/sites-available/my-app /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx

Bước 4: Bảo mật với SSL Let’s Encrypt

Ngày nay, website không có HTTPS sẽ bị trình duyệt cảnh báo “Không an toàn” ngay lập tức. Thay vì tốn vài triệu mỗi năm cho chứng chỉ SSL, bạn có thể dùng Certbot để lấy chứng chỉ hoàn toàn miễn phí.

sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d yourdomain.com

Certbot sẽ tự động chỉnh sửa file Nginx, thiết lập HTTPS và tự động gia hạn khi chứng chỉ sắp hết hạn. Bạn gần như không phải làm gì thêm.

Mẹo tối ưu: Kinh nghiệm từ thực tế triển khai

Sau nhiều lần đau đầu vì server hết dung lượng ổ cứng đột ngột, mình rút ra một bài học: Luôn phải quản lý log. PM2 ghi log rất chi tiết, nếu không kiểm soát, file log có thể phình lên hàng chục GB chỉ sau vài tuần.

Hãy cài thêm module pm2-logrotate để tự động nén và xóa log cũ:

pm2 install pm2-logrotate

Một lưu ý quan trọng khi cập nhật code (deploy): Đừng dùng pm2 restart. Hãy dùng pm2 reload. Khác biệt ở chỗ reload sẽ khởi động lại từng instance theo kiểu cuốn chiếu, giúp website đạt trạng thái Zero-downtime — không gián đoạn dù chỉ một giây.

Tổng kết

Để một ứng dụng Node.js vận hành chuyên nghiệp, bạn chỉ cần nhớ 4 trụ cột: NVM để quản lý phiên bản, PM2 để giữ app luôn sống, Nginx để điều phối traffic và Certbot để bảo mật. Workflow này đã giúp mình vận hành ổn định cho nhiều dự án thực tế với hàng nghìn user mỗi ngày. Chúc anh em deploy thành công!

Share: