Docker Compose 多容器編排
在現代的微服務(Microservices)架構中,一個完整的應用系統通常由多個獨立的容器組成。例如:一個標準的電商網站可能包含前端、後端、資料庫 (PostgreSQL)、快取 (Redis) 以及訊息佇列 (RabbitMQ)。
如果手動使用 docker run 來啟動這些相互連結的服務,你需要記住每個容器的啟動順序、網路配置、環境變數與磁碟掛載,這不僅耗時且極難維護。Docker Compose 就是為了解決這個問題而生的「編排指揮官」。
什麼是 Docker Compose?
Docker Compose 是 Docker 官方提供的工具,它讓我們能透過一個 YAML 格式的設定檔 (docker-compose.yaml) 來宣告應用程式的所有服務、網路與磁碟卷。
其核心價值在於 基礎架構即代碼 (Infrastructure as Code, IaC):
- 環境一致性:無論是開發、測試還是生產環境,都共用同一套配置,徹底解決「在我電腦上明明可以跑」的窘境。
- 一鍵操作:只需一個簡單指令,就能啟動或拆除整個系統。
- 配置自動化:自動處理容器間的網路通訊,讓你專注於開發而非網路設定。
docker-compose.yaml 配置深度詳解
Compose 檔案是整個工具的靈魂。以下是一個整合了進階選項(如資源限制與健康檢查)的完整配置範例:
services:
web:
build:
context: .
dockerfile: Dockerfile
# 完整語法 (Long Syntax) 的埠號對應
ports:
- target: 80
published: 8080
protocol: tcp
mode: host
# 環境變數
environment:
- DATABASE_URL=postgres://user:pass@db:5432/mydb
# 依賴關係(確保啟動順序與就緒狀態)
depends_on:
db:
condition: service_healthy
# 資源限制與預留
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
memory: 128M
restart: unless-stopped
# 解決殭屍進程問題
init: true
networks:
- backend
db:
image: postgres:15-alpine
env_file:
- .env.db
# 完整語法 (Long Syntax) 的磁碟卷掛載
volumes:
- type: volume
source: pg_data
target: /var/lib/postgresql/data
read_only: false
# 健康檢查:這決定了其他服務是否能開始連接
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U postgres']
interval: 5s
timeout: 3s
retries: 5
start_period: 10s
networks:
- backend
networks:
backend:
driver: bridge
volumes:
pg_data:
YAML 層級結構與語法邏輯
在編寫 Docker Compose 檔案時,正確理解層級(Indentation)與關鍵字分類至關重要。YAML 檔案是透過縮排來決定「誰屬於誰」的母子關係。
第一層級:宣告資源類型 (Level 1)
這是 YAML 檔案的最頂層(無縮排),用來告訴 Docker Compose 你需要準備哪些類型的資源:
services:最核心區塊。在這裡定義應用程式的各個容器。networks:定義專案使用的虛擬網路。volumes:定義專案使用的持久化數據卷。
第二層級:定義資源名稱 (Level 2)
這是緊接在第一層級下方的縮排項目。由你自定義名稱,這些名稱具有重要的實務意義:
- 服務名稱 (Service Name):
- 如範例中的
web與db。 - DNS 作用:這就是容器在內網中的「主機名稱」。在
web容器內,你只要連線到http://db:5432就能找到資料庫,不需要記住變動的 IP。
- 如範例中的
- 網路/卷名稱:
- 如
backend或pg_data。 - 這讓多個服務可以「引用」同一個資源。例如
web與db都宣告使用backend網路,它們才能互相通訊。
- 如
第三層級與更深:配置參數 (Level 3+)
在資源名稱下方,就是該資源的具體配置參數(如 image, ports, build 等),這些就是我們接下來要詳解的關鍵設定。
配置參數說明
深入了解這些參數,能讓你從單純的「容器啟動者」進化為「架構設計師」。以下是針對 YAML 範例中出現的各個重要區塊的深度技術詳解:
1. image:指定映像檔來源
- 如果不是自行建置,則使用
image指定官方或私有的映像檔。建議帶上具體的標籤(如postgres:15-alpine)而非使用latest,以確保環境的一致性與穩定性。
2. build:客製化建置參數
當你需要從本地原始碼打造映像檔時,build 區塊提供了詳細的控制:
context與dockerfile:分別定義建置的目錄路徑與 Dockerfile 的檔名。args(建置時變數):傳遞變數給 Dockerfile 內的ARG指令,方便在建置時決定環境參數(如版本號)。target:在多階段建置中,指定要建置到哪一個階段(如dev或prod)。
3. ports:詳細的埠號映射
範例中使用長語法 (Long Syntax) 展示了更精確的設定:
target:容器內部的埠號。published:暴露在宿主機上的埠號。protocol:通訊協定,常見為tcp或udp。mode:常見為host,在叢集模式 (Swarm) 下則有不同的路由選擇。
4. depends_on:定義啟動順序與就緒狀態
這是處理多服務依賴的關鍵:
condition: service_healthy:這讓 Web 服務不僅是在資料庫容器「啟動」後執行,而是要等到資料庫通過「健康檢查 (Healthcheck)」並回報為 Ready 狀態後才啟動。
5. deploy: resources:資源的硬限制與保留
limits(硬限制):容器佔用的天花板。例如cpus: '0.5'表示該容器最多只能消耗宿主機一半的核心性能;memory則限制記憶體上限。reservations(預留值):當宿主機資源緊張時,Docker 保證該容器一定能拿到的最低資源額度。
6. restart:自動重啟策略
定義容器因故障停止後的反應:
unless-stopped:除非被手動停止(如執行docker compose stop),否則當容器崩潰或 Docker 服務重啟時,該容器會自動重跑。
7. volumes:磁碟卷與持續性數據
範例使用長語法來精確定義掛載:
type: volume:使用由 Docker 管理的磁碟卷(如範例中的pg_data),這是生產環境最推薦的做法。read_only: false:設定為可讀寫,若設為true則容器內無法修改掛載的內容。
8. healthcheck:健康狀況監控
範例中展示了確保服務可用的全套配置:
test:執行的指令。interval:每隔多久執行一次檢查。timeout:單次檢查的超時時間。retries:連續失敗幾次後將容器標示為unhealthy。start_period:啟動初期的寬限期。
9. 架構層級配置 (Networks & Volumes)
在 YAML 的最底層,我們需要定義全域資源:
networks(頂層):定義該專案使用的網路名稱與驅動程式 (driver: bridge是單機最常用的模式)。volumes(頂層):定義持久化數據的磁碟卷空間。
10. command 與 entrypoint:容器的啟動靈魂
這兩個參數定義了容器啟動時該跑什麼程式:
entrypoint:設定容器作為一個「可執行指令」的存在(例如一個 Python 直譯器)。command:作為entrypoint的預設參數。- 交互關係:如果你設定了
entrypoint: ["python"]且command: ["app.py"],容器最終會執行python app.py。你可以隨時透過docker run或compose run的指令參數來覆蓋command的內容。
11. environment:環境變數的權重奧秘
配置的可管理性往往取決於你如何處理環境變數:
- 優先權順序:在 Compose 中,
environment的設定優先權最高,會直接蓋掉來自宿主機或是.env檔案的同名變數。 env_file:當環境變數多達數十個時,將它們歸類到獨立檔案中管理(如.env.db,.env.api)能讓你的 YAML 保持乾淨清爽。
12. logging:防止磁碟被撐爆的最後防線
許多生產環境的意外都是因為容器日誌把硬碟塞爆:
driver: json-file:預設的日誌驅動。- 滾動備份:強烈建議設定
max-size: "10m"與max-file: "3"。這表示每個日誌檔滿 10MB 就自動切分,且最多只保留最近的 3 份檔案。
Docker Compose 常用指令與技巧
掌握了 YAML 配置後,你需要一套流暢的指令來操作你的服務集群。
docker-compose。在最新的 Docker 版本中,建議使用整合後的 docker compose(中間是空格)。1. 服務啟動與生命週期管理
這組指令控制著容器的「生老病死」:
docker compose up -d: 最核心指令。它會自動建置/拉取映像檔、建立網路與磁碟卷,並背景啟動容器。docker compose down: 優雅地停止並移除容器與網路。docker compose stop/docker compose start: 僅停止或啟動容器。與down不同,這不會刪除容器,容器內的變動(非 Volume 部分)會被保留。docker compose restart: 重新啟動服務。常用於修改配置或程式碼後(若不需要重新建置映像檔時)。docker compose pause/docker compose unpause: 這是維運時的「定格」神器。pause會透過 Linux 的 cgroups 掛起容器內的所有進程,讓容器暫停運作但不釋放資源;unpause則恢復運作。
2. 進階啟動參數
--build:強制重新建置映像檔。--force-recreate:即便配置沒變,也強迫刪除並重新建立容器。--remove-orphans:清理掉那些曾經在 YAML 中,但現在已被刪除的舊服務殘留容器。
3. 日誌、偵錯與狀態檢視
docker compose logs -f --tail="100" --timestamps: 即時追蹤日誌。--timestamps能幫你分析服務啟動時各階段的耗時。docker compose ps: 列出該分案下所有服務的運行狀態、埠號映射與健康狀況。docker compose exec [service] [cmd]: 進去執行中的容器。例如docker compose exec db psql -U user。docker compose run --rm [service] [cmd]: 啟動一個一次性的容器來執行特定任務(如資料庫遷移),執行完畢後自動移除 (--rm)。
4. 維運與性能監控
當你的服務變多時,你需要知道誰在「偷吃」資源:
docker compose stats: 即時顯示所有服務的 CPU、記憶體、網路 I/O 與磁碟 I/O 佔用率。docker compose top: 列出特定服務容器內部正在運行的 Linux 進程列表。docker compose images: 查看目前專案所使用的所有映像檔及其 ID、大小、標籤資訊。
5. 配置驗證與安全性
docker compose config: 這不只是檢查語法。它會將你分散在多個 Overrides 檔案與.env中的配置進行「最終合併」並顯示出來。在執行up之前,這是確保合併結果符合預期的保險動作。docker compose version: 確認目前 Compose 套件的版本,這對確認是否支援某些 V2 新功能(如 Watch)至關重要。
高效率開發:Compose Watch
這是 Docker Compose V2 引入的強大功能,旨在取代傳統笨重的「掛載整份原始碼」方案。
配置範例:
services:
web:
build: .
develop:
watch:
- action: sync
path: ./src
target: /app/src
ignore:
- node_modules/
- action: rebuild
path: package.json
執行指令:
docker compose watch
當你修改 src/ 下的代碼時,Compose 會即時「同步」檔案到容器內;若修改了 package.json,它則會自動「重新建置」映像檔。這能維持開發環境的高效與純淨。
進階應用:多環境配置與 Overrides
在真實的開發流程中,我們通常需要針對不同階段(開發、測試、生產)使用不同的配置。例如:開發環境需要自動加載代碼變化,而生產環境需要資源限制與日誌監控。
Docker Compose 提供了強大的 Overrides 機制,讓你不需要為了每個環境都重新寫一份完整的 YAML 檔案。
合併機制的運作細節
當你指定多個 Compose 檔案時,Docker Compose 會按照順序將它們合併:
- 單一數值(Overwritten):對於只有一個值的設定(如
image,restart),後面的檔案會直接覆蓋前面的檔案。 - 列表與字典(Merged):對於列表狀的設定(如
ports,environment,volumes),後續檔案的內容會「附加」到前面檔案之後。
實戰範例:開發 vs. 生產配置
1. 基礎配置 (docker-compose.yaml):存放所有環境通用的設定。
services:
web:
image: my-app:latest
environment:
- DB_HOST=db
2. 生產環境覆蓋 (docker-compose.prod.yaml):增加限制與安全。
services:
web:
restart: always # 覆蓋
deploy: # 增加資源限制
resources:
limits:
memory: 1G
3. 本地開發覆蓋 (docker-compose.override.yaml):方便調試。
services:
web:
command: npm run dev # 使用開發模式
volumes: # 增加代碼掛載
- .:/app
ports: # 增加對外埠號
- '3000:3000'
指令執行與優先權
- 自動載入:如果你只執行
docker compose up,Compose 會自動尋找並合併docker-compose.yaml與docker-compose.override.yaml。 - 特定環境:如果要部署到正式環境,你應該明確排除開發用的 override 檔案:
# 基礎設定 + 生產設定 docker compose -f docker-compose.yaml -f docker-compose.prod.yaml up -d
-f 的順序非常重要:後面出現的檔案擁有更高的優先權,能覆蓋前面檔案中的相同欄位。常見問題與排查 (Troubleshooting)
問題一:啟動順序不代表服務可以用了
現象:Web 容器啟動後報錯「無法連接資料庫」,但資料庫容器明明正在 Running。
解決:這是因為資料庫容器「啟動」不代表資料庫「初始化完成」。你必須在 YAML 中使用 healthcheck,並讓 Web 的 depends_on 監聽 service_healthy 狀態。
問題二:埠號冲突
現象:報錯 Bind for 0.0.0.0:80 failed: port is already allocated。
解決:代表你宿主機上已經有其他程式(或另一個 Compose 專案)占用了該埠號。請修改 YAML 左側的埠號(Published port)。
問題三:修改 Dockerfile 後 up 沒反應
現象:修改了程式碼或 Dockerfile,但執行 up 後發現跑的還是舊版本。
解決:Compose 預設會優先使用現存映像檔。請加上 --build 參數確保重新建置:docker compose up -d --build。
總結
Docker Compose 不僅僅是縮短指令的腳本,更是定義應用生命週期的重要文件。透過 Healthcheck 確保服務強健、使用 Resources 保護宿主機性能、善用 Watch 加速開發、搭配 Overrides 管理不同環境,你就能輕鬆駕馭複雜的微服務系統。