Docker Compose Watch 簡介
Docker Compose Watch 是 Docker Compose 2.22.0 版本引入的強大功能,專為提升開發效率而設計。它能夠監控本地檔案系統的變更,並自動將變更同步到運行中的容器,實現真正的熱重載(Hot Reload)開發體驗。
為什麼需要 Docker Compose Watch?
在容器化開發環境中,開發者經常面臨以下困擾:
- 頻繁重建容器:每次程式碼變更都需要重新執行
docker compose up --build - 開發週期緩慢:等待容器重建和重啟消耗大量時間
- Volume 掛載的限制:傳統 bind mount 在某些場景下效能不佳或配置複雜
- 環境不一致:開發和生產環境的配置差異可能導致問題
Docker Compose Watch 解決了這些問題,讓您可以:
- 即時同步:檔案變更即時反映到容器中
- 智能重載:根據變更類型自動選擇適當的更新策略
- 零配置啟動:簡單的 YAML 配置即可啟用
- 保持環境一致:在接近生產環境的容器中進行開發
與傳統開發模式比較
傳統 Volume 掛載方式
1
2
3
4
5
| services:
web:
build: .
volumes:
- ./src:/app/src
|
傳統方式的缺點:
- 需要手動配置 volume 路徑
- 某些檔案變更(如 package.json)需要手動重建
- Windows/macOS 上的檔案系統效能問題
- 無法區分不同類型檔案的處理方式
Docker Compose Watch 方式
1
2
3
4
5
6
7
8
9
10
| services:
web:
build: .
develop:
watch:
- action: sync
path: ./src
target: /app/src
- action: rebuild
path: package.json
|
Watch 方式的優點:
- 明確的檔案監控規則
- 自動化的重建和重啟機制
- 更好的效能和可控性
- 支援多種更新動作
功能對比表
| 特性 | Volume 掛載 | Docker Compose Watch |
|---|
| 檔案同步 | 即時(bind mount) | 即時(檔案複製) |
| 依賴變更處理 | 手動重建 | 自動重建 |
| 配置變更處理 | 手動重啟 | 自動重啟 |
| 效能(跨平台) | 較差 | 優秀 |
| 配置複雜度 | 簡單 | 中等 |
| 精細控制 | 有限 | 完整 |
Watch 模式設定語法
基本語法結構
Watch 配置位於 develop 區塊下:
1
2
3
4
5
6
7
8
9
10
11
12
| services:
service_name:
build: .
develop:
watch:
- action: sync
path: ./local_path
target: /container_path
ignore:
- node_modules/
- action: rebuild
path: ./requirements.txt
|
配置項說明
| 配置項 | 必要性 | 說明 |
|---|
action | 必要 | 變更時執行的動作(sync/rebuild/sync+restart) |
path | 必要 | 監控的本地路徑(相對於 Compose 檔案) |
target | sync 時必要 | 容器內的目標路徑 |
ignore | 選填 | 忽略的檔案或目錄列表 |
啟動 Watch 模式
1
2
3
4
5
6
7
8
| # 啟動 watch 模式
docker compose watch
# 背景執行並監控
docker compose up --watch
# 搭配其他參數
docker compose up -d --watch --build
|
sync、rebuild、sync+restart 動作差異
Docker Compose Watch 提供三種不同的動作類型,適用於不同的使用場景:
1. sync - 檔案同步
sync 動作會將變更的檔案直接複製到運行中的容器,不會重啟容器或服務。
適用場景:
- 前端靜態資源(HTML、CSS、JavaScript)
- 支援熱重載的框架(React、Vue、Next.js)
- 模板檔案
- 設定檔(不需重啟即可生效)
配置範例:
1
2
3
4
5
6
7
8
9
10
| services:
frontend:
build: ./frontend
develop:
watch:
- action: sync
path: ./frontend/src
target: /app/src
ignore:
- "**/*.test.js"
|
運作流程:
1
| 檔案變更 → 偵測變更 → 複製到容器 → 完成
|
2. rebuild - 完整重建
rebuild 動作會觸發容器的完整重建流程,包括重新執行 Dockerfile 的建構步驟。
適用場景:
- 依賴套件變更(package.json、requirements.txt、go.mod)
- Dockerfile 變更
- 編譯型語言的原始碼變更(Go、Rust、C++)
- 建構配置變更
配置範例:
1
2
3
4
5
6
7
8
9
| services:
backend:
build: ./backend
develop:
watch:
- action: rebuild
path: ./backend/package.json
- action: rebuild
path: ./backend/Dockerfile
|
運作流程:
1
| 檔案變更 → 偵測變更 → 停止容器 → 重建映像 → 啟動新容器 → 完成
|
3. sync+restart - 同步並重啟
sync+restart 動作會先同步檔案,然後重啟容器內的服務程序。
適用場景:
- 需要重啟才能載入的設定檔
- 不支援熱重載的應用程式
- Python/Node.js 應用程式(非 dev 模式)
- 資料庫遷移腳本
配置範例:
1
2
3
4
5
6
7
8
9
10
11
| services:
api:
build: ./api
develop:
watch:
- action: sync+restart
path: ./api/config
target: /app/config
- action: sync+restart
path: ./api/src
target: /app/src
|
運作流程:
1
| 檔案變更 → 偵測變更 → 複製到容器 → 重啟服務程序 → 完成
|
動作選擇決策樹
1
2
3
4
5
6
7
8
9
10
11
12
13
| 檔案變更
│
├── 是否影響建構流程?(依賴、Dockerfile)
│ │
│ └── 是 → rebuild
│
├── 應用程式是否支援熱重載?
│ │
│ ├── 是 → sync
│ │
│ └── 否 → sync+restart
│
└── 其他情況 → 根據需求選擇
|
三種動作的比較
| 特性 | sync | rebuild | sync+restart |
|---|
| 執行速度 | 最快 | 最慢 | 中等 |
| 需要重建映像 | 否 | 是 | 否 |
| 服務中斷 | 無 | 有 | 短暫 |
| 適用情境 | 靜態資源/熱重載 | 依賴變更 | 配置/原始碼 |
多服務專案設定範例
以下是一個完整的全端應用程式範例,包含前端、後端 API 和資料庫服務。
專案結構
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| my-fullstack-app/
├── docker-compose.yml
├── frontend/
│ ├── Dockerfile
│ ├── package.json
│ └── src/
│ ├── App.jsx
│ ├── components/
│ └── styles/
├── backend/
│ ├── Dockerfile
│ ├── requirements.txt
│ └── app/
│ ├── main.py
│ ├── routes/
│ └── models/
└── nginx/
└── nginx.conf
|
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
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
| version: "3.9"
services:
# PostgreSQL 資料庫
db:
image: postgres:15-alpine
container_name: app_database
environment:
POSTGRES_DB: myapp
POSTGRES_USER: developer
POSTGRES_PASSWORD: devpassword
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- app_network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U developer -d myapp"]
interval: 10s
timeout: 5s
retries: 5
# Redis 快取
redis:
image: redis:7-alpine
container_name: app_redis
networks:
- app_network
# Python FastAPI 後端
backend:
build:
context: ./backend
dockerfile: Dockerfile
container_name: app_backend
ports:
- "8000:8000"
environment:
DATABASE_URL: postgresql://developer:devpassword@db:5432/myapp
REDIS_URL: redis://redis:6379
DEBUG: "true"
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
networks:
- app_network
develop:
watch:
# Python 原始碼變更 - 同步並重啟
- action: sync+restart
path: ./backend/app
target: /app/app
ignore:
- "**/__pycache__"
- "**/*.pyc"
# 依賴變更 - 完整重建
- action: rebuild
path: ./backend/requirements.txt
# 靜態設定檔 - 僅同步
- action: sync
path: ./backend/static
target: /app/static
# React 前端
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
target: development
container_name: app_frontend
ports:
- "3000:3000"
environment:
REACT_APP_API_URL: http://localhost:8000
WATCHPACK_POLLING: "true"
depends_on:
- backend
networks:
- app_network
develop:
watch:
# React 原始碼 - 利用 HMR 熱重載
- action: sync
path: ./frontend/src
target: /app/src
ignore:
- "**/*.test.js"
- "**/*.test.jsx"
- "**/__tests__"
# 公開資源
- action: sync
path: ./frontend/public
target: /app/public
# 依賴變更 - 重建
- action: rebuild
path: ./frontend/package.json
- action: rebuild
path: ./frontend/package-lock.json
# Nginx 反向代理
nginx:
image: nginx:alpine
container_name: app_nginx
ports:
- "80:80"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- frontend
- backend
networks:
- app_network
develop:
watch:
# Nginx 配置變更需要重啟
- action: sync+restart
path: ./nginx/nginx.conf
target: /etc/nginx/nginx.conf
networks:
app_network:
driver: bridge
volumes:
postgres_data:
|
後端 Dockerfile(backend/Dockerfile)
1
2
3
4
5
6
7
8
9
10
11
12
13
| FROM python:3.11-slim
WORKDIR /app
# 安裝依賴
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 複製應用程式碼
COPY . .
# 開發模式使用 uvicorn 並啟用 reload
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
|
前端 Dockerfile(frontend/Dockerfile)
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
| # 開發階段
FROM node:20-alpine AS development
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
# 生產階段
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
FROM nginx:alpine AS production
COPY --from=builder /app/build /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
|
啟動開發環境
1
2
3
4
5
6
7
8
9
10
11
| # 啟動所有服務並開始監控
docker compose up --watch
# 或者背景執行
docker compose up -d --watch
# 查看日誌
docker compose logs -f
# 查看 watch 狀態
docker compose alpha watch
|
效能優化與忽略規則
忽略規則配置
合理的忽略規則可以顯著提升 Watch 的效能和避免不必要的同步:
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
| services:
app:
build: .
develop:
watch:
- action: sync
path: ./src
target: /app/src
ignore:
# 建構產物
- "**/dist"
- "**/build"
- "**/.next"
# 依賴目錄
- "**/node_modules"
- "**/__pycache__"
- "**/.venv"
- "**/vendor"
# 測試檔案
- "**/*.test.js"
- "**/*.test.ts"
- "**/*.spec.js"
- "**/__tests__"
- "**/coverage"
# IDE 和編輯器
- "**/.idea"
- "**/.vscode"
- "**/*.swp"
- "**/*.swo"
- "**/*~"
# 版本控制
- "**/.git"
- "**/.gitignore"
# 日誌和暫存
- "**/logs"
- "**/*.log"
- "**/tmp"
- "**/.cache"
# 環境設定
- "**/.env*"
- "**/*.local"
|
Glob 模式語法
| 模式 | 說明 | 範例匹配 |
|---|
* | 匹配任意字元(不含路徑分隔符) | *.js 匹配 app.js |
** | 匹配任意層級目錄 | **/test 匹配 src/test、lib/test |
? | 匹配單一字元 | file?.txt 匹配 file1.txt |
[abc] | 匹配括號內任一字元 | [abc].txt 匹配 a.txt |
{a,b} | 匹配大括號內任一模式 | *.{js,ts} 匹配 .js 和 .ts |
效能優化建議
1. 縮小監控範圍
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| # 不好的做法 - 監控整個專案
develop:
watch:
- action: sync
path: .
target: /app
# 好的做法 - 只監控需要的目錄
develop:
watch:
- action: sync
path: ./src
target: /app/src
- action: sync
path: ./public
target: /app/public
|
2. 分離不同動作的規則
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| develop:
watch:
# 高頻變更 - 使用 sync
- action: sync
path: ./src/components
target: /app/src/components
# 低頻但重要 - 使用 rebuild
- action: rebuild
path: ./package.json
# 配置變更 - 使用 sync+restart
- action: sync+restart
path: ./config
target: /app/config
|
3. 使用 .dockerignore
確保 .dockerignore 檔案正確配置,減少建構時的檔案傳輸:
1
2
3
4
5
6
7
8
9
10
11
12
| # .dockerignore
node_modules
npm-debug.log
.git
.gitignore
.env*
*.md
.vscode
.idea
coverage
dist
build
|
4. 多階段建構優化
1
2
3
4
5
6
7
8
9
10
11
| # 利用 Docker 層級快取
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci
FROM node:20-alpine AS development
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
CMD ["npm", "run", "dev"]
|
與其他開發工具整合
與 VS Code 整合
Dev Containers 配置
在 .devcontainer/devcontainer.json 中整合 Docker Compose Watch:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| {
"name": "My App Dev Container",
"dockerComposeFile": "../docker-compose.yml",
"service": "backend",
"workspaceFolder": "/app",
"features": {
"ghcr.io/devcontainers/features/docker-in-docker:2": {}
},
"customizations": {
"vscode": {
"extensions": [
"ms-python.python",
"ms-azuretools.vscode-docker"
],
"settings": {
"python.defaultInterpreterPath": "/usr/local/bin/python"
}
}
},
"postStartCommand": "docker compose up --watch -d"
}
|
tasks.json 配置
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
| {
"version": "2.0.0",
"tasks": [
{
"label": "Docker Compose Watch",
"type": "shell",
"command": "docker compose up --watch",
"group": {
"kind": "build",
"isDefault": true
},
"presentation": {
"reveal": "always",
"panel": "dedicated"
},
"problemMatcher": []
},
{
"label": "Docker Compose Down",
"type": "shell",
"command": "docker compose down",
"problemMatcher": []
}
]
}
|
與 Make 整合
建立 Makefile 簡化常用操作:
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
| .PHONY: dev watch build down logs clean
# 預設目標
dev: watch
# 啟動 watch 模式
watch:
docker compose up --watch
# 背景執行 watch
watch-detach:
docker compose up -d --watch
# 建構映像
build:
docker compose build
# 停止服務
down:
docker compose down
# 完整清除
clean:
docker compose down -v --rmi local
# 查看日誌
logs:
docker compose logs -f
# 重建並啟動
rebuild: build watch
# 進入後端容器
shell-backend:
docker compose exec backend /bin/sh
# 進入前端容器
shell-frontend:
docker compose exec frontend /bin/sh
# 執行測試
test:
docker compose exec backend pytest
docker compose exec frontend npm test
# 資料庫遷移
migrate:
docker compose exec backend alembic upgrade head
|
與 Git Hooks 整合
使用 pre-commit 確保程式碼品質:
1
2
3
4
5
6
7
8
9
10
| # .pre-commit-config.yaml
repos:
- repo: local
hooks:
- id: docker-compose-validate
name: Validate Docker Compose
entry: docker compose config -q
language: system
files: docker-compose.*\.ya?ml$
pass_filenames: false
|
與 CI/CD 整合
GitHub Actions 範例:
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
| # .github/workflows/docker-build.yml
name: Docker Build
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build with Docker Compose
run: docker compose build
- name: Run tests
run: |
docker compose up -d
docker compose exec -T backend pytest
docker compose exec -T frontend npm test
docker compose down
|
最佳實務與常見問題
最佳實務
1. 根據服務特性選擇動作
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
| services:
# 支援 HMR 的前端框架 → sync
react-app:
develop:
watch:
- action: sync
path: ./src
target: /app/src
# 需要重新載入的後端 → sync+restart
flask-api:
develop:
watch:
- action: sync+restart
path: ./app
target: /app/app
# 編譯型語言 → rebuild
go-service:
develop:
watch:
- action: rebuild
path: ./cmd
- action: rebuild
path: ./internal
|
2. 分離開發和生產配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| # docker-compose.yml (基礎配置)
services:
app:
build: .
# docker-compose.dev.yml (開發配置)
services:
app:
develop:
watch:
- action: sync
path: ./src
target: /app/src
# docker-compose.prod.yml (生產配置)
services:
app:
deploy:
replicas: 3
|
使用方式:
1
2
3
4
5
| # 開發環境
docker compose -f docker-compose.yml -f docker-compose.dev.yml up --watch
# 生產環境
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
|
3. 善用環境變數
1
2
3
4
5
6
7
8
9
| services:
app:
build:
context: .
args:
NODE_ENV: development
environment:
- DEBUG=${DEBUG:-true}
- LOG_LEVEL=${LOG_LEVEL:-debug}
|
4. 實作健康檢查
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| services:
api:
build: .
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
develop:
watch:
- action: sync+restart
path: ./app
target: /app/app
|
常見問題與解決方案
Q1: Watch 模式沒有偵測到檔案變更
可能原因與解決方案:
路徑配置錯誤
1
2
3
4
5
6
| # 確保路徑相對於 docker-compose.yml
develop:
watch:
- action: sync
path: ./src # 正確:相對路徑
target: /app/src
|
檔案被忽略規則排除
1
2
3
| # 檢查 ignore 規則是否過於寬鬆
ignore:
- "**/*.js" # 這會忽略所有 JS 檔案!
|
檔案系統權限問題
1
2
3
4
5
| # 確認檔案權限
ls -la ./src
# 必要時調整權限
chmod -R 755 ./src
|
Q2: 容器頻繁重建導致效能問題
解決方案:
1
2
3
4
5
6
7
8
9
10
11
12
13
| # 使用 sync 取代 rebuild(適用時)
develop:
watch:
# 改用 sync+restart 而非 rebuild
- action: sync+restart
path: ./src
target: /app/src
# 只有真正需要時才 rebuild
- action: rebuild
path: ./Dockerfile
- action: rebuild
path: ./package.json
|
Q3: Windows/macOS 上的效能問題
解決方案:
- 使用 Docker Desktop 的最新版本
- 啟用 VirtioFS(macOS)或 WSL2(Windows)
- 減少監控檔案數量
1
2
3
4
5
6
7
8
9
| # 精確指定監控路徑
develop:
watch:
- action: sync
path: ./src/app
target: /app/src/app
ignore:
- "**/*.test.*"
- "**/fixtures"
|
Q4: 容器內應用程式沒有自動重新載入
解決方案:
確保應用程式配置了正確的開發模式:
1
2
3
4
5
6
7
8
| # Python + uvicorn
CMD ["uvicorn", "main:app", "--reload", "--host", "0.0.0.0"]
# Node.js + nodemon
CMD ["npx", "nodemon", "--watch", "src", "src/index.js"]
# Go + air
CMD ["air", "-c", ".air.toml"]
|
Q5: 如何除錯 Watch 配置問題
1
2
3
4
5
6
7
8
9
10
11
| # 驗證 Compose 配置
docker compose config
# 查看詳細日誌
docker compose up --watch --verbose
# 檢查容器內檔案
docker compose exec service_name ls -la /app/src
# 監控 Docker 事件
docker events --filter 'type=container'
|
Q6: 多人協作時的配置衝突
解決方案:使用 override 檔案
1
2
3
4
5
6
7
8
| # docker-compose.override.yml(個人配置,加入 .gitignore)
services:
app:
develop:
watch:
- action: sync
path: ./src
target: /app/src
|
1
2
| # .gitignore
docker-compose.override.yml
|
除錯技巧
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| # 1. 確認 Compose 版本支援 Watch
docker compose version
# 需要 v2.22.0 或更高版本
# 2. 驗證配置語法
docker compose config --format json | jq '.services.app.develop'
# 3. 監控同步狀態
docker compose alpha watch --dry-run
# 4. 檢查容器檔案系統
docker compose exec app find /app -newer /app/marker -type f
# 5. 比較本地和容器內檔案
diff <(cat ./src/app.js) <(docker compose exec app cat /app/src/app.js)
|
總結
Docker Compose Watch 是現代容器化開發工作流程的重要工具,它解決了傳統開發模式中的諸多痛點:
- 提升開發效率:自動化的檔案同步和服務重載,減少手動操作
- 靈活的更新策略:sync、rebuild、sync+restart 三種動作適用不同場景
- 精細的控制能力:透過 ignore 規則和多規則配置實現精確控制
- 跨平台一致性:在 Windows、macOS、Linux 上提供一致的開發體驗
- 易於整合:與 VS Code、Make、CI/CD 等工具無縫整合
建議您從小型專案開始嘗試,熟悉各種動作類型的適用場景,逐步將 Docker Compose Watch 整合到您的開發工作流程中。
參考資源