好 pm2, 不用嗎?

Ray Lee | 李宗叡
Learn or Die
Published in
9 min readJul 27, 2019
Photo by NEW DATA SERVICES on Unsplash

English version

My Blog

前言

pm2 是什麼?

  • pm2 是一個 node 的程序管理器

pm2 解決什麼問題?

  • pm2 可以讓 node 服務 crash 掉之後,自動幫我們重啟
  • pm2 可以在 server 重啟之後,自動幫我們重啟
  • pm2 可利用 CPU 多核,開啟多程序,已達到類似負載平衡的效果
  • Graceful reload 可達成類似 rolling upgrade 的效果,0 downtime 升級
  • 多程序多服務,可提升處理 request 的速度
  • 可設定 cron 排程自動重啟時間
  • pm2 提供多項資訊,包含已重啟次數、 CPU 用量、 memory 用量, process id, 等等…
  • pm2 可以在指定的條件下,自動幫我們重啟,條件可以是’up time’, ‘已使用多少 memory’, 等等…,
  • pm2 可以幫我們整理 log, 讓 log 以我們想要的週期分割檔案,並保存我們想要的數量,若有超過,自動刪除。
  • pm2 提供簡單的部署方式,可一次性部署到多台 server
  • pm2 可與 CD / CD 工具做結合, CI / CD 部署也沒有問題

好 pm2, 不用嗎?

本篇將提到:

  • 安裝 pm2
  • 使用 CLI 啟動 pm2
  • 使用 pm2 設定檔 ecosystem 啟動 pm2
  • 使用 pm2 設定檔 ecosystem 部署 node 專案
  • 使用 pm2 搭配 GitLab CI / CD Runner 部署 node 專案

安裝

  • 全域安裝

pm2 with CLI

可以使用 pm2 CLI 來啟動 node 專案, 範例如下:

以上範例中設定代表的意思,參考如下:

開始可以附加的參數

  • --name 指定 app 一個名字
  • --watch 檔案有變更時,會自動重新啟動
  • --max-memory-restart Memory 使用超過這個門檻時,會自動重啟
  • --log 指定 log 的位址, 若要指定新位址,需將原本的 process 刪掉,再重新啟動指定
  • --output 指定 output log 位址
  • --error 指定 error log 位址
  • --log-date-format 指定 log 的格式
  • --merge-logs 同一個 app 跑多程序時,不要依據程序 id 去分割 log, 全部合在一起
  • --arg1 --arg2 --arg3 指派額外的參數
  • --restart-delay 自動重啟時,要 delay 多久
  • --time 給 log 加上前綴
  • --no-autorestart 不要自動重啟
  • --cron 指定 cron 規律,強制重啟
  • --no-daemon 無 daemon 模式, listen log 模式

叢集模式

  • pm2 自動偵測該機器的 CPU 數量,啟動最大能負荷的 process, 適用上面的選項, -i 後面接希望啟動 instance 的數量, 0 或 max 默認自動偵測 CPU 啟動最大值

管理程序

  • 直接 kill 掉 process, 再重新開始程序
  • 如果是在 cluster mode, reload 會依序升級重啟每一個程序,達到 zero downtime 升級
  • 停止服務
  • 停止並刪除服務
  • 除了 app_name 之外,你也可以指定 all : 啟動所有程序 id : 該程序 id

顯示管理程序狀態

Logs

  • 輸出 log
  • 顯示指定行數 log (指定倒數 200 行)
  • 指定輸出程序 log
  • 指定輸出格式
  • 清空 log
  • 取消 log 可以利用指定 log 路徑為 /dev/null 來取消 log 輸出, log 參數用法請參考 ecosystem 範例

循環 log

如果你看過 log 檔案超肥,幾年的 log 都寫在同一個檔案; 如果你打開 log 資料夾,發現裡面躺著幾百個 log 檔案; 如果你看過千奇百怪的 log 檔名; 如果你 du -h 發現 log 資料夾大的嚇死人 如果你有以上的經驗,那恭喜你,你有救了

安裝

config 檔位置

/home/user/.pm2/module_conf.json

參數

  • max_size (預設 10M): 當 log 檔案達到多大時, logrotate module 會將它分割成另外一個檔案。 logrotate module 有可能在檢查檔案時,檔案已經超過指定的大小了,所以超過一些些是可能的。 單位可以自行指定, 10G, 10M, 10K
  • retain (預設 30 個 log 檔案): 預設最多保存的 log 數量,如果設定為 7 的話,將會保存目前的 log, 以及最多 7 個 log 檔案
  • compress (預設 false): 壓縮所有循環 log 檔案
  • dateFormat (時間格式,預設 YYYY-MM-DD_HH-mm-ss) : 檔案命名的時間格式
  • rotateModule (預設 true) : 跟其他 apps 一樣,循環 pm2’s module
  • workerInterval (預設 30 秒) : 多久 logrotate 會檢查一次 log 檔案大小
  • rotateInterval (預設每天午夜循環, 範例 0 0 * * * ): 除了設定檔案大小以外,我們也可以設定以時間為單位去循環,格式上採用 node-schedule
  • TZ (預設為系統時間): 檔案命名的時間會根據你所設定的時區而改變

圖示

terminal 監控面板

pm2 ecosystem

CLI 工具固然不錯,但只要是人難免手滑打錯或漏打參數。 pm2 ecosystem 解決了這個問題,只要好好的打上一次,以後除非設定有變更,否則啟動服務只需要短短幾個指令,而且 ecosystem 檔案還可以納入 git 控管,跟著專案跑

  • 產生範例 ecosystem file

CLI

跟前面介紹過的管理程序一樣,差別只是將 app.js 換成 ecosystem.js 多個管理程序 CLI, 這邊就只列出 start, 其餘同上

從 ecosystem 中只啟動特定 app

下面的 appName 為我們寫在 ecosystem.config.js 檔案中的 appName

帶入參數

拿下面的範例來說,如果我輸入 pm2 start ecosystem --only app1 --env production , 那麼 pm2 就會使用 NODE_ENV=production 這個環境變數

參數範例

下面的參數有點多,我們肯定不會一次使用到這麼多的參數,所以可以視專案需求留下我們需要的參數即可

pm2 部署

pm2 的部署功能,可以讓我們從本機直接部署到多台 server 上, 也可以結合 CI / CD 工具,在提交 commit 後自動部署

部署前的必要條件

  • 首先要先確定,local 到 remote 端的 ssh key 有準備好了嗎? local 到 remote server 的 ssh 連線是必要的哦! 簡單來說,你需要在 local 放一把 private key, 然後在你的 remote server 放一把 public key, 這樣才能暢通無阻哦! 這部分再麻煩 Google 一下哦!
  • 再來,因為 pm2 會 ssh 到 remote server 上,然後在 remote server 上從我們的專案處 GitHub 或 GitLab 將專案 clone 下來,所以務必確保 remote server 是可以從 GitHub 或 GitLab clone 我們的專案, 所以你要在 remote server 上放一把 clone 用的 private key, 然後將 public key 放在 GitLab 或 GitHub 上,這部分也是麻煩 Google 一下哦
  • 由於首次 ssh 連線時會跳詢問是否將 public key 加入到 known host,這個 prompt 會讓 pm2 deploy 卡住,所以務必先將 remote server 設定好哦! 可以先連線一次,也可以修改 ssh config, 取消這個 hostKey 的 確認功能。
  • 接下來,要將 ecosystem 設定檔寫好,這部分請參考上方的 deploy 範例
  • 最後,請確認 remote server 的 ssh 通道 (預設 port 22) 不是關閉的哦!

初始化遠端資料夾

在部署之前, 先在 remote server 上初始化專案的資料夾, 可以帶入不同的參數讓 pm2 根據設定檔做相對應得部署

部署

  • 部署 在初始化遠端資料夾之後,我們就可以使用 pm2 的部署功能了
  • deploy 可使用的參數如下,也可使用 pm2 deploy help 查看

部署相關指令

強制重啟

pm2 的部署,會要求 local 端先將變更推上 Git repository, 然後 pm2 會在 remote server 執行 git pull, 所以當 local 的變更尚未推上 Git 時,部署會失敗。 這時候如果我們硬要部署,我們可以使用

CI / CD 部署

  • 利用 GitLab 的 CI / CD Runner 配合 pm2 來跑自動部署, 以下為 gitlab.yml 檔案範例
  • 設定好之後,只要 git push 到 master branch, 就會觸發 GitLab CI / CD Runner 自動完成 CI / CD

開機自動啟動

  • 產生開機 script
  • 取消開機自動重啟
  • 儲存下次重啟時,預設啟動的 process
  • 如果有更新 node 的版本,記得更新 script

有變更時重啟

監看該資料夾下的所有檔案,以及子資料夾,並且忽略 node_module 這個資料夾

更新 PM2

常用指令

自動補齊

  • 支援 pm2 指令可以打 tab 自動補齊

疑難雜症

遇到錯誤 Error: ENOENT: no such file or directory, uv_cwd

意思是說, pm2 的工作目錄資料夾不存在,所謂的工作目錄資料夾就是我們第一次啟動 pm2 的位置。很可能是我們啟動之後,就不小心把它刪了,如果要尋找工作目錄資料夾在哪,可以使用下面的 command

  1. 找到 pm2 的 process id
  1. 然後查詢該 process 執行時所在的目錄(將上面得到的 process id 替換下面的 PM2_Process_ID
  1. 公布結果

結果應該會如下, 最後的 deleted 表示該目錄已經被刪除了

現在知道原因了,那解決的方法呢? 我們要先把目前的 process 砍掉,然後到一個安全一點的地方在開啟一次,以免下次又被誤刪了!

  1. 殺掉 pm2 process id
kill -9 processID
  1. 到一個安全不會再被意外砍掉的目錄再次啟動 pm2

參考資料

pm2 官網 pm2 logrotate

Write Medium in Markdown? Try Markdium!

--

--

Ray Lee | 李宗叡
Learn or Die

It's Ray. I do both backend and frontend, but more focus on backend. I like coding, and would like to see the whole picture of a product.