部署与运维:用 PM2 管理进程、Docker 容器化部署、环境变量管理

信令服务器写好了,跑起来了,然后呢?

说实话,很多新手在这个阶段会松一口气。但真正的问题才刚刚开始——怎么让它稳定地跑在生产环境里? 我见过太多开发者在本地跑得欢,一上服务器就各种崩溃、内存泄漏、进程挂掉没人管。嗯,今天我们就来聊聊这个。

为什么需要进程管理?

你想想看,一个 WebRTC 信令服务器,它要同时处理成百上千的连接。Node.js 单线程模型虽然高效,但一旦遇到未捕获的异常,整个进程就挂了。用户那边就是一片「连接断开」的报错。

我早期做的一个项目,信令服务器平均每 3 小时崩溃一次。那时候我还手动 ssh 上去重启,后来实在受不了了,才老老实实上了 PM2。

PM2:你的进程保姆

PM2 说白了就是一个 Node.js 进程管理工具。它能帮你做三件事:守护进程、负载均衡、日志管理

安装与基本使用

# 全局安装
npm install -g pm2

# 启动信令服务器
pm2 start server.js --name webrtc-signal

# 查看进程状态
pm2 list

# 查看日志
pm2 logs webrtc-signal

# 重启
pm2 restart webrtc-signal

我个人习惯用 ecosystem.config.js 来管理配置,这样所有参数一目了然:

// ecosystem.config.js
module.exports = {
  apps: [{
    name: 'webrtc-signal',
    script: 'server.js',
    instances: 'max',          // 利用多核 CPU
    exec_mode: 'cluster',      // 集群模式
    env: {
      NODE_ENV: 'production',
      PORT: 3000
    },
    env_file: '.env',          // 环境变量文件
    max_memory_restart: '200M',// 内存超过 200M 自动重启
    log_date_format: 'YYYY-MM-DD HH:mm:ss',
    error_file: './logs/err.log',
    out_file: './logs/out.log',
    merge_logs: true
  }]
};
我的经验: 集群模式下,PM2 会启动多个 worker 进程,每个 worker 监听同一个端口。这样即使一个 worker 挂了,其他 worker 还能继续服务。我曾经用这个模式扛住了 5000 并发连接,稳得很。

常用运维命令

命令 作用
pm2 status 查看所有进程状态
pm2 monit 实时监控 CPU/内存
pm2 save 保存当前进程列表
pm2 startup 设置开机自启
pm2 delete webrtc-signal 删除进程
注意: 使用 pm2 startup 后,系统重启时会自动恢复所有进程。但前提是你之前执行过 pm2 save。我曾经忘了 save,服务器重启后一脸懵。

Docker 容器化部署

PM2 解决了进程管理的问题,但环境一致性问题呢?你本地跑得好好的,到服务器上就报错——这种「在我机器上没问题」的尴尬,我相信你遇到过。

Docker 就是来解决这个的。它把你的应用和所有依赖打包成一个镜像,走到哪跑到哪。

编写 Dockerfile

# 使用官方 Node.js 镜像
FROM node:18-alpine

# 设置工作目录
WORKDIR /app

# 复制 package.json 和 package-lock.json
COPY package*.json ./

# 安装依赖
RUN npm install --production

# 复制源代码
COPY . .

# 暴露端口
EXPOSE 3000

# 使用 PM2 启动
CMD ["npx", "pm2-runtime", "start", "ecosystem.config.js"]

这里有个细节:pm2-runtime 是 PM2 的 Docker 专用版本。它会把日志输出到 stdout,方便 Docker 的日志收集。

构建与运行

# 构建镜像
docker build -t webrtc-signal:v1 .

# 运行容器
docker run -d \
  --name signal-server \
  -p 3000:3000 \
  --env-file .env \
  --restart always \
  webrtc-signal:v1
避坑指南: 我曾经把 .env 文件直接 COPY 到镜像里,结果密钥泄露了。正确的做法是用 --env-file 在运行时注入环境变量,或者用 Docker Swarm 的 secrets 功能。

环境变量管理

信令服务器通常需要配置:端口号、JWT 密钥、数据库连接串、TLS 证书路径等。这些信息不应该硬编码在代码里。

12-Factor App 原则

说白了就是:配置和代码分离。环境变量是存储配置的最佳方式。

# .env 文件(不要提交到 Git)
PORT=3000
JWT_SECRET=your-secret-key-here
REDIS_URL=redis://localhost:6379
TLS_CERT=/etc/ssl/certs/server.crt
TLS_KEY=/etc/ssl/private/server.key
LOG_LEVEL=info

在代码中读取环境变量:

// config.js
const dotenv = require('dotenv');
dotenv.config();

module.exports = {
  port: process.env.PORT || 3000,
  jwtSecret: process.env.JWT_SECRET,
  redisUrl: process.env.REDIS_URL,
  tls: {
    cert: process.env.TLS_CERT,
    key: process.env.TLS_KEY
  },
  logLevel: process.env.LOG_LEVEL || 'debug'
};

不同环境的配置策略

环境 配置文件 特点
开发 .env.development 本地 Redis、调试日志
测试 .env.test 内存数据库、模拟证书
生产 .env.production 高安全级别、集群模式
我的习惯:package.json 的 scripts 里指定环境:"start:prod": "NODE_ENV=production pm2 start ecosystem.config.js"。这样不同环境用不同的配置,互不干扰。

知识体系总览

下面这张图总结了本章的核心逻辑:

信令服务器部署与运维体系 信令服务器代码 PM2 进程管理 Docker 容器化 环境变量管理 • 守护进程自动重启 • 集群模式负载均衡 • 日志管理与监控 • 开机自启 • 环境一致性 • 快速部署与回滚 • 资源隔离 • CI/CD 集成 • 配置与代码分离 • 多环境配置 • 敏感信息保护 • 运行时注入 稳定、可扩展的生产环境

写在最后

部署与运维这件事,说白了就是「让服务一直跑着,别出幺蛾子」。PM2 帮你管进程,Docker 帮你管环境,环境变量帮你管配置。这三板斧用好了,你的信令服务器就能稳稳地跑上几个月不用操心。

我记得有一次线上事故,Redis 挂了导致信令服务器全部报错。但因为 PM2 配置了自动重启,加上 Docker 的健康检查,整个服务在 30 秒内就恢复了。用户那边只是感觉「卡了一下」,没有造成大规模投诉。嗯,这就是运维的价值。


公众号:蓝海资料掘金营,微信 deep3321