在现代 Web 开发中,Node.js 应用的部署和管理至关重要。为了确保应用的高可用性和性能,开发者常常采用 PM2(进程管理工具)和 Docker(容器化平台)的结合方案。本文将详细介绍 PM2 的功能、Docker 的优势,并展示如何在 Docker 容器中使用 PM2 管理 Node.js 应用,实现高效、稳定的部署。
1. 什么是 PM2?
PM2(Process Manager 2)是 Node.js 的流行进程管理工具,能够帮助开发者更好地管理和监控应用进程。以下是 PM2 的核心功能:
PM2 的主要功能
- 进程守护:应用崩溃后,PM2 会自动重启,确保服务高可用。
- 集群模式:通过多进程管理(
cluster
模式),充分利用多核 CPU,提升应用性能。 - 日志管理:支持日志分割、集中管理和实时查看,方便排查问题。
- 性能监控:提供实时资源使用情况(CPU、内存),支持告警功能。
- 零停机重启:通过
reload
命令实现无缝更新,避免服务中断。
PM2 的配置文件(如 ecosystem.config.js
)允许开发者定义应用的启动参数、环境变量和资源限制,简化了应用的部署和管理。
2. 什么是 Docker?
Docker 是一个容器化平台,允许开发者将应用及其依赖打包成镜像,并在任何支持 Docker 的环境中运行。Docker 的核心优势包括:
Docker 的主要优势
- 环境一致性:确保开发、测试和生产环境的依赖一致,避免“在我机器上能跑”问题。
- 资源隔离:限制容器的 CPU、内存等资源,防止单个应用影响宿主机或其他容器。
- 快速部署:通过镜像文件,实现“一次打包,到处运行”。
- 扩展性:结合 Kubernetes 或 Docker Swarm,轻松实现水平扩展和负载均衡。
3. 为什么在 Docker 中使用 PM2?
Docker 推荐“单进程容器”模式,即每个容器运行一个主进程。但 Node.js 是单线程的,直接运行多个 node
进程会违反这一原则。PM2 的引入解决了这一问题:
PM2 在 Docker 中的作用
- 单入口点:PM2 作为容器的主进程(PID 1),管理多个 Node.js 子进程。
- 优雅关闭:Docker 发送
SIGTERM
信号时,PM2 会优雅关闭所有子进程。 - 资源利用:通过 PM2 的集群模式,充分利用多核 CPU,提升应用性能。
- 日志管理:PM2 将日志输出到标准输出(
stdout
),由 Docker 收集,避免日志冗余。
4. Docker + PM2 的典型使用场景
4.1 高并发 API 网关
- 场景:需要处理每秒数千次请求的 API 网关。
- 优势:PM2 的集群模式利用多核 CPU,Docker 提供资源隔离和快速扩展。
4.2 微服务架构
- 场景:多个 Node.js 微服务需要独立管理。
- 优势:每个微服务打包为 Docker 镜像,PM2 管理容器内的多个子进程。
4.3 持续集成/持续部署(CI/CD)
- 场景:自动化测试、构建和部署。
- 优势:Docker 镜像标准化,PM2 支持零停机重启。
4.4 多环境一致性
- 场景:开发、测试和生产环境需要一致的配置。
- 优势:Docker 镜像确保依赖一致,PM2 配置文件统一管理。
5. Docker + PM2 的完整示例
以下是一个完整的 Node.js 应用示例,展示如何在 Docker 中使用 PM2 管理应用。
5.1 项目结构
node-pm2-docker/
├── app.js # Node.js 应用入口文件
├── ecosystem.config.js # PM2 配置文件
└── Dockerfile # Docker 配置文件
5.2 应用代码 (app.js
)
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;
app.use(express.json());
// 健康检查接口
app.get('/health', (req, res) => {
res.status(200).json({
status: 'ok',
pid: process.pid,
timestamp: new Date().toISOString()
});
});
// 示例接口
app.get('/api', (req, res) => {
res.send('Hello from Node.js PM2 Docker!');
});
const server = app.listen(port, () => {
console.log(`Server running on port ${port} (PID: ${process.pid})`);
});
// 优雅关闭处理
process.on('SIGTERM', () => {
console.log('SIGTERM signal received: closing HTTP server');
server.close(async (err) => {
if (err) {
console.error('Error closing server:', err);
} else {
console.log('Server closed');
}
});
});
5.3 PM2 配置文件 (ecosystem.config.js
)
module.exports = {
apps: [{
name: 'node-app',
script: './app.js',
instances: 'max',
exec_mode: 'cluster',
env: {
NODE_ENV: 'production'
},
max_memory: '512M',
log_date_format: 'YYYY-MM-DD HH:mm:ss',
out_file: '/dev/stdout',
err_file: '/dev/stderr'
}]
};
解释:
- “name”:应用名称,便于管理。
- “script”:应用入口文件。
- “instances”:启动的实例数量,设置为"max"表示根据CPU核心数自动确定。
- “exec_mode”:执行模式,"cluster"表示开启集群模式,负载均衡。
- “env”:环境变量配置,这里设置为生产环境。
- “max_memory”:内存使用限制,超过则自动重启。
- “log_file”、“out_file”、“err_file”:日志文件路径和格式,便于日志管理。
5.4 Docker 配置文件 (Dockerfile
)
dockerfile"># 使用 node:16 的官方镜像作为基础
FROM node:16
# 安装 PM2
RUN npm install pm2 -g
# 创建应用目录
WORKDIR /app
# 将整个项目文件复制到容器中
COPY . /app/
# 设置环境变量
ENV NODE_ENV production
# 设置容器停止时的信号处理
STOPSIGNAL SIGINT
# 启动命令:使用 PM2 启动应用
CMD ["pm2-runtime", "start", "ecosystem.config.js"]
5.5 构建和运行
- 构建 Docker 镜像:
docker build -t node-pm2-docker .
- 运行容器:
docker run -p 3000:3000 node-pm2-docker
- 验证应用状态:
docker exec -it node-pm2-docker pm2 ls
6. 总结
通过本文的介绍,可以看出 PM2 和 Docker 的结合使用为 Node.js 应用带来了以下优势:
- 高可用性:PM2 自动重启崩溃的进程,Docker 提供容器级别的资源隔离。
- 性能优化:PM2 的集群模式充分利用多核 CPU,Docker 提供快速扩展能力。
- 环境一致性:Docker 镜像确保依赖一致,PM2 配置文件统一管理。
- 日志管理:PM2 和 Docker 协同管理日志,方便排查问题。