前言

在現代軟體開發中,容器化技術已成為不可或缺的核心技能。Docker 作為容器化的領導者,不僅解決了「在我的電腦上可以運行」的經典問題,更為微服務架構、CI/CD 流程、雲端部署奠定了基礎。而 Docker Compose 則進一步簡化了多容器應用的管理與編排。

本文將深入探討 Docker 與 Docker Compose 的核心概念、設定檔語法、實際應用場景,並提供完整的實務範例。無論您是剛接觸容器化技術的開發者,還是希望深化 DevOps 技能的工程師,都能從中獲得實用的知識與技巧。

Docker 核心概念深度解析

容器化技術的本質

容器化是一種作業系統層級的虛擬化技術,它將應用程式及其相依性封裝在一個輕量級、可移植的容器中。與傳統虛擬機器相比,容器共享主機的核心,因此具有更高的效能和資源利用率。

graph TD
    A[物理主機] --> B[主機作業系統]
    B --> C[Docker Engine]
    C --> D[容器 1]
    C --> E[容器 2]
    C --> F[容器 3]
    
    D --> D1[應用程式 A]
    D --> D2[函式庫與相依性]
    
    E --> E1[應用程式 B]
    E --> E2[函式庫與相依性]
    
    F --> F1[應用程式 C]
    F --> F2[函式庫與相依性]

Docker 架構組成

Docker 採用客戶端-伺服器架構,主要包含以下組件:

  • Docker Client:使用者介面,負責與 Docker Daemon 通訊
  • Docker Daemon (dockerd):核心服務,管理映像檔、容器、網路和儲存
  • Docker Registry:映像檔倉庫,如 Docker Hub、私有 Registry
  • Docker Objects:映像檔 (Images)、容器 (Containers)、網路 (Networks)、儲存卷 (Volumes)

Dockerfile 深度剖析

Dockerfile 基本結構

Dockerfile 是定義映像檔建置過程的純文字腳本,每個指令都會在映像檔中建立一個新的層 (Layer)。

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
# 基礎映像檔 - 選擇適合的基底
FROM golang:1.21-alpine AS builder

# 設定工作目錄
WORKDIR /app

# 複製相依性檔案(利用 Docker 快取層)
COPY go.mod go.sum ./

# 下載相依性
RUN go mod download

# 複製原始碼
COPY . .

# 建置應用程式
RUN CGO_ENABLED=0 GOOS=linux go build -o main .

# 多階段建置 - 生產環境映像檔
FROM alpine:latest

# 安裝必要的系統套件
RUN apk --no-cache add ca-certificates tzdata

# 建立非 root 使用者
RUN adduser -D -s /bin/sh appuser

# 設定工作目錄
WORKDIR /root/

# 從建置階段複製執行檔
COPY --from=builder /app/main .

# 變更檔案擁有者
RUN chown appuser:appuser main

# 切換到非 root 使用者
USER appuser

# 暴露埠號
EXPOSE 8080

# 健康檢查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1

# 啟動命令
CMD ["./main"]

Dockerfile 最佳實踐解析

1. 多階段建置 (Multi-stage Build)

多階段建置可以顯著減少最終映像檔大小,提高安全性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 第一階段:建置環境
FROM node:18-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

# 第二階段:運行環境
FROM node:18-alpine AS runtime
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
WORKDIR /app
COPY --from=build --chown=nextjs:nodejs /app .
USER nextjs
EXPOSE 3000
CMD ["npm", "start"]

2. 層級優化策略

合理安排指令順序,最大化快取效益:

1
2
3
4
5
6
7
8
9
10
11
12
# ❌ 不佳的做法 - 每次程式碼變更都會重新安裝相依性
FROM python:3.11-slim
COPY . /app
WORKDIR /app
RUN pip install -r requirements.txt

# ✅ 良好的做法 - 優先複製相依性檔案
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .

Docker Compose 詳細解析

Docker Compose 的核心價值

Docker Compose 解決了多容器應用編排的複雜性問題。在微服務架構中,一個完整的應用可能包含:

  • Web 伺服器容器
  • 資料庫容器
  • 快取容器
  • 訊息佇列容器
  • 監控容器

手動管理這些容器的啟動順序、網路連接、資料持久化是極其複雜的,Docker Compose 提供了宣告式配置來簡化這個過程。

Docker Compose 檔案結構深度分析

基本語法結構

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
version: '3.8'  # Compose 檔案格式版本

services: # 定義服務
service-name:
# 服務配置

networks: # 定義網路(可選)
network-name:
# 網路配置

volumes: # 定義儲存卷(可選)
volume-name:
# 儲存卷配置

configs: # 定義配置(可選)
config-name:
# 配置定義

secrets: # 定義密鑰(可選)
secret-name:
# 密鑰定義

完整的 Web 應用範例

以下是一個包含前端、後端、資料庫、快取的完整範例:

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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
version: '3.8'  # 指定 Compose 檔案格式版本

services:
# 前端服務 - React 應用
frontend:
build:
context: ./frontend # 指定建置目錄
dockerfile: Dockerfile # 指定 Dockerfile 路徑
target: production # 使用多階段建置的 production 階段
ports:
- "3000:3000" # 對外暴露 3000 埠
environment:
- NODE_ENV=production # 設定執行環境
- REACT_APP_API_URL=http://backend:8080 # API 伺服器位址
depends_on:
backend:
condition: service_healthy # 等待 backend 健康檢查通過
networks:
- app-network # 加入應用網路
restart: unless-stopped # 異常時自動重啟
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000"] # 健康檢查指令
interval: 30s
timeout: 10s
retries: 3

# 後端服務 - Go API
backend:
build:
context: ./backend # 指定建置目錄
dockerfile: Dockerfile
ports:
- "8080:8080" # 對外暴露 8080 埠
environment:
- DB_HOST=postgres # 資料庫主機
- DB_PORT=5432
- DB_NAME=appdb
- DB_USER=postgres
- DB_PASSWORD_FILE=/run/secrets/db_password # 使用 Docker Secret 管理密碼
- REDIS_URL=redis://redis:6379 # Redis 快取連線字串
depends_on:
postgres:
condition: service_healthy # 等待資料庫健康
redis:
condition: service_started # 等待 Redis 啟動
networks:
- app-network
- db-network # 同時加入應用與資料庫網路
secrets:
- db_password # 掛載密碼密鑰
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s

# 資料庫服務 - PostgreSQL
postgres:
image: postgres:15-alpine # 使用輕量級映像檔
environment:
- POSTGRES_DB=appdb
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD_FILE=/run/secrets/db_password
volumes:
- postgres_data:/var/lib/postgresql/data # 資料持久化
- ./database/init.sql:/docker-entrypoint-initdb.d/init.sql:ro # 初始化 SQL
networks:
- db-network
secrets:
- db_password
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"] # 健康檢查
interval: 10s
timeout: 5s
retries: 5

# 快取服務 - Redis
redis:
image: redis:7-alpine
command: redis-server --requirepass myredispassword # 設定存取密碼
volumes:
- redis_data:/data
- ./redis/redis.conf:/usr/local/etc/redis/redis.conf:ro # 自訂設定檔
networks:
- app-network
restart: unless-stopped
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 3

# 反向代理 - Nginx
nginx:
image: nginx:alpine
ports:
- "80:80" # HTTP
- "443:443" # HTTPS
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro # 主設定檔
- ./nginx/ssl:/etc/nginx/ssl:ro # SSL 憑證
- static_files:/var/www/static:ro # 靜態檔案
depends_on:
- frontend
- backend
networks:
- app-network
restart: unless-stopped

# 監控服務 - Prometheus
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090" # Prometheus UI
volumes:
- ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml:ro # 設定檔
- prometheus_data:/prometheus # 資料持久化
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.console.libraries=/etc/prometheus/console_libraries'
- '--web.console.templates=/etc/prometheus/consoles'
networks:
- monitoring-network
- app-network
restart: unless-stopped

# 日誌聚合 - ELK Stack
elasticsearch:
image: elasticsearch:8.8.0
environment:
- discovery.type=single-node # 單節點模式
- "ES_JAVA_OPTS=-Xms512m -Xmx512m" # JVM 記憶體限制
- xpack.security.enabled=false # 關閉安全驗證(測試用)
volumes:
- elasticsearch_data:/usr/share/elasticsearch/data
networks:
- elk-network
restart: unless-stopped

logstash:
image: logstash:8.8.0
volumes:
- ./elk/logstash/pipeline:/usr/share/logstash/pipeline:ro # 管線設定
- ./elk/logstash/config:/usr/share/logstash/config:ro # 其他設定
depends_on:
- elasticsearch
networks:
- elk-network
- app-network
restart: unless-stopped

kibana:
image: kibana:8.8.0
ports:
- "5601:5601" # Kibana UI
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
depends_on:
- elasticsearch
networks:
- elk-network
restart: unless-stopped

# 網路定義
networks:
app-network:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/16 # 指定子網
db-network:
driver: bridge
internal: true # 僅內部可存取,提升安全性
monitoring-network:
driver: bridge
elk-network:
driver: bridge

# 儲存卷定義
volumes:
postgres_data:
driver: local
redis_data:
driver: local
static_files:
driver: local
prometheus_data:
driver: local
elasticsearch_data:
driver: local

# 密鑰定義
secrets:
db_password:
file: ./secrets/db_password.txt # 密碼檔案路徑

說明重點

  • 每個服務都明確標註用途與核心設定。
  • 使用 depends_onhealthcheck 確保服務啟動順序與健康狀態。
  • 透過多網路與 internal: true 實現網路隔離,提升安全性。
  • 密鑰與敏感資訊採用 Docker Secrets 管理,避免硬編碼。
  • 所有資料持久化皆透過 volume 管理,確保資料安全。
  • 監控與日誌聚合(Prometheus、ELK)納入同一編排,便於一站式運維。

1. 環境變數管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 方法一:直接在 docker-compose.yml 中定義
services:
app:
environment:
- NODE_ENV=production
- DB_HOST=localhost

# 方法二:使用 .env 檔案
services:
app:
env_file:
- .env
- .env.production

# 方法三:從檔案讀取(適用於密碼等敏感資訊)
services:
app:
environment:
- DB_PASSWORD_FILE=/run/secrets/db_password

2. 依賴關係與健康檢查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
services:
backend:
depends_on:
database:
condition: service_healthy # 等待資料庫健康檢查通過
restart: true # 依賴服務重啟時也重啟

database:
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s # 檢查間隔
timeout: 5s # 超時時間
retries: 5 # 重試次數
start_period: 30s # 啟動寬限期

3. 資源限制與約束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
services:
app:
deploy:
resources:
limits:
cpus: '2.0' # CPU 限制
memory: 1G # 記憶體限制
reservations:
cpus: '0.5' # CPU 保留
memory: 512M # 記憶體保留
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
window: 120s

實際應用場景深度分析

場景一:微服務開發環境

在微服務架構開發中,開發者需要同時運行多個服務:

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
version: '3.8'

services:
# API Gateway - 負責統一入口與路由流量至各微服務
api-gateway:
build: ./api-gateway # 建置 API Gateway 映像檔
ports:
- "8080:8080" # 對外暴露 8080 埠
environment:
- USER_SERVICE_URL=http://user-service:3001 # 使用者服務 API 位址
- ORDER_SERVICE_URL=http://order-service:3002 # 訂單服務 API 位址
- PAYMENT_SERVICE_URL=http://payment-service:3003# 付款服務 API 位址
depends_on:
- user-service # 依賴 user-service 啟動
- order-service # 依賴 order-service 啟動
- payment-service # 依賴 payment-service 啟動

# 使用者服務 - 處理用戶相關業務邏輯
user-service:
build: ./services/user-service # 建置 user-service 映像檔
environment:
- DB_HOST=user-db # 指定資料庫主機
- REDIS_URL=redis://redis:6379 # 指定 Redis 快取服務
depends_on:
- user-db # 依賴 user-db 啟動
- redis # 依賴 redis 啟動

# 訂單服務 - 處理訂單相關業務邏輯
order-service:
build: ./services/order-service # 建置 order-service 映像檔
environment:
- DB_HOST=order-db # 指定資料庫主機
- KAFKA_BROKERS=kafka:9092 # 指定 Kafka 佇列
depends_on:
- order-db # 依賴 order-db 啟動
- kafka # 依賴 kafka 啟動

# 付款服務 - 處理付款相關業務邏輯
payment-service:
build: ./services/payment-service # 建置 payment-service 映像檔
environment:
- DB_HOST=payment-db # 指定資料庫主機
- KAFKA_BROKERS=kafka:9092 # 指定 Kafka 佇列
depends_on:
- payment-db # 依賴 payment-db 啟動
- kafka # 依賴 kafka 啟動

# 使用者資料庫 - PostgreSQL
user-db:
image: postgres:15 # 使用 PostgreSQL 15 映像檔
environment:
POSTGRES_DB: users # 資料庫名稱
POSTGRES_USER: postgres # 使用者名稱
POSTGRES_PASSWORD: password # 密碼(建議改用 secrets 管理)
volumes:
- user_db_data:/var/lib/postgresql/data # 資料持久化

# 訂單資料庫 - PostgreSQL
order-db:
image: postgres:15
environment:
POSTGRES_DB: orders
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
volumes:
- order_db_data:/var/lib/postgresql/data

# 付款資料庫 - PostgreSQL
payment-db:
image: postgres:15
environment:
POSTGRES_DB: payments
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
volumes:
- payment_db_data:/var/lib/postgresql/data

# Kafka 訊息佇列 - 微服務間通訊
kafka:
image: confluentinc/cp-kafka:latest # 使用 Confluent Kafka 映像檔
environment:
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 # 指定 Zookeeper 位置
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092# 對外宣告的連接資訊
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 # 主題複製因子
depends_on:
- zookeeper # 依賴 zookeeper 啟動

# Zookeeper - Kafka 依賴的協調服務
zookeeper:
image: confluentinc/cp-zookeeper:latest
environment:
ZOOKEEPER_CLIENT_PORT: 2181 # Zookeeper 客戶端連接埠

# Redis 快取服務
redis:
image: redis:alpine # 使用輕量級 Redis 映像檔
volumes:
- redis_data:/data # 資料持久化

volumes:
user_db_data: # 使用者資料庫儲存卷
order_db_data: # 訂單資料庫儲存卷
payment_db_data:# 付款資料庫儲存卷
redis_data: # Redis 資料儲存卷

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
# docker-compose.test.yml
version: '3.8' # 指定 Compose 檔案格式版本

services:
# 應用程式測試環境
app-test:
build:
context: . # 指定建置目錄(專案根目錄)
target: test # 使用多階段建置的 test 階段
environment:
- NODE_ENV=test # 設定執行環境為 test
- DB_HOST=test-db # 指定測試資料庫主機
- REDIS_URL=redis://test-redis:6379 # 指定測試 Redis 快取服務
depends_on:
test-db:
condition: service_healthy # 等待 test-db 健康檢查通過
test-redis:
condition: service_started # 等待 test-redis 啟動完成
volumes:
- ./coverage:/app/coverage # 掛載測試覆蓋率報告目錄
command: npm run test:ci # 執行 CI 測試指令

# 測試資料庫
test-db:
image: postgres:15-alpine # 使用輕量級 PostgreSQL 映像檔
environment:
POSTGRES_DB: test_db # 資料庫名稱
POSTGRES_USER: test_user # 使用者名稱
POSTGRES_PASSWORD: test_password # 密碼(僅測試用)
healthcheck:
test: ["CMD-SHELL", "pg_isready -U test_user"] # 健康檢查指令
interval: 5s # 檢查間隔
timeout: 5s # 超時時間
retries: 5 # 重試次數
tmpfs:
- /var/lib/postgresql/data # 使用記憶體檔案系統加速資料庫測試

# 測試快取
test-redis:
image: redis:alpine # 使用輕量級 Redis 映像檔
tmpfs:
- /data # 使用記憶體檔案系統加速快取

# 端到端測試
e2e-tests:
build:
context: ./e2e # 指定 E2E 測試專案目錄
environment:
- APP_URL=http://app-test:3000 # 指定被測試應用程式的 URL
depends_on:
app-test:
condition: service_healthy # 等待 app-test 健康檢查通過
volumes:
- ./e2e/reports:/app/reports # 掛載 E2E 測試報告目錄
command: npm run test:e2e # 執行端到端測試指令

生產環境的 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
# docker-compose.prod.yml
version: '3.8' # 指定 Compose 檔案格式版本

services:
app:
image: myapp:${APP_VERSION} # 使用特定版本標籤的應用映像檔
deploy:
replicas: 3 # 多副本部署,提升可用性與負載能力
update_config:
parallelism: 1 # 每次只更新一個副本,確保服務不中斷
delay: 10s # 每個副本更新間隔 10 秒
order: start-first # 新副本先啟動再關閉舊副本
restart_policy:
condition: on-failure # 當服務異常時自動重啟
delay: 5s # 重啟前延遲 5 秒
max_attempts: 3 # 最多重啟 3 次
resources:
limits:
cpus: '1.0' # 限制最多使用 1 顆 CPU
memory: 512M # 限制最多使用 512MB 記憶體
reservations:
cpus: '0.5' # 預留 0.5 顆 CPU
memory: 256M # 預留 256MB 記憶體
environment:
- NODE_ENV=production # 設定執行環境為 production
- DB_HOST=db # 指定資料庫主機
- DB_PASSWORD_FILE=/run/secrets/db_password # 透過 Docker Secret 管理密碼
secrets:
- db_password # 掛載資料庫密碼密鑰
networks:
- app-network # 加入應用網路
logging:
driver: "json-file" # 使用 json-file 日誌驅動
options:
max-size: "10m" # 單一日誌檔案最大 10MB
max-file: "3" # 最多保留 3 個日誌檔案

# 負載平衡器
nginx:
image: nginx:alpine # 使用輕量級 Nginx 映像檔
ports:
- "80:80" # 對外暴露 80 埠(HTTP)
- "443:443" # 對外暴露 443 埠(HTTPS)
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro # 掛載 Nginx 主設定檔
- ./ssl:/etc/nginx/ssl:ro # 掛載 SSL 憑證目錄
- static_files:/var/www/static:ro # 掛載靜態檔案目錄
depends_on:
- app # 依賴 app 服務先啟動
networks:
- app-network # 加入應用網路
deploy:
restart_policy:
condition: on-failure # 異常時自動重啟

# 資料庫
db:
image: postgres:15-alpine # 使用輕量級 PostgreSQL 映像檔
environment:
POSTGRES_DB: ${DB_NAME} # 指定資料庫名稱(由環境變數提供)
POSTGRES_USER: ${DB_USER} # 指定資料庫使用者
POSTGRES_PASSWORD_FILE: /run/secrets/db_password # 透過 Secret 管理密碼
volumes:
- db_data:/var/lib/postgresql/data # 資料持久化
- ./backups:/backups # 掛載備份目錄
secrets:
- db_password # 掛載資料庫密碼密鑰
networks:
- db-network # 加入資料庫專用網路
deploy:
restart_policy:
condition: on-failure # 異常時自動重啟
placement:
constraints:
- node.role == manager # 僅允許在 Swarm 管理節點運行

# 監控代理
node-exporter:
image: prom/node-exporter:latest # 使用 Prometheus Node Exporter 映像檔
volumes:
- /proc:/host/proc:ro # 掛載主機 /proc 目錄(唯讀)
- /sys:/host/sys:ro # 掛載主機 /sys 目錄(唯讀)
- /:/rootfs:ro # 掛載主機根目錄(唯讀)
command:
- '--path.procfs=/host/proc' # 指定 procfs 路徑
- '--path.sysfs=/host/sys' # 指定 sysfs 路徑
- '--collector.filesystem.ignored-mount-points=^/(sys|proc|dev|host|etc)($$|/)' # 忽略特定掛載點
networks:
- monitoring-network # 加入監控網路
deploy:
mode: global # 在每個節點都運行一份

networks:
app-network:
driver: overlay # 使用 overlay 網路,支援多主機
attachable: true # 容器可動態加入此網路

db-network:
driver: overlay # 資料庫專用 overlay 網路
internal: true # 僅內部服務可存取,提升安全性

monitoring-network:
driver: overlay # 監控專用 overlay 網路

volumes:
db_data:
driver: local # 本地儲存卷,持久化資料

static_files:
driver: local # 靜態檔案儲存卷

secrets:
db_password:
external: true # 使用外部管理的密鑰

configs:
nginx_config:
external: true # 使用外部管理的 Nginx 設定檔

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
# 啟動所有服務
docker-compose up -d

# 啟動特定服務
docker-compose up -d nginx postgres

# 停止所有服務
docker-compose down

# 停止並移除所有資源(包含網路、儲存卷)
docker-compose down -v --remove-orphans

# 重新建置並啟動
docker-compose up -d --build

# 查看服務狀態
docker-compose ps

# 查看服務日誌
docker-compose logs -f backend

# 進入容器執行指令
docker-compose exec backend bash

# 擴展服務副本數量
docker-compose up -d --scale backend=3

# 驗證設定檔語法
docker-compose config

# 只建置映像檔,不啟動服務
docker-compose build

環境管理最佳實踐

1. 多環境配置策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 目錄結構
project/
├── docker-compose.yml # 基礎配置
├── docker-compose.dev.yml # 開發環境覆蓋
├── docker-compose.test.yml # 測試環境覆蓋
├── docker-compose.prod.yml # 生產環境覆蓋
├── .env.dev # 開發環境變數
├── .env.test # 測試環境變數
└── .env.prod # 生產環境變數

# 使用指定環境配置
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d

# 使用環境變數檔案
docker-compose --env-file .env.dev up -d

2. 開發環境優化配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# docker-compose.dev.yml
version: '3.8'

services:
backend:
build:
target: development # 使用開發階段
volumes:
- ./src:/app/src:ro # 程式碼熱重載
- ./node_modules:/app/node_modules
environment:
- NODE_ENV=development
- DEBUG=app:*
ports:
- "9229:9229" # Node.js 偵錯埠

frontend:
volumes:
- ./frontend/src:/app/src:ro
- ./frontend/public:/app/public:ro
environment:
- FAST_REFRESH=true # React 快速重新整理

效能優化與安全性考量

映像檔優化策略

1. 基礎映像檔選擇

1
2
3
4
5
6
7
8
9
10
11
12
# ❌ 使用完整 Ubuntu 映像檔(約 72MB)
FROM ubuntu:22.04

# ✅ 使用 Alpine Linux(約 5MB)
FROM alpine:3.18

# ✅ 使用 Distroless 映像檔(更安全)
FROM gcr.io/distroless/java17-debian11

# ✅ 針對特定語言的優化映像檔
FROM golang:1.21-alpine AS builder
FROM scratch # 最小化的最終映像檔

2. 層級快取優化

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
# ✅ 優化層級順序
FROM node:18-alpine

# 1. 安裝系統相依性(變化頻率低)
RUN apk add --no-cache \
ca-certificates \
tzdata \
&& rm -rf /var/cache/apk/*

# 2. 設定工作目錄
WORKDIR /app

# 3. 複製套件檔案(變化頻率中等)
COPY package*.json ./

# 4. 安裝 npm 套件
RUN npm ci --only=production && npm cache clean --force

# 5. 複製應用程式碼(變化頻率高)
COPY . .

# 6. 建置應用程式
RUN npm run build

# 7. 啟動指令
CMD ["npm", "start"]

安全性最佳實踐

1. 非 Root 使用者執行

1
2
3
4
5
6
7
8
9
10
11
12
# 建立專用使用者
RUN addgroup -g 1001 -S nodejs \
&& adduser -S nextjs -u 1001

# 變更檔案擁有權
COPY --chown=nextjs:nodejs . .

# 切換到非特權使用者
USER nextjs

# 使用非特權埠號
EXPOSE 3000

2. 密鑰管理

1
2
3
4
5
6
7
8
9
10
11
12
13
# 使用 Docker Secrets
services:
app:
environment:
- DB_PASSWORD_FILE=/run/secrets/db_password
secrets:
- db_password

secrets:
db_password:
file: ./secrets/db_password.txt
# 或使用外部密鑰管理
# external: true

3. 網路隔離

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
networks:
frontend-network:
driver: bridge

backend-network:
driver: bridge
internal: true # 內部網路,無法存取外部

database-network:
driver: bridge
internal: true

services:
frontend:
networks:
- frontend-network
- backend-network

backend:
networks:
- backend-network
- database-network

database:
networks:
- database-network # 只能被後端存取

監控與日誌管理

容器健康檢查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
services:
app:
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:3000/health || exit 1"]
interval: 30s # 檢查間隔
timeout: 10s # 超時時間
retries: 3 # 失敗重試次數
start_period: 60s # 啟動寬限期

database:
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $POSTGRES_USER -d $POSTGRES_DB"]
interval: 10s
timeout: 5s
retries: 5

日誌聚合配置

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
# 使用 ELK Stack 進行日誌管理
version: '3.8'

services:
app:
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
labels: "service=app"
# 或使用 Fluentd
logging:
driver: fluentd
options:
fluentd-address: localhost:24224
tag: app.logs

# 日誌收集器
fluentd:
image: fluentd:v1.16-1
volumes:
- ./fluentd/conf:/fluentd/etc
- ./logs:/var/log/fluentd
ports:
- "24224:24224"
- "24224:24224/udp"

# Elasticsearch 儲存
elasticsearch:
image: elasticsearch:8.8.0
environment:
- discovery.type=single-node
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
volumes:
- elasticsearch_data:/usr/share/elasticsearch/data

# Kibana 視覺化
kibana:
image: kibana:8.8.0
ports:
- "5601:5601"
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200

故障排除與偵錯技巧

常見問題診斷

1. 服務啟動失敗

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 查看詳細錯誤訊息
docker-compose logs service-name

# 查看服務狀態
docker-compose ps

# 進入容器偵錯
docker-compose exec service-name sh

# 檢查網路連接
docker-compose exec service-name ping other-service

# 驗證環境變數
docker-compose exec service-name env

2. 網路連接問題

1
2
3
4
5
6
7
8
9
10
11
# 列出所有網路
docker network ls

# 檢查網路詳細資訊
docker network inspect project_default

# 測試服務間連接
docker-compose exec frontend wget -qO- http://backend:8080/health

# 檢查埠號映射
docker-compose port frontend 3000

3. 儲存卷問題

1
2
3
4
5
6
7
8
9
10
11
# 列出所有儲存卷
docker volume ls

# 檢查儲存卷詳細資訊
docker volume inspect project_db_data

# 備份儲存卷資料
docker run --rm -v project_db_data:/data -v $(pwd):/backup alpine tar czf /backup/backup.tar.gz /data

# 恢復儲存卷資料
docker run --rm -v project_db_data:/data -v $(pwd):/backup alpine tar xzf /backup/backup.tar.gz -C /

效能監控指令

1
2
3
4
5
6
7
8
9
10
11
# 查看容器資源使用情況
docker stats

# 查看特定服務的資源使用
docker-compose exec backend top

# 監控檔案系統使用情況
docker-compose exec backend df -h

# 檢查記憶體使用詳情
docker-compose exec backend cat /proc/meminfo

總結與最佳實踐建議

核心概念回顧

  1. 容器化思維:將應用程式及其環境封裝為可移植的容器
  2. 多階段建置:優化映像檔大小,分離建置與運行環境
  3. 服務編排:使用 Docker Compose 管理複雜的多容器應用
  4. 配置管理:合理使用環境變數、密鑰和配置檔案
  5. 網路隔離:通過自定義網路提高安全性
  6. 資料持久化:正確使用儲存卷管理應用資料

開發流程建議

1. 專案初始化階段

  • 設計合理的目錄結構
  • 建立多環境配置檔案
  • 配置基礎的健康檢查和日誌
  • 建立開發環境的熱重載機制

2. 開發階段最佳實踐

  • 使用 .dockerignore 優化建置上下文
  • 實施層級快取策略
  • 配置適當的資源限制
  • 建立統一的錯誤處理和日誌格式

3. 測試與CI/CD整合

  • 建立專用的測試環境配置
  • 實施自動化測試流程
  • 配置健康檢查和服務依賴
  • 建立映像檔版本控制策略

4. 生產部署考量

  • 實施安全性最佳實踐
  • 配置監控和告警機制
  • 建立備份和災難恢復流程
  • 規劃容量和擴展策略

進階學習方向

掌握 Docker 和 Docker Compose 的基礎後,建議深入學習:

  1. 容器編排平台:Kubernetes、Docker Swarm
  2. 服務網格:Istio、Linkerd
  3. 監控方案:Prometheus + Grafana、ELK Stack
  4. 安全掃描:Trivy、Clair
  5. 映像檔倉庫:Harbor、Nexus
  6. GitOps 流程:ArgoCD、FluxCD

Docker 容器化技術已成為現代軟體開發的基石,熟練掌握 Docker 和 Docker Compose 不僅能提高開發效率,更是邁向 DevOps 和雲端原生架構的重要一步。透過不斷實踐和探索,您將能夠建構更穩定、可擴展、安全的容器化應用系統。


本文涵蓋了 Docker 和 Docker Compose 的核心概念與實際應用,希望能幫助您在容器化技術的學習路上更進一步。如有任何問題或建議,歡迎在評論區交流討論!