結論先講

Docker Compose 上生產不是「湊合用」,是正確的選擇——前提是你配對了。 health check、restart policy、resource limits、log rotation、備份——這五件事做好,Docker Compose 在 1000 人以內比 K8s 更穩定、更便宜、更好 debug。


生產等級的 docker-compose.yml

version: '3.8'
 
services:
  app:
    image: your-app:${VERSION:-latest}
    restart: unless-stopped                    # 掛了自動重啟
    deploy:
      resources:
        limits:
          cpus: '2.0'
          memory: 2G
        reservations:
          cpus: '1.0'
          memory: 1G
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
    logging:
      driver: "json-file"
      options:
        max-size: "50m"
        max-file: "5"                          # 最多 5 個 50MB = 250MB
    environment:
      - NODE_ENV=production
      - DB_HOST=postgres
      - REDIS_HOST=redis
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
 
  nginx:
    image: nginx:alpine
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./certs:/etc/nginx/certs:ro
    depends_on:
      app:
        condition: service_healthy
 
  postgres:
    image: postgres:16-alpine
    restart: unless-stopped
    deploy:
      resources:
        limits:
          cpus: '2.0'
          memory: 4G
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5
    volumes:
      - pg_data:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: ${DB_PASSWORD}
 
  redis:
    image: redis:7-alpine
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 3
    volumes:
      - redis_data:/data
 
volumes:
  pg_data:
  redis_data:

五個關鍵配置

配置為什麼
restart: unless-stopped服務掛了自動重啟,除非你手動 stop
healthcheckDocker 定期檢查服務是否健康,不健康就重啟
deploy.resources.limits防止一個 container 吃掉所有資源
logging.max-size + max-file防止 log 吃爆磁碟
depends_on: condition: service_healthy確保依賴的服務(DB、Redis)啟動完成後才啟動 app

部署流程

Zero Downtime 部署(簡易版)

#!/bin/bash
# deploy.sh
 
# 1. Pull 新 image
docker compose pull app
 
# 2. 滾動更新(先啟動新的,再停舊的)
docker compose up -d --no-deps --scale app=2 app
sleep 10  # 等新 container 健康
 
# 3. 停掉舊的
docker compose up -d --no-deps --scale app=1 app

不如 K8s 的 rolling update 優雅,但對 1000 人以內的系統夠用。

更安全的做法:Blue-Green

# 跑兩套 app(blue + green),nginx 切換
docker compose -f docker-compose.blue.yml up -d
# 測試 blue 正常
# 修改 nginx upstream 指向 blue
docker compose -f docker-compose.green.yml down

監控

沒有 K8s 的 Prometheus operator,但你可以用:

工具用途Docker Compose 友好度
PortainerContainer 管理 UI最佳(專為 Docker 設計)
Grafana + Prometheus指標監控好(compose 加兩個 service)
LokiLog 收集好(取代 ELK,更輕量)
Uptime KumaHealth check 監控好(一個 container)

備份策略

K8s 有 Velero 做叢集備份。Docker Compose 用 cron + 腳本:

# backup.sh — 每天凌晨 3 點跑
#!/bin/bash
 
# DB 備份
docker compose exec -T postgres pg_dump -U postgres mydb > /backup/db_$(date +%Y%m%d).sql
 
# Redis 備份(RDB snapshot)
docker compose exec -T redis redis-cli BGSAVE
cp /var/lib/docker/volumes/redis_data/_data/dump.rdb /backup/redis_$(date +%Y%m%d).rdb
 
# 保留最近 30 天
find /backup -mtime +30 -delete

簡單但有效。


Docker Compose 的真實限制

限制影響解法
單機天花板1 台機器的 CPU/RAM 是上限nginx LB 多台
沒有自動擴縮流量暴增時不會自動加 container手動 scale 或上 K8s
跨機器困難Docker Compose 設計給單機Docker Swarm 或 K8s
秘密管理粗糙.env 檔明文存密碼Docker Secrets 或 Vault

當你碰到這些限制中的任何一個,就是考慮上 K8s 的時候。 但在那之前,Docker Compose 是更好的選擇。


下一篇

可觀測性(一):分散式 Tracing — 一個請求跨三個服務,出問題了怎麼追?OpenTelemetry + Jaeger 讓你看到完整的請求鏈路——哪個服務花了多少時間、在哪裡卡住。


本系列文章

完整 68 篇目錄見 系列首頁

← 上一篇:Docker Compose vs K8s:1000 人是分水嶺 → 下一篇:可觀測性(一):分散式 Tracing 怎麼追一個請求