部署
本章节介绍 Express.js 应用的部署方法和生产环境配置。
生产环境配置
环境变量
NODE_ENV=production
PORT=3000
MONGODB_URI=mongodb://localhost:27017/myapp
JWT_SECRET=your-secret-key
生产配置
const express = require('express');
const helmet = require('helmet');
const compression = require('compression');
const rateLimit = require('express-rate-limit');
const app = express();
app.set('trust proxy', 1);
app.use(helmet());
app.use(compression());
app.use(express.json({ limit: '10kb' }));
const limiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 100
});
app.use('/api', limiter);
PM2 部署
安装 PM2
npm install -g pm2
PM2 配置
ecosystem.config.js:
module.exports = {
apps: [{
name: 'express-app',
script: './src/app.js',
instances: 'max',
exec_mode: 'cluster',
autorestart: true,
watch: false,
max_memory_restart: '1G',
env: {
NODE_ENV: 'development',
PORT: 3000
},
env_production: {
NODE_ENV: 'production',
PORT: 3000
}
}]
};
PM2 命令
pm2 start ecosystem.config.js --env production
pm2 stop express-app
pm2 restart express-app
pm2 logs express-app
pm2 monit
pm2 save
pm2 startup
Docker 部署
Dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
USER node
CMD ["node", "src/app.js"]
Docker Compose
docker-compose.yml:
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- MONGODB_URI=mongodb://mongo:27017/myapp
- REDIS_URL=redis://redis:6379
depends_on:
- mongo
- redis
restart: always
mongo:
image: mongo:6
volumes:
- mongo_data:/data/db
restart: always
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
restart: always
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./ssl:/etc/nginx/ssl
depends_on:
- app
restart: always
volumes:
mongo_data:
redis_data:
Docker 命令
docker build -t express-app .
docker run -p 3000:3000 express-app
docker-compose up -d
docker-compose logs -f
docker-compose down
Nginx 反向代理
Nginx 配置
nginx.conf:
upstream express_app {
server app:3000;
keepalive 64;
}
server {
listen 80;
server_name example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
proxy_pass http://express_app;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
location /static {
alias /app/public;
expires 1y;
add_header Cache-Control "public, immutable";
}
}
云平台部署
Heroku
heroku create my-express-app
heroku config:set MONGODB_URI=your-mongodb-uri
heroku config:set JWT_SECRET=your-secret
git push heroku main
Vercel
vercel.json:
{
"version": 2,
"builds": [
{
"src": "src/app.js",
"use": "@vercel/node"
}
],
"routes": [
{
"src": "/(.*)",
"dest": "src/app.js"
}
]
}
Railway
railway init
railway run node src/app.js
railway up
监控与日志
日志中间件
const morgan = require('morgan');
const fs = require('fs');
const path = require('path');
const accessLogStream = fs.createWriteStream(
path.join(__dirname, 'access.log'),
{ flags: 'a' }
);
app.use(morgan('combined', { stream: accessLogStream }));
健康检查
app.get('/health', (req, res) => {
const health = {
uptime: process.uptime(),
timestamp: Date.now(),
status: 'healthy'
};
res.json(health);
});
进程监控
process.on('uncaughtException', (err) => {
console.error('Uncaught Exception:', err);
process.exit(1);
});
process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled Rejection:', reason);
process.exit(1);
});
性能优化
- 启用 Gzip 压缩:使用 compression 中间件
- 静态文件缓存:设置合适的缓存头
- 数据库连接池:优化数据库连接
- 集群模式:利用多核 CPU
- CDN 加速:静态资源使用 CDN