跳到主要内容

部署

本章节介绍 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);
});

性能优化

  1. 启用 Gzip 压缩:使用 compression 中间件
  2. 静态文件缓存:设置合适的缓存头
  3. 数据库连接池:优化数据库连接
  4. 集群模式:利用多核 CPU
  5. CDN 加速:静态资源使用 CDN