部署与运维:用 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 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-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 帮你管环境,环境变量帮你管配置。这三板斧用好了,你的信令服务器就能稳稳地跑上几个月不用操心。
我记得有一次线上事故,Redis 挂了导致信令服务器全部报错。但因为 PM2 配置了自动重启,加上 Docker 的健康检查,整个服务在 30 秒内就恢复了。用户那边只是感觉「卡了一下」,没有造成大规模投诉。嗯,这就是运维的价值。
公众号:蓝海资料掘金营,微信 deep3321