部署上线 #

一、部署概述 #

本章将介绍Fastify应用的生产环境部署方法。

1.1 部署方式 #

方式 适用场景 优点
PM2 VPS/云服务器 简单易用、自动重启
Docker 容器化部署 环境一致、易于扩展
Serverless 无服务器架构 按需付费、自动扩展
Kubernetes 大规模部署 高可用、自动扩展

1.2 部署清单 #

  • [ ] 环境变量配置
  • [ ] 日志配置
  • [ ] 错误处理
  • [ ] 健康检查
  • [ ] 安全配置
  • [ ] 性能优化
  • [ ] 监控告警

二、生产环境配置 #

2.1 环境变量 #

.env.production

env
NODE_ENV=production
PORT=3000
HOST=0.0.0.0
LOG_LEVEL=info

DATABASE_URL=mongodb://production-server:27017/myapp
REDIS_URL=redis://production-server:6379

JWT_SECRET=your-production-secret-key
JWT_EXPIRES_IN=15m

2.2 生产配置 #

src/config/production.js

javascript
export default {
  logger: {
    level: 'info',
    redact: ['req.headers.authorization', 'req.headers.cookie'],
    serializers: {
      req(req) {
        return {
          method: req.method,
          url: req.url,
          headers: {
            'user-agent': req.headers['user-agent']
          }
        }
      }
    }
  },
  
  trustProxy: true,
  ignoreTrailingSlash: true,
  bodyLimit: 1048576
}

2.3 优雅关闭 #

src/server.js

javascript
import Fastify from 'fastify'
import config from './config/index.js'
import app from './app.js'

const fastify = Fastify(config)

fastify.register(app)

const signals = ['SIGINT', 'SIGTERM']

for (const signal of signals) {
  process.on(signal, async () => {
    fastify.log.info(`Received ${signal}, shutting down...`)
    
    await fastify.close()
    
    fastify.log.info('Server closed')
    process.exit(0)
  })
}

const start = async () => {
  try {
    await fastify.listen({
      port: config.port,
      host: config.host
    })
    
    fastify.log.info(`Server running on ${config.host}:${config.port}`)
  } catch (err) {
    fastify.log.error(err)
    process.exit(1)
  }
}

start()

三、PM2部署 #

3.1 安装PM2 #

bash
npm install -g pm2

3.2 PM2配置 #

ecosystem.config.js

javascript
module.exports = {
  apps: [{
    name: 'fastify-app',
    script: './src/server.js',
    instances: 'max',
    exec_mode: 'cluster',
    autorestart: true,
    watch: false,
    max_memory_restart: '1G',
    env_production: {
      NODE_ENV: 'production',
      PORT: 3000
    },
    env_staging: {
      NODE_ENV: 'staging',
      PORT: 3000
    },
    log_date_format: 'YYYY-MM-DD HH:mm:ss',
    error_file: './logs/error.log',
    out_file: './logs/out.log',
    merge_logs: true
  }]
}

3.3 PM2命令 #

bash
pm2 start ecosystem.config.js --env production

pm2 stop fastify-app

pm2 restart fastify-app

pm2 delete fastify-app

pm2 logs fastify-app

pm2 monit

pm2 list

pm2 save

pm2 startup

3.4 PM2监控 #

bash
pm2 install pm2-logrotate

pm2 set pm2-logrotate:max_size 10M
pm2 set pm2-logrotate:retain 7

四、Docker部署 #

4.1 Dockerfile #

Dockerfile

dockerfile
FROM node:20-alpine AS builder

WORKDIR /app

COPY package*.json ./

RUN npm ci --only=production

FROM node:20-alpine

WORKDIR /app

RUN addgroup -g 1001 -S nodejs
RUN adduser -S fastify -u 1001

COPY --from=builder --chown=fastify:nodejs /app/node_modules ./node_modules
COPY --chown=fastify:nodejs . .

USER fastify

EXPOSE 3000

ENV NODE_ENV=production
ENV PORT=3000

CMD ["node", "src/server.js"]

4.2 Docker Compose #

docker-compose.yml

yaml
version: '3.8'

services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - DATABASE_URL=mongodb://mongo:27017/myapp
      - REDIS_URL=redis://redis:6379
    depends_on:
      - mongo
      - redis
    restart: always
    healthcheck:
      test: ["CMD", "wget", "-q", "--spider", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3

  mongo:
    image: mongo:7
    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:ro
      - ./ssl:/etc/nginx/ssl:ro
    depends_on:
      - app
    restart: always

volumes:
  mongo_data:
  redis_data:

4.3 Nginx配置 #

nginx.conf

nginx
events {
    worker_connections 1024;
}

http {
    upstream fastify_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://fastify_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 /health {
            proxy_pass http://fastify_app/health;
            access_log off;
        }
    }
}

4.4 Docker命令 #

bash
docker build -t fastify-app .

docker run -p 3000:3000 fastify-app

docker-compose up -d

docker-compose logs -f app

docker-compose down

五、健康检查 #

5.1 健康检查端点 #

javascript
fastify.get('/health', async (request, reply) => {
  return {
    status: 'ok',
    timestamp: new Date().toISOString(),
    uptime: process.uptime()
  }
})

fastify.get('/ready', async (request, reply) => {
  const checks = {
    database: await checkDatabase(),
    redis: await checkRedis()
  }
  
  const healthy = Object.values(checks).every(v => v)
  
  reply.code(healthy ? 200 : 503)
  return { checks, healthy }
})

async function checkDatabase() {
  try {
    await fastify.mongo.db.admin().ping()
    return true
  } catch {
    return false
  }
}

async function checkRedis() {
  try {
    await fastify.redis.ping()
    return true
  } catch {
    return false
  }
}

六、日志管理 #

6.1 日志配置 #

javascript
const fastify = require('fastify')({
  logger: {
    level: 'info',
    file: './logs/app.log',
    redact: ['req.headers.authorization'],
    formatters: {
      level: (label) => {
        return { level: label.toUpperCase() }
      }
    }
  }
})

6.2 日志轮转 #

使用pm2-logrotate或logrotate:

/etc/logrotate.d/fastify-app

text
/var/log/fastify-app/*.log {
    daily
    rotate 14
    compress
    delaycompress
    notifempty
    create 0640 www-data www-data
    sharedscripts
    postrotate
        pm2 reloadLogs
    endscript
}

七、监控告警 #

7.1 Prometheus监控 #

javascript
fastify.register(require('@fastify/under-pressure'), {
  maxEventLoopDelay: 1000,
  maxHeapUsedBytes: 100000000,
  maxRssBytes: 100000000
})

fastify.get('/metrics', async (request, reply) => {
  return {
    memory: fastify.memoryUsage(),
    eventLoopDelay: fastify.eventLoopDelay(),
    cpuUsage: process.cpuUsage()
  }
})

7.2 APM集成 #

使用Elastic APM:

javascript
require('elastic-apm-node').start({
  serviceName: 'fastify-app',
  serverUrl: process.env.APM_SERVER_URL
})

八、安全加固 #

8.1 安全配置 #

javascript
import helmet from '@fastify/helmet'
import rateLimit from '@fastify/rate-limit'

await fastify.register(helmet, {
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"]
    }
  }
})

await fastify.register(rateLimit, {
  max: 100,
  timeWindow: '1 minute'
})

8.2 HTTPS配置 #

javascript
import fs from 'fs'

const fastify = require('fastify')({
  https: {
    key: fs.readFileSync('./ssl/key.pem'),
    cert: fs.readFileSync('./ssl/cert.pem')
  }
})

九、CI/CD配置 #

9.1 GitHub Actions #

.github/workflows/deploy.yml

yaml
name: Deploy

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '20'
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Run tests
        run: npm test
      
      - name: Build Docker image
        run: docker build -t fastify-app .
      
      - name: Deploy to server
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.HOST }}
          username: ${{ secrets.USERNAME }}
          key: ${{ secrets.SSH_KEY }}
          script: |
            cd /app
            docker-compose pull
            docker-compose up -d
            docker image prune -f

十、云平台部署 #

10.1 AWS部署 #

使用Elastic Beanstalk或ECS:

Dockerrun.aws.json

json
{
  "AWSEBDockerrunVersion": "1",
  "Image": {
    "Name": "fastify-app",
    "Update": "true"
  },
  "Ports": [
    {
      "ContainerPort": "3000"
    }
  ],
  "Environment": [
    {
      "Name": "NODE_ENV",
      "Value": "production"
    }
  ]
}

10.2 Heroku部署 #

Procfile

text
web: node src/server.js
bash
heroku create fastify-app
heroku config:set NODE_ENV=production
git push heroku main

10.3 Vercel Serverless #

api/index.js

javascript
import Fastify from 'fastify'
import app from '../src/app.js'

const fastify = Fastify()

fastify.register(app)

export default async function handler(req, res) {
  await fastify.ready()
  fastify.server.emit('request', req, res)
}

十一、总结 #

本章我们学习了:

  1. 生产配置:环境变量、优雅关闭
  2. PM2部署:配置、命令、监控
  3. Docker部署:Dockerfile、Docker Compose、Nginx
  4. 健康检查:健康端点、依赖检查
  5. 日志管理:配置、轮转
  6. 监控告警:Prometheus、APM
  7. 安全加固:Helmet、HTTPS
  8. CI/CD:GitHub Actions
  9. 云平台:AWS、Heroku、Vercel

恭喜你完成了Fastify完全指南的学习!

最后更新:2026-03-28