Docker Compose
單一 docker run 帶一堆旗標難維護,多個容器更是。Docker Compose 用一份 compose.yaml 把整套應用(多服務、網路、volume)宣告起來,一個指令拉起整組。
現行版本是 docker compose(v2,空格,無連字號),已內建於 Docker CLI。舊的 docker-compose(v1,Python 版)已 EOL、不再維護,不要再用。compose.yaml 也不需要也不要寫 version: 頂層欄位,它在 Compose Specification 已 obsolete,寫了只會收到過時警告。
點 compose.yaml 裡的鍵,看每個區塊在做什麼:
頂層結構
name: myapp # 專案名(可選)
services: # 必要,各個容器
web:
image: nginx
networks: # 可選,自訂網路
volumes: # 可選,具名 volume
configs: # 可選,非敏感設定檔
secrets: # 可選,敏感資料
service 常用鍵
| 鍵 | 用途 |
|---|
image | 拉現成映像(固定 tag,別 latest) |
build | 從 Dockerfile build(context / dockerfile / args / target) |
ports | 發布埠,主機:容器 |
environment / env_file | 環境變數(內聯 / 從檔案) |
volumes | bind mount 與具名 volume |
depends_on | 啟動順序(配 condition: service_healthy) |
healthcheck | 健康檢查指令 |
restart | 重啟策略(no / always / on-failure / unless-stopped) |
networks | 加入哪些網路 |
command / entrypoint | 覆蓋映像的預設指令 / 進入點 |
profiles | 把服務歸入 profile,預設不啟動 |
deploy | 資源限制與 GPU |
建立網路
docker compose up 會自動建一個專案專屬的 bridge 網路(命名 <專案名>_default),所有服務自動加入。服務名就是它在這個網路上的 DNS hostname,所以 web 服務直接用 db:5432 連資料庫,不必管 IP。要分層隔離就自己宣告網路:
services:
proxy:
networks: [frontend]
app:
networks: [frontend, backend]
db:
networks: [backend] # 只在 backend,proxy 連不到
networks:
frontend:
driver: bridge
backend:
driver: bridge
proxy 與 db 不同網路無法直接通訊,只有 app 能同時接觸兩者。
容器群組(專案)名稱
Compose 用「專案名」把這組資源綁在一起,並當命名前綴:
| 資源 | 命名格式 | 範例(專案 myapp、服務 web) |
|---|
| 容器 | <專案>-<服務>-<index> | myapp-web-1 |
| 網路 | <專案>_<網路> | myapp_default |
| Volume | <專案>_<volume> | myapp_db-data |
優先序(高到低):docker compose -p <name> > COMPOSE_PROJECT_NAME 環境變數 > 頂層 name: > compose.yaml 所在目錄名。
環境設定參數
| 寫法 | 適用 |
|---|
environment: | 少量、可見的變數,直接寫在 YAML,支援 ${VAR} 插值 |
env_file: | 變數多、或機密想抽出 YAML 並 gitignore |
專案根的 .env | 給 compose.yaml 本身插值用(不是自動注入容器) |
插值語法:${VAR} 取值、${VAR:-default} 未設定或為空用預設、${VAR:?msg} 未設定就報錯中止。容器內環境變數的優先序(高到低):docker compose run -e > environment: > env_file: > 宿主 shell 繼承 > Dockerfile ENV。
使用 GPU
官方寫法是在服務的 deploy.resources.reservations.devices 宣告(主機需先裝 NVIDIA Container Toolkit):
services:
trainer:
image: nvidia/cuda:12.9.0-base-ubuntu22.04
command: nvidia-smi
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all # 或 count: 1 / device_ids: ['0','3']
capabilities: [gpu] # 必填,缺了部署會報錯
效能與啟動順序
depends_on 配 healthcheck:用 condition: service_healthy 等依賴服務真的 ready 才啟動,避免應用搶在資料庫還沒起來時連線失敗。
- 資源限制:
deploy.resources.limits 的 cpus / memory 在 standalone(非 Swarm)模式也生效。
pull_policy 與 build cache:控制要不要每次都 pull、build 重用快取。
- 啟動相關:
up -d(背景)、--build(先重 build)、--scale web=3(擴成 3 份)、--wait(等到全部 healthy)。
services:
app:
depends_on:
db:
condition: service_healthy
db:
image: postgres:18
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER}"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
volume 與 bind mount 映射
Compose 的 volumes: 同時管 bind mount 與具名 volume,靠來源寫法區分(差別見 資料落點):
services:
app:
volumes:
- ./src:/app/src # bind mount:主機相對路徑
- ./config.yaml:/app/config.yaml:ro # 唯讀
- app-data:/var/lib/data # 具名 volume(需頂層宣告)
volumes:
app-data:
要保留的資料用具名 volume,開發即時同步用 bind mount。不在 volumes: 指定的路徑,寫進去就只在容器可寫層,容器一刪就沒。
一份完整的 compose.yaml
# compose.yaml — 不需要也不要寫 version 欄位(已 obsolete)
name: myapp
services:
web:
build:
context: .
target: production
ports:
- "8080:80"
environment:
DATABASE_URL: "postgresql://db:5432/${DB_NAME:-mydb}"
env_file:
- path: .env
required: false
volumes:
- ./static:/app/static:ro
- upload-data:/app/uploads
depends_on:
db:
condition: service_healthy
restart: unless-stopped
networks: [frontend, backend]
db:
image: postgres:18
environment:
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: ${DB_NAME:-mydb}
volumes:
- db-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER}"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
restart: unless-stopped
networks: [backend]
networks:
frontend:
driver: bridge
backend:
driver: bridge
volumes:
db-data:
upload-data:
docker compose 常用指令
docker compose up -d # 背景拉起整組
docker compose up -d --build # 先重 build 再起
docker compose ps # 看這個專案的容器
docker compose logs -f web # 即時看 web 服務日誌
docker compose exec web sh # 進 web 服務的 shell
docker compose config # 展開並驗證 compose 檔(成功回 exit 0)
docker compose build --no-cache # 不用快取重 build
docker compose down # 停止並移除容器與網路
一份 compose.yaml 起整組長這樣:
docker compose down -v 會刪掉具名 volume(compose.yaml 的 volumes: 區段宣告的那些),資料庫資料常在裡面,下去就沒了。只想停服務、保留資料用不帶 -v 的 docker compose down。
接下來
官方參考:docs.docker.com/reference/compose-file、Compose GPU 支援