将 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


暂无评论内容