Hướng dẫn triển khai ứng dụng Node.js trên server/VPS production với Ubuntu 18.04

Giới thiệu

Node.js là một hệ thống phần mềm được thiết kế để viết các ứng dụng internet có khả năng mở rộng, đặc biệt là máy chủ web. Nó có thể chạy trên MacOS, Linux, Windows và FreeBSD. Mặc dù có thể chạy dưới dạng câu lệnh command-line, bài viết này chỉ tập trung vào việc thiết lập Node.js chạy dưới dạng daemon, hoặc service. Điều đó có nghĩa là nếu bạn làm theo bài hướng dẫn này, Node.js sẽ có khả năng tự tái khởi động nếu nó bị tắt đột ngột hoặc server/VPS bị crash – một tính năng buộc phải có khi chạy trên môi trường production.
Trong bài viết này, bạn sẽ thiết lập một ứng dụng Node.js trên một server/VPS Ubuntu 18.04 Bionic Beaver đơn lẻ. Server/VPS này sẽ chạy node.js dưới trình quản lý của PM2, và được cung cấp cho khách truy cập qua một Nginx reverse proxy được bảo mật bằng chứng chỉ SSL miễn phí của Let’s Encrypt

Yêu cầu

Bài hướng dẫn này ngầm hiểu rằng bạn đã cài đặt những thứ sau đây :
* Một server/VPS Ubuntu 18.04 được thiết lập hoàn chỉnh, bao gồm một user non-root (không phải root) nhưng có quyền sudo và hoạt động kèm một firewall (tường lửa).
* Một tên miền gắn với địa chỉ IP của server/VPS. Trong ví dụ này mình sẽ dùng hucau.net.
* Nginx được cài đặt hoàn chỉnh và được cấu hình sử dụng chứng chỉ của Let’s Encrypt.

Bước 1 – Cài đặt Node.js

Bắt đầu với việc cài đặt phiên bản mới nhất của Node.js từ nguồn NodeSource. Bạn nên sử dụng NodeSource vì nó cung cấp mặt định phiên bản LTS (Long-Term Support), được hỗ trợ các bản vá bảo mật mới trong thời gian dài nhất.
Trước tiên, bạn thêm PPA của NodeSource vào apt để có thể truy cập vào nội dung của nó. Hãy chắc chắn rằng bạn đang ở thư mục gốc của user đang dùng bằng lệnh cd ~, sau đó dùng curl để tải đoạn mã bash script cài đặt Node.js 8.x :

curl -sL https://deb.nodesource.com/setup_8.x -o nodesource_setup.sh

Tiếp theo, chạy script vừa tải về dưới quyền sudo :

sudo bash nodesource_setup.sh

PPA sẽ được thêm vào thiết lập của apt và ngay sau đó cache sẽ tự cập nhật, tương tự việc bạn chạy sudo apt-get update. Cập nhật xong, bạn đã có thể cài đặt gói Node.js :

sudo apt-get install nodejs

Để kiểm tra phiên bản Node.js vừa cài đặt, bạn gõ

nodejs -v
Output
v8.11.3

Lưu ý : Khi cài đặt từ PPA của NodeSource, Node.js sẽ được gọi từ lệnh nodejs, thay vì node như bình thường.
Gói nodejs này bao gồm file binary nodejs và trình quản lý gói npm, nên bạn không cần cài đặt riêng npm nữa.
npm theo dõi các gói và cập nhật chúng dựa trên một file cấu hình được lưu trong /home.
File cấu hình này sẽ được tạo lần đầu tiên bạn chạy npm. Để tạo file này đồng thời kiểm tra phiên bản npm đã được cài đặt, bạn gõ:

npm -v
Output
5.6.0

Để một số gói npm có thể hoạt động, nhất là các gói phụ thuộc vào việc build từ source, bạn cũng cần cài đặt thêm gói build-essential (nếu chưa có):

sudo apt install build-essential

Với việc cài đặt xong Node.js, chúng ta sẽ sang bước tiếp theo – viết một ứng dụng để test Node.js.

Bước 2 – Tạo ứng dụng Node.js

Công việc tiếp theo là viết một ứng dụng cơ bản, nhằm mục đích kiểm tra sự hoạt động bình thường của Node.js. Ứng dụng kiểu Hello, World này sẽ trả về “Hello World” với bất kỳ HTTP request nào được gửi đến. Bạn cũng có thể bỏ qua phần này và thay thế bằng ứng dụng mà mình đã viết sẵn nếu muốn.
Trước tiên, tạo ứng dụng mạng tên hello.js:

cd ~
nano hello.js

File hello.js mang nội dung như sau :

const http = require('http');

const hostname = 'localhost';
const port = 3000;

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World!\n');
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

Ứng dụng node.js đơn giản này sẽ listen ở địa chỉ localhost trên port 3000, và đơn giản là hồi đáp “Hello World!” với bất kỳ request nào được gửi đến. Tuy nhiên hiện tại vì nó đang listen trên localhost nên khách sẽ không thể truy cập được vào ứng dụng cực kỳ phức tạp của chúng ta.
Để test thử ứng dụng, gõ :

node hello.js
Output
Server running at http://localhost:3000/

Lưu ý: Chạy ứng dụng node.js kiểu này sẽ khóa phiên làm việc của bạn lại và bạn không thể nào chạy thêm câu lệnh nào khác cho tới khi tắt ứng dụng bằng Ctrl+C. Trong trường hợp này bạn có thể sử dụng screen, hoặc mở một phiên làm việc mới (terminal hoặc PuTTY)
Để test ứng dụng với tư cách khách truy cập, bạn mở một phiên làm việc khác, kết nối đến localhost với curl như sau :

curl http://localhost:3000

Ứng dụng hoạt động bình thường sẽ cho kết quả

Output
Hello World!

Nếu bạn không nhìn thấy như trên, có lẽ ứng dụng của bạn đang có vấn đề, bạn nên tham khảo lại những bước cài đặt trên.

Bước 3 – Cài đặt PM2

Tiếp theo là cài đặt PM2 – một trình quản lý process cho các ứng dụng Node.js. PM2 cho phép bạn có thể biến các ứng dụng thành các daemon, hay service, tùy bạn gọi, để chúng có thể chạy nền mà không cần phải mở phiên làm việc trong suốt quá trình sử dụng.
Sử dụng npm để cài đặt phiên bản mới nhất của PM2 trên server của bạn :

sudo npm install [email protected] -g

Option -g ở trên nghĩa là global, chỉ định cho npm cài đặt gói cho toàn bộ hệ thống.
Trước tiên hãy làm quen với câu lệnh pm2 start để chạy ứng dụng hello.js dưới nền :

pm2 start hello.js

Câu lệnh trên cũng sẽ thêm ứng dụng của bạn vào danh sách ứng dụng được quản lý bởi PM2, danh sách này xuất hiện mỗi khi bạn khởi động một ứng dụng nào đó :

[PM2] Spawning PM2 daemon with pm2_home=/home/hucau.net/.pm2
[PM2] PM2 Successfully daemonized
[PM2] Starting /home/hucau.net/hello.js in fork_mode (1 instance)
[PM2] Done.
┌──────────┬────┬──────┬──────┬────────┬─────────┬────────┬─────┬───────────┬───────┬──────────┐
│ App name │ id │ mode │ pid  │ status │ restart │ uptime │ cpu │ mem       │ user  │ watching │
├──────────┼────┼──────┼──────┼────────┼─────────┼────────┼─────┼───────────┼───────┼──────────┤
│ hello    │ 0  │ fork │ 1338 │ online │ 0       │ 0s     │ 0%  │ 23.0 MB   │ hucau.net │ disabled │
└──────────┴────┴──────┴──────┴────────┴─────────┴────────┴─────┴───────────┴───────┴──────────┘
 Use `pm2 show <id|name>` to get more details about an app

Như bạn có thể thấy, PM2 tự đống gán cho mỗi ứng dụng một id, cũng như App name chính là tên của ứng dụng (không có .js đằng sau). PM2 cũng theo dõi những thông tin khác, bao gồm PID của process, trạng thái, mức sử dụng bộ nhớ của nó.
Các ứng dụng chạy dưới quyền của PM2 sẽ được tự động khởi động lại nếu nó bị crash hoặc bị tắt đột ngột, tuy nhiên bạn cần thêm một bước nữa để đảm bảo ứng dụng luôn chạy, đó là cài đặt sao cho PM2 khởi động cùng với hệ điều hành. Bạn sử dụng lệnh startup để sinh ra câu lệnh giúp khởi động PM2 và các process đi kèm bằng cách gõ :

pm2 startup systemd
Output
[PM2] Init System found: systemd
[PM2] To setup the Startup Script, copy/paste the following command:
sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u hucau.net --hp /home/hucau.net

Dòng cuối cùng của kết quả câu lệnh trên chính là đoạn script cài đặt PM2 vào các tiến trình startup của systemd. Hãy copy và chạy nó, chú ý tên username bạn đang dùng khác với hucau.net dưới đây:

sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u hucau.net --hp /home/hucau.net

Giờ bạn đã có một systemd unit gọi đến pm2 khi đăng nhập vào user của bạn. Sau khi pm2 khởi động, nó sẽ tự gọi đến ứng dụng hello.js của chúng ta.
Khởi động service với systemctl :

sudo systemctl start pm2-hucau.net

Kiểm tra trạng thái của systemd unit

systemctl status pm2-hucau.net

Bên cạnh những thứ chúng ta đã đề cập, PM2 còn có những lệnh con giúp bạn dễ dàng quản lý và xem thông tin của các ứng dụng đang chạy:
* Dừng ứng dụng bằng App name hoặc id:

pm2 stop tên_hoặc_id
  • Khởi động lại ứng dụng
pm2 restart tên_hoặc_id
  • Xem danh sách các ứng dụng đang được PM2 quản lý
pm2 list
  • Xem thông tin chi tiết về một ứng dụng
pm2 info tên_app
  • Xem bảng điều khiển tổng quát của PM2
pm2 monit

Giờ ứng dụng Node.js của bạn đã chạy và được giám sát bởi PM2, giờ đến phần thiết lập Reverse Proxy đưa các request từ khách đến với ứng dụng và ngược lại.

Bước 4 – Thiết lập Nginx làm Reverse Proxy Server

Ứng dụng của bạn đang chạy và listenlocalhost, nhưng khách vào site bạn cần một cách để có thể tương tác với ứng dụng đằng sau localhost đó, đây là lúc Nginx trở nên hữu ích với tính năng Reverse Proxy của nó.
Giả sử bạn đã có file cấu hình website của mình ở /etc/ngĩn/sites-available/hucau.net. Hãy mở nó lên bằng nano:

sudo nano /etc/nginx/sites-available/hucau.net

Trong block server, chắc hẳn bạn đã có một block location /, hãy thay thế những gì ở trong block location / đó bằng nội dung sau đây. Chú ý thay thông số port bằng port mà ứng dụng của bạn đang listen.

server {
...
    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;
    }
...
}

Cấu hình trên chỉ định cho server hồi đáp các request vào root path (địa chỉ gốc). Giả sử tên miền là hucau.net thì truy cập http://hucau.net/ sẽ đưa request của chúng ta đến với hello.js đang listen tại port 3000localhost.
Bạn có thể thêm vào trong block server các block location khác để trỏ đến những ứng dụng khác nhau (hoặc cũng có thể giống nhau) trên server. Ví dụ mình cũng có thêm ứng dụng hello2.js chạy trên port 6000 và muốn nó truy cập được ở địa chỉ /app2 thì cấu hình sẽ như sau :

server {
...
    location /app2 {
        proxy_pass http://localhost:3001;
        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;
    }
...
}

Khi đã cấu hình xong tất cả, lưu file lại và đóng nano.
Kiểm tra lại các file cấu hình về mặt cú pháp :

sudo nginx -t

Khởi động lại Nginx

sudo systemctl restart nginx

Kết bài

Nếu các bước trên bạn đã thực hiện đúng như trong bài, giờ ứng dụng của bạn đã có thể truy cập tại địa chỉ của server/VPS (tên miền hoặc IP). Cách setup này nhanh gọn và phù hợp cho hầu hết những ứng dụng vừa và nhỏ. Với các ứng dụng lớn hơn, bạn nên cân nhắc việc thêm vào load balancer và một vài server để chia tải.

Để lại comment