前言
Docker Compose 在開發環境中非常便利,但要將其用於生產環境,需要考慮更多的安全性、可靠性和效能因素。本文將介紹將 Docker Compose 應用於生產環境的最佳實務。
生產環境考量
在將 Docker Compose 部署到生產環境前,需要考慮以下幾個關鍵因素:
- 高可用性:確保服務在故障時能自動恢復
- 安全性:保護敏感資料和限制容器權限
- 可觀測性:完善的日誌和監控機制
- 資源管理:合理分配和限制系統資源
- 網路隔離:適當的網路分割和存取控制
使用環境變數
在生產環境中,應避免將敏感資訊硬編碼在 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
|
網路安全建議
- 資料庫網路設定為
internal: true - 僅必要的服務暴露對外連接埠
- 使用網路別名進行服務發現
機密資料管理
使用 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
|
部署前檢查清單
在將應用部署到生產環境前,請確認以下事項:
總結
Docker Compose 在生產環境中的使用需要額外的配置和考量。本文介紹的最佳實務包括:
- 使用環境變數管理配置
- 使用多個 Compose 檔案區分環境
- 配置健康檢查確保服務可用性
- 設定資源限制避免資源耗盡
- 配置日誌輪替管理日誌檔案
- 設定適當的重啟策略
- 使用網路隔離提升安全性
- 使用 secrets 管理敏感資料
遵循這些最佳實務,可以讓您的 Docker Compose 部署更加穩定、安全和可維護。
參考資料