☰
pm2 容器化部署实战:基于 pm2-runtime 与官方 Docker 镜像运行 Node.js 应用
2026/9/30 2:25:01 网站建设 项目流程
  • 运维
  • CLI
  • 可观测性

【免费下载链接】pm2

Node.js/Typescript/Bun Production Process Manager with a built-in Load Balancer.

项目地址:https://gitcode.com/gh_mirrors/pm/pm2
点击查看免费下载

本篇技术指南以仓库 examples/docker-pm2 示例为骨架,完整讲解如何在 Docker 容器中使用 pm2 官方镜像(keymetrics/pm2:latest-alpine)与pm2-runtime命令管理 Node.js 应用进程,包括镜像构建、应用配置文件编写、日志流转发与 Keymetrics 监控接入。读完本文,你将掌握一套可直接复制的"容器内进程托管"方案,并理解pm2-runtime作为"容器专用 PM2 运行时"的底层实现原理。

示例目录结构总览

examples/docker-pm2目录展示了一个最小可运行的容器化 PM2 示例,其完整结构如下:

examples/docker-pm2/ ├── README.md # 使用说明(本文即基于它展开) ├── Dockerfile # 镜像构建定义 └── app/ ├── app.js # Express 示例应用 ├── package.json # 应用依赖清单 └── process.config.js # PM2 进程配置文件(ecosystem 文件)

示例的技术要点一句话概括:把整个 PM2(含守护进程与监控 Agent)封进镜像,容器启动时直接执行pm2-runtime,让容器的主进程就是 PM2 本身,从而天然获得进程守护、自动重启、日志聚合等能力,同时避免"容器即进程"模型下 PID 1 信号转发问题的复杂性。

镜像构建:Dockerfile 逐行拆解

示例仓库根目录下的 Dockerfile 内容如下:

FROM keymetrics/pm2:latest-alpine # Bundle APP files COPY ./app /app WORKDIR /app # Install app dependencies ENV NPM_CONFIG_LOGLEVEL warn RUN npm install --production ENV KEYMETRICS_SECRET xxxx ENV KEYMETRICS_PUBLIC yyyy CMD [ "pm2-runtime", "process.config.js" ]

各指令的职责与注意事项:

指令作用实操建议
FROM keymetrics/pm2:latest-alpine使用 PM2 官方 Alpine 基础镜像,镜像内已内置pm2、pm2-runtime二进制及 Node.js 运行时生产环境建议固定到具体版本 tag(如latest-alpine的某个发行版),避免latest漂移
COPY ./app /app将本地app/目录(含应用代码、package.json、process.config.js)复制进镜像/app配合.dockerignore排除node_modules等无关文件,缩小镜像上下文
WORKDIR /app设置工作目录,后续npm install与pm2-runtime均在此目录执行保证process.config.js中相对路径./app.js能正确解析
ENV NPM_CONFIG_LOGLEVEL warn降低 npm 安装日志噪音可移除;如需离线构建可改用npm ci --production
RUN npm install --production仅安装生产依赖(本示例为express),不装devDependencies,显著缩小镜像体积若应用使用 ES Modules 或需编译原生模块,需在此安装对应构建工具链
ENV KEYMETRICS_SECRET xxxx/ENV KEYMETRICS_PUBLIC yyyy注入 Keymetrics(PM2 Plus)监控密钥生产环境应通过--build-arg或容器编排系统的 secret 机制注入,不要写死明文
CMD [ "pm2-runtime", "process.config.js" ]容器启动后由pm2-runtime加载并托管process.config.js中声明的应用也可直接写CMD [ "pm2-runtime", "app.js" ]托管单个脚本

注意CMD使用exec 形式(JSON 数组)而非 shell 形式,这样容器 PID 1 进程就是pm2-runtime,可正确接收 Docker/K8s 下发的SIGTERM等信号并完成优雅退出。

应用代码与 PM2 进程配置

示例应用是一个标准的 Express HTTP 服务(examples/docker-pm2/app/app.js):

const express = require('express') const app = express() app.get('/', (req, res) => res.send('Hello World!')) app.listen(3000, () => console.log('Example app listening on port 3000!'))

依赖清单声明在 examples/docker-pm2/app/package.json:

{ "name": "app", "version": "1.0.0", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "license": "ISC", "dependencies": { "express": "^4.16.2" } }

真正决定 PM2 如何托管应用的是进程配置文件 examples/docker-pm2/app/process.config.js:

module.exports = { apps : [{ name : "express-app", script : "./app.js" }] }

这是一个标准的 PM2 ecosystem 配置文件(CommonJS 导出),apps数组内每个对象代表一个被托管的进程。name用于在pm2 list/pm2 logs中标识应用,script为相对WORKDIR的入口脚本。在此基础上可继续扩展大量生产级配置项,例如:

module.exports = { apps : [{ name : "express-app", script : "./app.js", instances : 2, // 多实例,配合内置负载均衡(cluster 模式) exec_mode : "cluster", // fork | cluster max_memory_restart : "100M", // 内存超限自动重启 watch : false, env : { NODE_ENV: "production" }, error_file : "/dev/null", // 错误日志重定向 out_file : "/dev/null", // 输出日志重定向 time : true // 日志行附加时间戳 }] }

这些字段会由 PM2 的启动逻辑解析并作用于进程生命周期管理,具体行为可对照 lib/binaries/Runtime4Docker.js 中透传给pm2.start(cmd, commander, ...)的选项处理。

构建与运行:三条核心命令

原文档给出了完整的三步操作流程,直接可执行:

# 1. 构建镜像 $ docker build -t docker-pm2-test . # 2. 查看本机镜像列表,确认构建成功 $ docker images # 3. 运行容器 $ docker run docker-pm2-test

运行后观察容器日志,应依次出现 PM2 启动信息与应用自身的输出:

Example app listening on port 3000!

容器内pm2-runtime会把 PM2 的日志流(stdout/stderr)直接打到容器标准输出,因此使用docker logs docker-pm2-test即可实时查看被托管应用的日志,这正是pm2-runtime相对常规pm2命令在容器场景下的关键差异(详见下文原理小节)。

如需验证应用可用性,可追加端口映射后访问:

$ docker run -p 3000:3000 docker-pm2-test $ curl http://localhost:3000/ # Hello World!

深入原理:pm2-runtime 为什么适合容器

pm2-runtime是 PM2 为容器场景(Docker、Kubernetes 等)提供的专用二进制,其完整 CLI 实现位于 lib/binaries/Runtime4Docker.js,代码注释将其明确定义为:

Specialized PM2 CLI for Containers(容器专用 PM2 命令行)

与常规 pm2 命令的本质区别

常规pm2采用守护进程(daemon)架构:CLI 进程与长期驻留的 God 守护进程分离,命令执行后 CLI 即退出,守护进程在后台持续托管应用。这在宿主机场景没问题,但在容器中会产生"容器主进程退出、后台守护进程孤儿化"等管理难题。

pm2-runtime则运行在no-daemon 模拟模式下:Runtime4Docker.js内部通过new PM2.custom({ daemon_mode: false, ... })创建内嵌 PM2 实例,容器主进程即 PM2 本身,被托管应用的生命周期与容器完全绑定——容器停止,进程随之回收;进程崩溃,PM2 按配置自动拉起。

关键实现细节(源码证据)

从 lib/binaries/Runtime4Docker.js 可以观察到以下几点:

  • 信号处理:实例启动后立即注册SIGINT/SIGTERM处理器,收到信号即调用Runtime.exit(),先执行pm2.kill()清理全部托管进程再process.exit,实现优雅退出(Runtime.exit定义于Runtime.js对应逻辑的对称实现中,见 lib/binaries/Runtime.js 的exitPM2);
  • 日志流直通容器 stdout:startLogStreaming()依据 CLI 选项在Log.jsonStream/Log.formatStream/Log.stream之间选择,默认把 PM2 全部应用日志透传到标准输出,天然适配docker logs;
  • 内嵌 PM2 实例:pm2_home、secret_key、public_key、machine_name均通过环境变量或 CLI 选项注入,其中密钥优先级为constants.js中的环境变量优先、命令行参数兜底。

pm2-runtime 常用选项

Runtime4Docker.js中通过commander注册的选项即为容器场景下的完整能力面,常用项整理如下:

选项含义
-i, --instances <number>启动 N 个实例并自动负载均衡(cluster 模式)
--no-autorestart启动应用但禁止自动重启
--stop-exit-codes <codes...>指定一组退出码,命中时跳过自动重启
--max-memory-restart <memory>超过内存阈值(如100M)自动重启
-c, --cron <pattern>按 cron 表达式定时重启应用
--interpreter <interpreter>指定解释器(bash、python 等)
--delay <seconds>延迟 N 秒后再加载配置文件启动
--web [port]在指定端口(默认 9615)启动 Web API
--env <name>注入配置文件中的env_<name>环境变量块
--watch监听文件变化并自动重启
--json/--format以 JSON 或key=val格式输出日志
--no-auto-exit全部进程出错/停止时也不自动退出

其中--no-autorestart、--stop-exit-codes、--delay、--only <app-name>等选项在Runtime4Docker.js的commander.option(...)注册列表中均有对应声明,说明容器运行时对"重启策略"的控制粒度比常规命令行更细,适配 K8s 下由编排层决定重启而非 PM2 自行无限重试的场景。

无应用存活时的自动退出

容器场景一个值得注意的细节:Runtime4Docker.js内置了autoExitWorker(配合--auto-exit),默认失败容忍次数DEFAULT_FAIL_COUNT = 3,每 2 秒轮询一次pm2.list,若检测到0 个应用处于online/launching状态,则连续重试 3 次后调用Runtime.exit(2)使容器退出——避免容器"空转"占资源。这一点在 test/e2e/binaries/pm2-runtime.sh 中有对应测试(pm2_runtime exited_app.js启动注定崩溃的应用后,断言 PM2 进程最终被回收)。

测试验证:pm2-runtime 的自动化用例

仓库 test/e2e/binaries/pm2-runtime.sh 给出了pm2-runtime的端到端验证方式,可作为理解其行为契约的补充:

# 启动 4 个实例,断言有 4 个进程处于 online 状态 $pm2_runtime app.js -i 4 should 'should have started 4 apps' 'online' 4 # 通过 JSON 配置文件启动,并验证 watch / ignore_watch 透传生效 $pm2_runtime app.json $pm2 prettylist | grep "watch: \[ 'server', 'client' \]" $pm2 prettylist | grep "ignore_watch: \[ 'node_modules', 'client/img' \]"

可见pm2-runtime与常规pm2共享同一套应用配置解析与进程管理内核,instances、watch、ignore_watch等配置均完整生效,容器内外行为保持一致。

集成 Keymetrics(PM2 Plus)监控

原文档明确提及示例内含 Keymetrics 集成能力,实现方式即 Dockerfile 中的两个环境变量:

ENV KEYMETRICS_SECRET xxxx ENV KEYMETRICS_PUBLIC yyyy

这两个变量在 constants.js 中被正式消费:

MACHINE_NAME : process.env.INSTANCE_NAME || process.env.MACHINE_NAME || process.env.PM2_MACHINE_NAME, SECRET_KEY : process.env.KEYMETRICS_SECRET || process.env.PM2_SECRET_KEY || process.env.SECRET_KEY, PUBLIC_KEY : process.env.KEYMETRICS_PUBLIC || process.env.PM2_PUBLIC_KEY || process.env.PUBLIC_KEY,

也就是说,只要在镜像或容器运行时注入合法的KEYMETRICS_SECRET/KEYMETRICS_PUBLIC(分别对应密钥对中的 secret 与 public),pm2-runtime在 Runtime4Docker.js 中创建内嵌 PM2 实例时就会自动带上这对密钥,从而把应用指标(事件循环延迟、内存、HTTP 请求等)推送至 Keymetrics 平台;INSTANCE_NAME或MACHINE_NAME则用于标识该容器在监控面板中的机器名。与之等效的运行时方式是在docker run时用-e覆盖:

$ docker run -e KEYMETRICS_SECRET=<你的secret> \ -e KEYMETRICS_PUBLIC=<你的public> \ -e INSTANCE_NAME=my-container \ docker-pm2-test

若暂不使用监控,把KEYMETRICS_SECRET/KEYMETRICS_PUBLIC置空或删除即可,不影响进程托管主流程。此外constants.js还支持PM2_SECRET_KEY/PM2_PUBLIC_KEY与PM2_MACHINE_NAME等别名变量,便于在既有 CI/CD 环境中统一注入。

生产化改造建议

基于本示例可以继续演进,以下是贴合容器/编排场景的实用方向:

  1. 固定镜像 tag:将latest-alpine替换为具体版本号(如keymetrics/pm2:12-alpine或对应 Node 版本的 tag),保证可复现构建;
  2. 多实例负载均衡:在process.config.js中设置instances与exec_mode: "cluster",由 PM2 内置负载均衡器分发请求(对应Runtime4Docker.js中-i选项的官方说明);
  3. 健康检查:为应用增加/healthz端点,并在 Dockerfile 中配置HEALTHCHECK,或依赖 K8s liveness/readiness probe,与pm2-runtime的退出码语义配合;
  4. 密钥安全:KEYMETRICS_SECRET/KEYMETRICS_PUBLIC应通过docker --build-arg、Docker secret 或 K8s ConfigMap/Secret 注入,绝不硬编码进镜像;
  5. 日志标准化:按需使用--json或--format输出结构化日志,便于采集到 ELK/Loki 等日志平台;
  6. 冷启动优化:对于频繁启停的短生命周期容器,可评估--fast-boot(见 lib/binaries/Runtime.js 的--fast-boot选项)以复用后台 PM2 实例、缩短二次启动耗时。

小结

本示例演示的"官方镜像 +pm2-runtime+ ecosystem 配置文件"三件套,是 PM2 官方推荐的容器内进程管理范式:pm2-runtime以 no-daemon 模式将 PM2 变为容器主进程,统一了进程守护、自动重启、日志聚合与 Keymetrics 监控,同时通过 exec 形式CMD与信号处理器保证容器优雅退出。无论是单一服务容器,还是需要多实例负载均衡与监控接入的生产集群,都可以从 examples/docker-pm2 这套最小示例出发快速落地。

  • 运维
  • CLI
  • 可观测性

【免费下载链接】pm2

Node.js/Typescript/Bun Production Process Manager with a built-in Load Balancer.

项目地址:https://gitcode.com/gh_mirrors/pm/pm2
点击查看免费下载

相关推荐

上一篇:XUnity.AutoTranslator:打破语言障碍的Unity游戏翻译神器终极指南
下一篇:AO3镜像站完全指南:3分钟解锁全球同人创作自由

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询