PM2實用指南及容器Docker部署

Node.js 默認單進程運行,對于32位系統最高可以使用 512MB 內存,對于64位最高可以使用 1GB 內存。對于多核CPU的計算機來說,這樣做效率很低,因為只有一個核在運行,其他核都在閑置,pm2 利用的 node 原生的 cluster 模塊可以順利解決該問題。
pm2 是一個帶有負載均衡功能的應用進程管理器,可以使 node 服務在后臺運行。
安裝
npm install pm2 -g
PM2常用命令
app.js為 api-service 服務的啟動程序,在生產環境中使用 pm2 進行管理
- 啟動
pm2 start app.js --name api-service
pm2 start app.js --watch # 實時監控 app.js 的方式啟動,當app.js文件有變動時,pm2會自動reload

- 查看進程
pm2 list
pm2 show 0 或者 # pm2 info 0 #查看進程詳細信息,0為PM2進程id

- 監控
pm2 monit

- 停止
pm2 stop all #停止PM2列表中所有的進程
pm2 stop 0 #停止PM2列表中進程為0的進程
- 重載
pm2 reload all #重載PM2列表中所有的進程
pm2 reload 0 #重載PM2列表中進程為0的進程
- 重啟
pm2 restart all #重啟PM2列表中所有的進程
pm2 restart 0 #重啟PM2列表中進程為0的進程
- 刪除PM2進程
pm2 delete 0 #刪除PM2列表中進程為0的進程
pm2 delete all #刪除PM2列表中所有的進程
自動啟動文件
生成腳本
pm2 ecosystem
創建文件:/api-service/ecosystem.config.js
module.exports = {
apps: [
{
name: "api-service",
script: "app.js",
merge_logs: true,
max_restarts: 20,
instances: 1,
max_memory_restart: "2G",
cwd: "/website/api-service/",
env: {
NODE_ENV: "development",
},
env_production: {
NODE_ENV: "production",
},
},
],
};
說明:
-
apps:json結構,apps是一個數組,每一個數組成員就是對應一個pm2中運行的應用 -
name:應用程序的名稱 -
cwd:應用程序所在的目錄 -
script:應用程序的腳本路徑 -
exec_interpreter:應用程序的腳本類型,這里使用的shell,默認是nodejs。 -
min_uptime:最小運行時間,這里設置的是60s即如果應用程序在60s內退出,pm2會認為程序異常退出,此時觸發重啟max_restarts設置數量 -
max_restarts:設置應用程序異常退出重啟的次數,默認15次(從0開始計數) -
exec_mode:應用程序啟動模式,這里設置的是cluster_mode(集群),默認是fork -
error_file:自定義應用程序的錯誤日志文件 -
out_file:自定義應用程序日志文件 -
pid_file:自定義應用程序的pid文件 -
watch:是否啟用監控模式,默認是false,如果設置成true,當應用程序變動時,pm2會自動重載,這里也可以設置你要監控的文件。
執行腳本
pm2 start /website/api-service/ecosystem.config.js

重啟
sudo reboot
查看進程
pm2 list
Dockerfile
上面介紹的安裝、部署、啟動等操作都可以使用 Docker 簡單的完成,關于 Docker 的使用可以參閱《面向WEB開發人員的Docker》。一般項目完整的環境包括 Node 作為后臺服務,Vue 或者 Angular 作為前端,那么生產環境可以選擇 Nginx + Node + pm2 ,Nginx 作為 Web 項目的入口。這里在創建 Dockerfile 是以 nginx:1.21.1-alpine 作為基礎,完整代碼如下:
FROM nginx:1.21.1-alpine
# Stream the nginx logs to stdout and stderr
RUN ln -sf /dev/stdout /var/log/nginx/access.log && \
ln -sf /dev/stderr /var/log/nginx/error.log
# 安裝nodejs
ENV NODE_VERSION 16.6.1
RUN addgroup -g 1000 node \
&& adduser -u 1000 -G node -s /bin/sh -D node \
&& apk add --no-cache \
libstdc++ \
&& apk add --no-cache --virtual .build-deps \
curl \
&& ARCH= && alpineArch="$(apk --print-arch)" \
&& case "${alpineArch##*-}" in \
x86_64) \
ARCH='x64' \
CHECKSUM="9c8438a8d9a1e268153812d1d3f7f63b02283e2082dcd39274674f897496a22a" \
;; \
*) ;; \
esac \
&& if [ -n "${CHECKSUM}" ]; then \
set -eu; \
curl -fsSLO --compressed "https://unofficial-builds.nodejs.org/download/release/v$NODE_VERSION/node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz"; \
echo "$CHECKSUM node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz" | sha256sum -c - \
&& tar -xJf "node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz" -C /usr/local --strip-components=1 --no-same-owner \
&& ln -s /usr/local/bin/node /usr/local/bin/nodejs; \
else \
echo "Building from source" \
# backup build
&& apk add --no-cache --virtual .build-deps-full \
binutils-gold \
g++ \
gcc \
gnupg \
libgcc \
linux-headers \
make \
python3 \
# gpg keys listed at https://github.com/nodejs/node#release-keys
&& for key in \
4ED778F539E3634C779C87C6D7062848A1AB005C \
94AE36675C464D64BAFA68DD7434390BDBE9B9C5 \
74F12602B6F1C4E913FAA37AD3A89613643B6201 \
71DCFD284A79C3B38668286BC97EC7A07EDE3FC1 \
8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600 \
C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8 \
C82FA3AE1CBEDC6BE46B9360C43CEC45C17AB93C \
DD8F2338BAE7501E3DD5AC78C273792F7D83545D \
A48C2BEE680E841632CD4E44F07496B3EB3C1762 \
108F52B48DB57BB0CC439B2997B01419BD92F80A \
B9E2F5981AA6E0CD28160D9FF13993A75599653C \
; do \
gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys "$key" || \
gpg --batch --keyserver keyserver.ubuntu.com --recv-keys "$key" ; \
done \
&& curl -fsSLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION.tar.xz" \
&& curl -fsSLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc" \
&& gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \
&& grep " node-v$NODE_VERSION.tar.xz\$" SHASUMS256.txt | sha256sum -c - \
&& tar -xf "node-v$NODE_VERSION.tar.xz" \
&& cd "node-v$NODE_VERSION" \
&& ./configure \
&& make -j$(getconf _NPROCESSORS_ONLN) V= \
&& make install \
&& apk del .build-deps-full \
&& cd .. \
&& rm -Rf "node-v$NODE_VERSION" \
&& rm "node-v$NODE_VERSION.tar.xz" SHASUMS256.txt.asc SHASUMS256.txt; \
fi \
&& rm -f "node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz" \
&& apk del .build-deps \
# smoke tests
&& node --version \
&& npm --version
# NODEJS服務
ENV SERVICE_WORKDIR=/webapps/api-service
WORKDIR $SERVICE_WORKDIR
COPY ./api-service/package.json /webapps/api-service/package.json
RUN npm install && npm cache clean --force
RUN npm install pm2 -g
COPY ./api-service/config /webapps/api-service/config
COPY ./api-service/src/controllers /webapps/api-service/src/controllers
COPY ./api-service/src/models /webapps/api-service/src/models
COPY ./api-service/src/services /webapps/api-service/src/services
COPY ./api-service/src/routers /webapps/api-service/src/routers
COPY ./api-service/src/utils /webapps/api-service/src/utils
COPY ./api-service/app.js /webapps/api-service
COPY ./api-service/ecosystem.config.js /webapps/api-service
RUN mkdir ~/.pm2
RUN chmod 755 -R ~/.pm2
# VUE前端
WORKDIR /webapps/app
COPY ./dist /webapps/app
COPY ./etc/nginx/default.conf /etc/nginx/conf.d/
RUN chmod 755 -R /webapps/app \
&& chmod 755 -R /usr/local/bin \
&& chmod 755 -R /webapps/api-service
EXPOSE 80
WORKDIR /webapps/api-service
COPY start.sh .
CMD [ "./start.sh" ]
./start.sh 的腳本如下:
#!/bin/sh
nginx
pm2 start /webapps/api-service/ecosystem.config.js --no-daemon
nginx 配置文件 ./etc/nginx/default.conf 的代碼如下:
server{
listen 80;
root /webapps/app;
index index.html;
charset utf-8;
add_header "X-UA-Compatible" "IE=Edge,chrome=1";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
location /api {
proxy_pass http://127.0.0.1:4200;
}
location / {
if ($request_filename ~* ^.*?.(html|htm)$){
expires -1s;
add_header Cache-Control no-cache,no-store,must-revalidate;
}
try_files $uri $uri/ /index.html;
}
}