将 Compose 应用迁移到生产环境

将 Compose 应用迁移到生产环境

开发环境 vs 生产环境的差异

# 开发环境
services:
  app:
    build: .
    volumes:
      - .:/app          # 代码热更新
    ports:
      - "3000:3000"     # 直接暴露端口
    environment:
      NODE_ENV: development
      DEBUG: "*"

# 生产环境需要的变化
services:
  app:
    image: registry.example.com/myapp:1.0.0  # 使用已构建的镜像
    # 没有代码挂载
    ports:
      - "3000"                                # 随机端口(通过 LB 访问)
    environment:
      NODE_ENV: production
    restart: always
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]

迁移步骤

第一步:优化 Dockerfile

# ❌ 开发用 Dockerfile(大、慢)
FROM node:18
WORKDIR /app
COPY . .
RUN npm install
CMD ["npm", "run", "dev"]

# ✅ 生产用 Dockerfile(小、快、安全)
FROM node:18-alpine AS builder
WORKDIR /build
COPY package*.json ./
RUN npm ci --only=production

FROM node:18-alpine
RUN addgroup -g 1001 appgroup && adduser -u 1001 -G appgroup -s /bin/sh -D appuser
WORKDIR /app
COPY --from=builder /build/node_modules ./node_modules
COPY . .
USER appuser
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=5s --start-period=40s --retries=3 \
  CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
CMD ["node", "server.js"]

第二步:拆分 Compose 文件

# docker-compose.yml(基础配置)
services:
  app:
    image: ${REGISTRY:-localhost}/${IMAGE_NAME:-myapp}:${TAG:-latest}
    environment:
      NODE_ENV: production
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 40s
    restart: always

  db:
    image: postgres:15-alpine
    volumes:
      - pg-data:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: ${DB_NAME}
      POSTGRES_USER: ${DB_USER}
    env_file:
      - ./env/db.env
    restart: always
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${DB_USER}"]
      interval: 10s

volumes:
  pg-data:
# docker-compose.prod.yml(生产专属配置)
services:
  app:
    ports:
      - "3000"           # 随机主机端口
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
    deploy:
      resources:
        limits:
          cpus: "0.5"
          memory: "512M"
        reservations:
          cpus: "0.25"
          memory: "256M"

  db:
    deploy:
      resources:
        limits:
          cpus: "1"
          memory: "2G"
        reservations:
          cpus: "0.5"
          memory: "1G"

  # 生产环境特有的监控服务
  prometheus:
    image: prom/prometheus
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
      - prometheus-data:/prometheus
    restart: always

  grafana:
    image: grafana/grafana
    ports:
      - "3001:3000"
    volumes:
      - grafana-data:/var/lib/grafana
    restart: always

volumes:
  prometheus-data:
  grafana-data:

第三步:配置 CI/CD 流水线

# .github/workflows/deploy.yml
name: Build and Deploy

on:
  push:
    branches: [main]

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Build Docker image
        run: |
          docker build -t ${{ secrets.REGISTRY }}/myapp:${{ github.sha }} .
          docker tag ${{ secrets.REGISTRY }}/myapp:${{ github.sha }} ${{ secrets.REGISTRY }}/myapp:latest

      - name: Push to registry
        run: |
          echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login -u "${{ secrets.REGISTRY_USER }}" --password-stdin
          docker push ${{ secrets.REGISTRY }}/myapp:${{ github.sha }}
          docker push ${{ secrets.REGISTRY }}/myapp:latest

      - name: Deploy to production
        run: |
          ssh ${{ secrets.DEPLOY_HOST }} "
            cd /opt/myapp &&
            docker compose -f docker-compose.yml -f docker-compose.prod.yml pull &&
            docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
          "

第四步:生产安全加固

services:
  app:
    # 以非 root 用户运行
    user: "1001:1001"

    # 只读根文件系统
    read_only: true
    tmpfs:
      - /tmp
      - /var/run

    # 安全能力限制
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE

    # 安全配置
    security_opt:
      - no-new-privileges:true

    # 日志限制
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

    # 重启策略
    restart: unless-stopped

第五步:生产部署脚本

#!/bin/bash
# deploy.sh - 生产部署脚本

set -e

ENV=${1:-production}
TAG=${2:-latest}
COMPOSE_DIR="/opt/myapp"
REGISTRY="registry.example.com"

echo "🚀 开始部署 (环境: $ENV, 版本: $TAG)"

# 1. 拉取最新代码
cd $COMPOSE_DIR
git pull origin main

# 2. 拉取最新镜像
docker compose \
  -f docker-compose.yml \
  -f docker-compose.$ENV.yml \
  pull

# 3. 启动服务
docker compose \
  -f docker-compose.yml \
  -f docker-compose.$ENV.yml \
  up -d --remove-orphans

# 4. 等待健康检查
echo "⏳ 等待服务就绪..."
sleep 30

# 5. 验证部署
if docker compose ps | grep -q "(healthy)"; then
  echo "✅ 部署成功"
else
  echo "❌ 部署失败,触发回滚..."
  docker compose -f docker-compose.yml -f docker-compose.$ENV.yml down
  # 回滚到上一个版本
  TAG=$PREV_TAG docker compose up -d
  exit 1
fi

# 6. 清理旧镜像
docker image prune -f

生产环境检查清单

安全

  • [ ] 不使用 root 用户运行容器
  • [ ] 敏感信息通过 secrets/env_file 注入
  • [ ] 只读根文件系统
  • [ ] 限制 Linux capabilities
  • [ ] 使用镜像签名验证

可靠性

  • [ ] 配置 healthcheck
  • [ ] 设置 restart: always
  • [ ] 配置资源限制
  • [ ] 配置日志轮转
  • [ ] 监控告警

可维护性

  • [ ] 使用命名卷持久化数据
  • [ ] 环境变量分离到 .env 文件
  • [ ] 区分开发和生产 Compose 文件
  • [ ] 配置 CI/CD 流水线
  • [ ] 备份策略

常见问题

Q:直接在生产环境使用 docker-compose.override.yml 有什么风险?

A:override 文件会自动加载,可能导致生产环境使用了不应有的配置(如调试端口、低安全设置)。生产环境应显式指定 Compose 文件。

Q:生产环境需要配置多少资源限制?

A:使用实际流量测试出的结果。通用建议:CPU 限制为平均使用的 2 倍,内存限制为 1.5 倍。为关键服务保留资源余量。

Q:如何进行灰度发布?

A:通过多 Compose 文件或 Nginx 蓝绿部署。更推荐迁移到 Kubernetes 以获得完整的灰度发布能力。

© 版权声明
THE END
喜欢就支持一下吧
点赞10 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容