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;
}
}