# 前言
# 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 專案
# 安裝
- 全域安裝
npm install pm2@latest -g
# pm2 with CLI
# 可以使用 pm2 CLI
來啟動 node 專案, 範例如下:
pm2 start location/fileName.js --name appName \ |
以上範例中設定代表的意思,參考如下:
# 靜態檔的服務器
- 也可以服務靜態檔
pm2 serve <path> <port>
# 啟動可以附加的參數
--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 模式--spa
限定 serve 使用, 會重導所有的請求到 index.html--basic-auth-username
--basic-auth-password
用於靜態檔, 讓該頁面需要帳號密碼方可存取
# 叢集模式
- pm2 自動偵測該機器的 CPU 數量,啟動最大能負荷的 process, 適用上面的選項,
-i
後面接希望啟動 instance 的數量, 0 或 max 默認自動偵測 CPU 啟動最大值pm2 start app.js -i max
# 管理程序
直接 kill 掉 process, 再重新開始程序
pm2 restart app_name
如果是在 cluster mode,
reload
會依序升級重啟每一個程序,達到 zero downtime 升級pm2 reload app_name
停止服務
pm2 stop app_name
停止並刪除服務
pm2 delete app_name
除了 app_name 之外,你也可以指定
all
: 啟動所有程序id
: 該程序 id
# 顯示管理程序狀態
pm2 [list|ls|status] |
# Logs
輸出 log
pm2 logs
顯示指定行數 log (指定倒數 200 行)
pm2 logs --lines 200
指定輸出程序 log
pm2 logs id
指定輸出格式 format
pm2 logs --format
指定輸出格式 json
pm2 logs --json
清空 log
pm2 flush
取消 log
可以利用指定 log 路徑為/dev/null
來取消 log 輸出, log 參數用法請參考 ecosystem 範例
# 循環 log
如果你看過 log 檔案超肥,幾年的 log 都寫在同一個檔案; 如果你打開 log 資料夾,發現裡面躺著幾百個 log 檔案; 如果你看過千奇百怪的 log 檔名; 如果你 du -h
發現 log 資料夾大的嚇死人
如果你有以上的經驗,那恭喜你,你有救了
# 安裝
pm2 install pm2-logrotate |
# 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 monit |
# pm2 ecosystem
CLI 工具固然不錯,但只要是人難免手滑打錯或漏打參數。 pm2 ecosystem 解決了這個問題,只要好好的打上一次,以後除非設定有變更,否則啟動服務只需要短短幾個指令,而且 ecosystem 檔案還可以納入 git 控管,跟著專案跑
- 產生範例 ecosystem file
pm2 ecosystem
# CLI
跟前面介紹過的管理程序一樣,差別只是將 app.js 換成 ecosystem.js
多個管理程序 CLI, 這邊就只列出 start, 其餘同上
pm2 start ecosystem.config.js |
# 從 ecosystem 中只啟動特定 app
下面的 appName 為我們寫在 ecosystem.config.js 檔案中的 appName
pm2 start ecosystem.config.js --only yourApp |
# 帶入參數
拿下面的範例來說,如果我輸入 pm2 start ecosystem --only app1 --env production
, 那麼 pm2 就會使用 NODE_ENV=production
這個環境變數
# 參數範例
下面的參數有點多,我們肯定不會一次使用到這麼多的參數,所以可以視專案需求留下我們需要的參數即可
module.exports = { |
# 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 的 確認功能。
echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config
- 接下來,要將 ecosystem 設定檔寫好,這部分請參考上方的 deploy 範例
- 最後,請確認 remote server 的 ssh 通道 (預設 port 22) 不是關閉的哦!
# 初始化遠端資料夾
在部署之前, 先在 remote server 上初始化專案的資料夾, 可以帶入不同的參數讓 pm2 根據設定檔做相對應得部署
pm2 deploy ecosystem.config.js production setup |
# 部署
- 部署
在初始化遠端資料夾之後,我們就可以使用 pm2 的部署功能了pm2 deploy ecosystem.config.js production
- deploy 可使用的參數如下,也可使用
pm2 deploy help
查看pm2 deploy <configuration_file> <environment> <command>
Commands:
setup 遠端初始化(第一次部署才會用到)
update 更新到最新的 commit
revert [n] 回復到上一次的 deployment
curr[ent] 輸出目前上線中的 commit
prev[ious] 輸出上一次部署的 commit
exec|run <cmd> 執行指定的指令
list 列出包含目前,以及之前所部署的 commit
[ref] 部署到指定的 ref
# 部署相關指令
pm2 startOrRestart all.json # 重啟所有 app |
# 強制重啟
pm2 的部署,會要求 local 端先將變更推上 Git repository, 然後 pm2 會在 remote server 執行 git pull, 所以當 local 的變更尚未推上 Git 時,部署會失敗。
這時候如果我們硬要部署,我們可以使用
pm2 deploy ecosystem.json production --force |
# CI / CD 部署
- 利用 GitLab 的 CI / CD Runner 配合 pm2 來跑自動部署, 以下為 gitlab.yml 檔案範例
- 設定好之後,只要 git push 到 master branch, 就會觸發 GitLab CI / CD Runner 自動完成 CI / CD
# 使用輕量化 pm2 image
image: keymetrics/pm2:latest-alpine
stages:
- deploy
Deploy:
stage: deploy
script:
# 若 ssh-agent 未安裝,則安裝
- 'which ssh-agent || ( apk add --update openssh )'
# 安裝 bash, 以執行 pm2 CLI 工具
- apk add --update bash
# 安裝 git, pm2 要連過去時會用到
- apk add --update git
# 執行 ssh agent
- eval $(ssh-agent -s)
# 將 ssh key 加到 ssh agent, 此 ssh key 為 GitLab 的 variable 選項
- echo "$SSH_PRIVATE_KEY" | ssh-add -
# 執行 pm2 CLI
- pm2 deploy ecosystem.config.js production update
only:
- master
# 開機自動啟動
產生開機 script
pm2 startup
取消開機自動重啟
pm2 unstartup
儲存下次重啟時,預設啟動的 process
pm2 save
如果有更新 node 的版本,記得更新 script
pm2 unstartup && pm2 startup && pm2 save
# 有變更時重啟
監看該資料夾下的所有檔案,以及子資料夾,並且忽略 node_module
這個資料夾
cd /path/to/my/app |
# 更新 PM2
npm install pm2@latest -g && pm2 update |
# 常用指令
# Fork 模式 |
# 自動補齊
- 支援 pm2 指令可以打
tab
自動補齊pm2 completion install
# 疑難雜症
# 遇到錯誤 Error: ENOENT: no such file or directory, uv_cwd
意思是說, pm2
的工作目錄資料夾不存在,所謂的工作目錄資料夾就是我們第一次啟動 pm2
的位置。很可能是我們啟動之後,就不小心把它刪了,如果要尋找工作目錄資料夾在哪,可以使用下面的 command
- 找到
pm2
的 process id
ps ax | grep PM2 |
- 然後查詢該 process 執行時所在的目錄(將上面得到的 process id 替換下面的
PM2_Process_ID
ls -l /proc/PM2_Process_ID/cwd |
- 公布結果
ls -l /proc/24016/cwd |
結果應該會如下, 最後的 deleted
表示該目錄已經被刪除了
lrwxrwxrwx 1 root root 0 Feb 4 17:04 /proc/24016/cwd -> /home/nodejs/deploy(deleted) |
現在知道原因了,那解決的方法呢? 我們要先把目前的 process 砍掉,然後到一個安全一點的地方在開啟一次,以免下次又被誤刪了!
殺掉 pm2 process id
kill -9 processID
到一個安全不會再被意外砍掉的目錄再次啟動
pm2
cd ~ && pm2 -v
留言