在服务器部署Node.js应用时,环境差异和进程管理是两大痛点。本文将详细介绍如何通过DockerPM2的组合拳,在CentOS上部署NestJS应用,实现环境标准化和进程高可用。


一、为什么选择Docker+PM2?
  1. Docker的价值

    • 构建一次,到处运行:解决"本地正常,线上报错"的经典问题
    • 资源隔离:避免多个应用互相影响
    • 版本控制:镜像tag实现秒级回滚
  2. PM2的核心优势

    • 多进程管理:榨干多核CPU性能
    • 故障自愈:应用崩溃自动重启
    • 日志集中化:告别散落的log文件

二、PM2配置解析(ecosystem.config.ts)
module.exports = {
  apps: [{
    name: 'nestjs-app',       // 服务标识名
    script: './dist/src/main.js', // 入口文件路径
    instances: 'max',          // 按CPU核心数最大化进程
    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'    // 错误输出流
  }]
};

配置亮点:

  • instances: 'max':自动检测CPU核心数启动进程(比如8核CPU启动8个进程)
  • 日志重定向到/dev/stdout:让Docker捕获日志
  • 内存限制:防止单个进程内存泄漏拖垮主机

三、Dockerfile深度优化
# 使用官方Node 20镜像
FROM node:20-alpine

# 设置容器内工作目录
WORKDIR /app

# 安装全局工具 - 使用alpine镜像加速
RUN npm install -g pnpm pm2

# 先拷贝依赖文件(利用Docker缓存层)
COPY package.json pnpm-lock.yaml ./

# 安装依赖 - 锁定版本保证一致性
RUN pnpm install --frozen-lockfile

# 拷贝项目文件(注意.dockerignore排除node_modules)
COPY . .

# 构建生产环境代码
RUN pnpm run build

# 删除开发依赖 - 减少镜像体积
RUN pnpm prune --production

# 设置时区(重要!)
RUN apk add --no-cache tzdata && \
    cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

# 暴露应用端口(与NestJS监听端口一致)
EXPOSE 8080

# 容器启动命令 - 使用pm2-runtime
CMD ["pm2-runtime", "dist/ecosystem.config.js"]

关键优化点:

  1. 使用node:20-alpine基础镜像(体积比普通镜像小60%)
  2. 分阶段复制文件:先复制package.json安装依赖,再复制源码
  3. --frozen-lockfile:锁定依赖版本,避免意外升级
  4. pnpm prune --production:移除开发依赖,镜像体积减少40%

四、Docker构建与运行详解
1. 镜像构建最佳实践
# 在项目根目录执行(包含Dockerfile)
docker build \
  -t nestjs-app:1.0.0 \    # 打上语义化版本标签
  --build-arg APP_ENV=production \  # 构建参数传递
  .   # 注意最后的点表示当前目录

构建参数说明:

  • -t:给镜像命名并打标签(推荐语义化版本)
  • --build-arg:传递构建时参数(可在Dockerfile中用ARG接收)
  • .:指定Docker上下文路径
2. 容器运行关键参数
docker run -d \
  --name nestjs-production \  # 容器命名
  -p 8080:8080 \             # 端口映射(主机:容器)
  -v ./logs:/app/logs \       # 日志持久化
  --memory=1g \              # 限制最大内存
  --cpus=2 \                 # 限制CPU核数
  --restart=unless-stopped \ # 自动重启策略
  -e TZ=Asia/Shanghai \      # 时区设置
  nestjs-app:1.0.0

参数解析:

参数 作用 示例值
-p 端口映射 8080:8080
-v 数据卷挂载 /host/path:/container/path
--memory 内存限制 512m/2g
--cpus CPU限制 1.5(1.5个核心)
--restart 重启策略 no/always/on-failure
-e 环境变量 NODE_ENV=production
3. 生产环境常用命令
# 查看容器实时日志
docker logs -f nestjs-production

# 进入容器诊断
docker exec -it nestjs-production sh

# 查看资源占用
docker stats nestjs-production

# 更新服务(先构建新镜像)
docker stop nestjs-production
docker rm nestjs-production
docker run ...(使用新镜像启动)

五、避坑指南
  1. 时区问题
    在Dockerfile中安装tzdata并设置时区,避免日志时间戳错误

  2. 内存不足
    Docker运行参数需大于PM2配置的内存总和:
    容器内存 >= PM2进程数 × max_memory

  3. 权限问题
    在Dockerfile中添加用户权限管理:

    RUN adduser -D appuser
    USER appuser
    
  4. 构建缓存失效
    将易变文件(如代码)放在Dockerfile后面:

    COPY package.json ./   # 先复制
    RUN pnpm install
    COPY . .               # 后复制代码
    

六、为什么选择这个方案?
  1. 效率提升

    • 构建时间减少50%:利用Docker层缓存
    • 资源利用率提升300%:PM2集群模式榨干CPU性能
  2. 运维便捷

    • 一键回滚:docker run nestjs-app:1.0.0
    • 日志集中:docker logs查看所有进程日志
  3. 成本优化

    • 镜像体积缩小60%:使用alpine基础镜像
    • 内存消耗降低40%:生产依赖修剪

最终部署效果:

  • 单容器内运行多个Node进程
  • 进程崩溃自动重启
  • 资源占用可视化监控
  • 部署过程完全标准化
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐