PM2 — Cluster Mode and Zero-Downtime Restarts

1. Mở đầu

  • Dùng PM2 đã lâu nhưng có thể bạn chưa biết rõ điều này Zero-Downtime Restart với PM2.
  • Khi đọc bài viết này mình mặc định là mọi người đều đã thành thạo về những lệnh cơ bản của PM2
  • Trước tiên mình sẽ nói qua về cluster mode

2. Cluster mode

2.1. Cluster mode trong nodejs

  • Trước hết thì chúng ta phải hiểu cluster mode trong nodejs là gì?
  • Chúng ta đều biết nodejs là xử lý đơn luồng. Thế rồi bạn nghĩ bạn có CPU 8 nhân, 16 threads. Giờ bạn muốn duyệt một 1 triệu records để tìm phần tử lớn nhất. Với node.js, sẽ chỉ có 1 thread của CPU là thực hiện công việc duyệt tìm vì mặc định Node.js là single-thread. 1 thread chạy cắm đầu, 7 threads kia ngồi chơi và để ngăn chặn tình trạng ngồi chơi đó thì cluster đã ra đời.
  • Việc xử lý cluster trong code nodejs như nào mình sẽ không đề cập đến.

2.2. Cluster trong PM2

  • Việc khởi tạo cluster trong PM2 khá đơn giản mà không phải sửa code.
$ pm2 start app.js -i n
  • Trong đó n là số lượng instance được bật, có thể được sử dụng để tạo cluster.
  • Trong trường hợp muốn khai thác tối đa số cores.
$ pm2 start app.js -i 0
//hoăc
$ pm2 start app.js -i max
  • Hay nếu bạn muốn để lại 2-3 cores cho người khác.
$ pm2 start app.js -i -2
  • Một vấn đề nữa là làm thế nào để update số cores theo ý muôn. Đợt mới vào công ty thì mình toàn xóa đi cài lại :(
$pm2 scale ${name} ${target_cores_number}

3. Zero-Downtime Restart với PM2

3.1. Cách khiến bạn chạy được code

  • Đến đây mọi người nghĩ là xong rồi đúng không. Chỉ cần làm xong cluster mode rồi dùng pm2 reload là được mà vi khi chạy pm2 reload thì pm2 sẽ restart lại từng cái instances một nghĩa là khi restart xong cái hiện tại thì cái tiếp theo mới bắt đầu restart thì sẽ không bị downtime.
  • Tuy nhiên, khi mà restart xong rồi nhưng vẫn mất quá nhiều thời gian để setup 1 số cái như Database Connection(s), Logger setup, Web Socket setup, cron job schedules,... thì vẫn có trường hợp bị downtime. Thì khi đó chúng ta dùng wait-ready để hạn chế trường hợp trên.
  • Thì để dùng wait-ready ta phải sửa 1 chút ở code. Chúng ta thêm process.send('ready'); file startup của mình.
  • Mặc định thì PM2 sẽ chạy 3s để 1 instance bảo nó đã ready (nếu chúng ta dùng wait-ready) nếu quá 3s thì sẽ tự động chạy tiếp mà không đợi cái trước nó restart xong. Chúng ta sẽ dùng listen-timeout để bắt đợi. (thường để an toàn mình cho chạy 10000 tức là 10s)
  • Nếu mà instance mà ready trước 10s thì nó sẽ chuyển tiếp luôn mà không cần đợi nữa.
pm2 start your_startup_file.js  --name YOUR_APP_NAME -i 2 --wait-ready --listen-timeout 10000
  • Thì để dùng wait-ready ta phải sửa 1 chút ở code. Chúng ta thêm process.send('ready'); file startup của mình.
  • Khi bạn config xong thế này thì pull code mới nhất sau này chỉ cần chạy pm2 reload ${your_app_name}

3.2. Cách khiến bạn cảm thấy chuyên nghiệp hơn.

  • Hồi trước config tay như trên nhưng được anh Tuấn Chuối ở công ty khai sáng chúng ta có thể chạy bằng script. (Cái này mình sẽ nói không đề cập chi tiết ở đây, mặc định mọi người đã hiểu hoặc có thể làm 1 blog khác về đề tài này)
  • Config thế này ở trong file ecosystem-dev.config.js hay ecosystem-production.config.js
module.exports = {
  apps: [
    {
      name: 'your-app-name',
      script: "serve",
      env: {},
      watch: false,
      exec_mode: 'cluster',
      instances: 2,
      listen-timeout: 10000,
      restart_delay: 10000,
      autorestart: true, //auto restart if app crashes
    },
  ],
};

  • Rồi mình chỉ cần chạy script mà mình viết trong file ecosystem.config.js
  • Đến đây là hết rồi và mình xin cảm ơn những ai đã đọc cẩn thận đến đây.

4. Tham khảo

https://medium.com/learnwithrahul/zero-downtime-deployments-with-pm2-93013713df15
https://futurestud.io/tutorials/pm2-cluster-mode-and-zero-downtime-restarts