Docker Compose 生產環境最佳實務

Docker Compose Production Best Practices

前言

Docker Compose 在開發環境中非常便利,但要將其用於生產環境,需要考慮更多的安全性、可靠性和效能因素。本文將介紹將 Docker Compose 應用於生產環境的最佳實務。


生產環境考量

在將 Docker Compose 部署到生產環境前,需要考慮以下幾個關鍵因素:

  1. 高可用性:確保服務在故障時能自動恢復
  2. 安全性:保護敏感資料和限制容器權限
  3. 可觀測性:完善的日誌和監控機制
  4. 資源管理:合理分配和限制系統資源
  5. 網路隔離:適當的網路分割和存取控制

使用環境變數

在生產環境中,應避免將敏感資訊硬編碼在 docker-compose.yml 中。使用 .env 檔案或環境變數來管理配置。

.env 檔案

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# .env
APP_ENV=production
APP_PORT=8080
DB_HOST=db
DB_PORT=3306
DB_NAME=myapp
DB_USER=app_user
DB_PASSWORD=secure_password_here
REDIS_HOST=redis
REDIS_PORT=6379

在 docker-compose.yml 中引用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
services:
  app:
    image: myapp:${APP_VERSION:-latest}
    environment:
      - APP_ENV=${APP_ENV}
      - DB_HOST=${DB_HOST}
      - DB_PORT=${DB_PORT}
      - DB_NAME=${DB_NAME}
      - DB_USER=${DB_USER}
      - DB_PASSWORD=${DB_PASSWORD}

重要提醒:確保 .env 檔案加入 .gitignore,避免將敏感資訊提交到版本控制系統。


多環境設定檔

使用多個 Compose 檔案來區分不同環境的配置。

基礎配置 docker-compose.yml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
version: "3.9"

services:
  app:
    image: myapp:${APP_VERSION:-latest}
    networks:
      - app_network
    depends_on:
      - db
      - redis

  db:
    image: mysql:8.0
    volumes:
      - db_data:/var/lib/mysql
    networks:
      - app_network

  redis:
    image: redis:7-alpine
    networks:
      - app_network

networks:
  app_network:
    driver: bridge

volumes:
  db_data:

生產環境 docker-compose.prod.yml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
version: "3.9"

services:
  app:
    restart: always
    deploy:
      resources:
        limits:
          cpus: '2'
          memory: 2G
        reservations:
          cpus: '1'
          memory: 1G
    logging:
      driver: "json-file"
      options:
        max-size: "100m"
        max-file: "5"

  db:
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_root_password
    deploy:
      resources:
        limits:
          cpus: '2'
          memory: 4G

  redis:
    restart: always
    command: redis-server --appendonly yes

啟動生產環境

1
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d

健康檢查設定

健康檢查確保服務正常運作,並在服務異常時自動重啟。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
services:
  app:
    image: myapp:latest
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 60s

  db:
    image: mysql:8.0
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 30s
      timeout: 10s
      retries: 5
      start_period: 30s

  redis:
    image: redis:7-alpine
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 3

依賴健康檢查

1
2
3
4
5
6
7
services:
  app:
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy

資源限制設定

在生產環境中限制容器資源使用,避免單一服務耗盡系統資源。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
services:
  app:
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 1G
        reservations:
          cpus: '0.5'
          memory: 512M
    # 舊版相容寫法
    mem_limit: 1g
    cpus: 1.0

  worker:
    deploy:
      resources:
        limits:
          cpus: '2.0'
          memory: 2G
        reservations:
          cpus: '0.25'
          memory: 256M

日誌設定

配置適當的日誌驅動和輪替策略,避免日誌檔案無限增長。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
services:
  app:
    logging:
      driver: "json-file"
      options:
        max-size: "50m"
        max-file: "10"
        labels: "production_status"
        env: "APP_ENV"

  nginx:
    logging:
      driver: "json-file"
      options:
        max-size: "100m"
        max-file: "5"

使用外部日誌系統

1
2
3
4
5
6
7
services:
  app:
    logging:
      driver: "syslog"
      options:
        syslog-address: "tcp://192.168.1.100:514"
        tag: "myapp-{{.Name}}"

重啟策略

設定適當的重啟策略確保服務持續運作。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
services:
  app:
    restart: always  # 總是重啟

  db:
    restart: unless-stopped  # 除非手動停止

  worker:
    restart: on-failure  # 僅在失敗時重啟
    deploy:
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
        window: 120s

重啟策略說明

策略說明
no不自動重啟(預設值)
always總是重啟,包括手動停止後
unless-stopped除非手動停止,否則總是重啟
on-failure僅在非正常退出時重啟

網路設定

在生產環境中,使用自定義網路來隔離服務。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
services:
  nginx:
    networks:
      - frontend
      - backend

  app:
    networks:
      - backend
      - db_network

  db:
    networks:
      - db_network

networks:
  frontend:
    driver: bridge

  backend:
    driver: bridge
    internal: true  # 無法從外部存取

  db_network:
    driver: bridge
    internal: true
    ipam:
      config:
        - subnet: 172.28.0.0/16

網路安全建議

  1. 資料庫網路設定為 internal: true
  2. 僅必要的服務暴露對外連接埠
  3. 使用網路別名進行服務發現

機密資料管理

使用 Docker Secrets 來管理敏感資料(需搭配 Swarm 模式)。

使用 Secrets

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
version: "3.9"

services:
  db:
    image: mysql:8.0
    secrets:
      - db_root_password
      - db_password
    environment:
      MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_root_password
      MYSQL_PASSWORD_FILE: /run/secrets/db_password

  app:
    image: myapp:latest
    secrets:
      - source: app_secret_key
        target: secret_key
        mode: 0400

secrets:
  db_root_password:
    file: ./secrets/db_root_password.txt
  db_password:
    file: ./secrets/db_password.txt
  app_secret_key:
    external: true

非 Swarm 模式替代方案

在非 Swarm 環境中,可使用以下方式:

1
2
3
4
5
6
services:
  app:
    env_file:
      - ./secrets/.env.production
    volumes:
      - ./secrets/credentials.json:/run/secrets/credentials.json:ro

完整生產環境範例

以下是一個完整的生產環境 Docker Compose 配置範例:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
version: "3.9"

services:
  # Nginx 反向代理
  nginx:
    image: nginx:alpine
    container_name: prod_nginx
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./nginx/ssl:/etc/nginx/ssl:ro
      - nginx_logs:/var/log/nginx
    depends_on:
      app:
        condition: service_healthy
    networks:
      - frontend
      - backend
    restart: always
    healthcheck:
      test: ["CMD", "nginx", "-t"]
      interval: 30s
      timeout: 10s
      retries: 3
    logging:
      driver: "json-file"
      options:
        max-size: "50m"
        max-file: "5"

  # 應用程式服務
  app:
    image: myapp:${APP_VERSION:-latest}
    container_name: prod_app
    environment:
      - NODE_ENV=production
      - DB_HOST=db
      - DB_PORT=3306
      - DB_NAME=${DB_NAME}
      - DB_USER=${DB_USER}
      - DB_PASSWORD=${DB_PASSWORD}
      - REDIS_URL=redis://redis:6379
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy
    networks:
      - backend
      - db_network
    restart: always
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 60s
    deploy:
      resources:
        limits:
          cpus: '2'
          memory: 2G
        reservations:
          cpus: '0.5'
          memory: 512M
    logging:
      driver: "json-file"
      options:
        max-size: "100m"
        max-file: "10"

  # MySQL 資料庫
  db:
    image: mysql:8.0
    container_name: prod_db
    volumes:
      - db_data:/var/lib/mysql
      - ./mysql/conf.d:/etc/mysql/conf.d:ro
      - db_logs:/var/log/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD}
      - MYSQL_DATABASE=${DB_NAME}
      - MYSQL_USER=${DB_USER}
      - MYSQL_PASSWORD=${DB_PASSWORD}
    networks:
      - db_network
    restart: always
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${DB_ROOT_PASSWORD}"]
      interval: 30s
      timeout: 10s
      retries: 5
      start_period: 60s
    deploy:
      resources:
        limits:
          cpus: '2'
          memory: 4G
        reservations:
          cpus: '1'
          memory: 2G
    logging:
      driver: "json-file"
      options:
        max-size: "100m"
        max-file: "5"

  # Redis 快取
  redis:
    image: redis:7-alpine
    container_name: prod_redis
    command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD}
    volumes:
      - redis_data:/data
    networks:
      - backend
    restart: always
    healthcheck:
      test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
      interval: 10s
      timeout: 5s
      retries: 3
    deploy:
      resources:
        limits:
          cpus: '1'
          memory: 1G
        reservations:
          cpus: '0.25'
          memory: 256M
    logging:
      driver: "json-file"
      options:
        max-size: "50m"
        max-file: "3"

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge
    internal: true
  db_network:
    driver: bridge
    internal: true

volumes:
  db_data:
    driver: local
  redis_data:
    driver: local
  nginx_logs:
    driver: local
  db_logs:
    driver: local

部署前檢查清單

在將應用部署到生產環境前,請確認以下事項:

  • 所有敏感資訊都使用環境變數或 secrets 管理
  • 設定適當的資源限制
  • 配置健康檢查
  • 設定日誌輪替
  • 配置適當的重啟策略
  • 使用內部網路隔離資料庫
  • 定期備份資料卷
  • 設定監控和告警

總結

Docker Compose 在生產環境中的使用需要額外的配置和考量。本文介紹的最佳實務包括:

  1. 使用環境變數管理配置
  2. 使用多個 Compose 檔案區分環境
  3. 配置健康檢查確保服務可用性
  4. 設定資源限制避免資源耗盡
  5. 配置日誌輪替管理日誌檔案
  6. 設定適當的重啟策略
  7. 使用網路隔離提升安全性
  8. 使用 secrets 管理敏感資料

遵循這些最佳實務,可以讓您的 Docker Compose 部署更加穩定、安全和可維護。


參考資料

comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy