claude-code/claude-zh/skills/docker-patterns/SKILL.md

364 lines
8.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
name: docker-patterns
description: Docker 和 Docker Compose 模式,涵蓋在地開發、容器安全性、網路配置、磁碟卷 (Volume) 策略以及多服務調度。
---
# Docker 模式 (Docker Patterns)
針對容器化開發的 Docker 與 Docker Compose 最佳實踐。
## 何時啟用
- 為在地開發設置 Docker Compose。
- 設計多容器架構。
- 排除容器網路或磁碟卷相關故障。
- 審查 Dockerfile 的安全性與映像檔大小。
- 從在地開發流程遷移至容器化工作流。
## 用於在地開發的 Docker Compose
### 標準網頁應用技術棧 (Standard Web App Stack)
```yaml
# docker-compose.yml
services:
app:
build:
context: .
target: dev # 使用多階段 Dockerfile 的 dev 階段
ports:
- "3000:3000"
volumes:
- .:/app # 綁定掛載 (Bind mount) 以實現熱重載 (Hot reload)
- /app/node_modules # 匿名磁碟卷 -- 保留容器內的套件依賴
environment:
- DATABASE_URL=postgres://postgres:postgres@db:5432/app_dev
- REDIS_URL=redis://redis:6379/0
- NODE_ENV=development
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
command: npm run dev
db:
image: postgres:16-alpine
ports:
- "5432:5432"
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: app_dev
volumes:
- pgdata:/var/lib/postgresql/data
- ./scripts/init-db.sql:/docker-entrypoint-initdb.d/init.sql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 3s
retries: 5
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redisdata:/data
mailpit: # 在地電子郵件測試
image: axllent/mailpit
ports:
- "8025:8025" # 網頁介面
- "1025:1025" # SMTP 服務
volumes:
pgdata:
redisdata:
```
### 開發 vs 生產環境 Dockerfile
```dockerfile
# 階段一:依賴套件 (deps)
FROM node:22-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
# 階段二:開發環境 (dev - 熱重載、除錯工具)
FROM node:22-alpine AS dev
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
EXPOSE 3000
CMD ["npm", "run", "dev"]
# 階段三:建置 (build)
FROM node:22-alpine AS build
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build && npm prune --production
# 階段四:生產環境 (production - 最簡映像檔)
FROM node:22-alpine AS production
WORKDIR /app
RUN addgroup -g 1001 -S appgroup && adduser -S appuser -u 1001
USER appuser
COPY --from=build --chown=appuser:appgroup /app/dist ./dist
COPY --from=build --chown=appuser:appgroup /app/node_modules ./node_modules
COPY --from=build --chown=appuser:appgroup /app/package.json ./
ENV NODE_ENV=production
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s CMD wget -qO- http://localhost:3000/health || exit 1
CMD ["node", "dist/server.js"]
```
### 覆蓋檔案 (Override Files)
```yaml
# docker-compose.override.yml (自動載入,僅限開發用的設定)
services:
app:
environment:
- DEBUG=app:*
- LOG_LEVEL=debug
ports:
- "9229:9229" # Node.js 除錯器端口
# docker-compose.prod.yml (顯式用於生產環境)
services:
app:
build:
target: production
restart: always
deploy:
resources:
limits:
cpus: "1.0"
memory: 512M
```
```bash
# 開發模式 (會自動載入 override 檔案)
docker compose up
# 生產模式
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
```
## 網路配置 (Networking)
### 服務探索 (Service Discovery)
在同一個 Compose 網路中的服務可透過「服務名稱」進行解析:
```
# 在 "app" 容器內可以使用:
postgres://postgres:postgres@db:5432/app_dev # "db" 會解析為 db 容器
redis://redis:6379/0 # "redis" 會解析為 redis 容器
```
### 自定義網路
```yaml
services:
frontend:
networks:
- frontend-net
api:
networks:
- frontend-net
- backend-net
db:
networks:
- backend-net # 僅 api 可存取frontend 無法存取
networks:
frontend-net:
backend-net:
```
### 僅暴露必要的端口
```yaml
services:
db:
ports:
- "127.0.0.1:5432:5432" # 僅允許來自 Host 的存取,不對公開網路開放
# 在生產環境中可完全省略 ports -- 僅讓 Docker 內部網路存取
```
## 磁碟卷策略 (Volume Strategies)
```yaml
volumes:
# 具名磁碟卷 (Named volume):在容器重啟後仍保留資料,由 Docker 管理
pgdata:
# 綁定掛載 (Bind mount):將主機目錄映射到容器內 (用於開發)
# - ./src:/app/src
# 匿名磁碟卷 (Anonymous volume):保護容器內生成的內容不被綁定掛載覆蓋
# - /app/node_modules
```
### 常見模式
```yaml
services:
app:
volumes:
- .:/app # 源碼 (綁定掛載,實現熱重載)
- /app/node_modules # 避免主機環境干擾容器內的 node_modules
- /app/.next # 保留建構快取 (Build cache)
db:
volumes:
- pgdata:/var/lib/postgresql/data # 持久化資料
- ./scripts/init.sql:/docker-entrypoint-initdb.d/init.sql # 初始化腳本
```
## 容器安全性
### Dockerfile 強化 (Hardening)
```dockerfile
# 1. 使用具體的標籤 (絕對不要使用 :latest)
FROM node:22.12-alpine3.20
# 2. 以非 root 使用者執行
RUN addgroup -g 1001 -S app && adduser -S app -u 1001
USER app
# 3. 移除不必要的權限能力 (Capabilities於 compose 中設定)
# 4. 盡可能使用唯讀 (Read-only) 根文件系統
# 5. 映像檔各層 (Layers) 中不要包含敏感資訊 (Secrets)
```
### Compose 安全設定
```yaml
services:
app:
security_opt:
- no-new-privileges:true
read_only: true
tmpfs:
- /tmp
- /app/.cache
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE # 僅用於綁定小於 1024 的端口時
```
### 敏感資訊管理 (Secret Management)
```yaml
# 推薦做法 (GOOD):使用環境變數 (在執行時注入)
services:
app:
env_file:
- .env # 絕不將 .env 提交至 git
environment:
- API_KEY # 從主機環境承襲
# 推薦做法 (GOOD):使用 Docker Secrets (僅限 Swarm 模式)
secrets:
db_password:
file: ./secrets/db_password.txt
services:
db:
secrets:
- db_password
# 錯誤做法 (BAD):將敏感資訊寫死在映像檔中
# ENV API_KEY=sk-proj-xxxxx # 絕對不要這樣做
```
## .dockerignore 建議配置
```
node_modules
.git
.env
.env.*
dist
coverage
*.log
.next
.cache
docker-compose*.yml
Dockerfile*
README.md
tests/
```
## 排除故障 (Debugging)
### 常用指令
```bash
# 查看日誌
docker compose logs -f app # 跟隨 app 日誌輸出
docker compose logs --tail=50 db # 查看 db 的最後 50 行日誌
# 在執行中的容器內執行指令
docker compose exec app sh # 進入 app 的 shell
docker compose exec db psql -U postgres # 連接至 postgres
# 檢查狀態
docker compose ps # 查看執行中的服務
docker compose top # 查看各容器內的程序
docker stats # 查看資源佔用情況
# 重新建置
docker compose up --build # 重新建置映像檔並啟動
docker compose build --no-cache app # 強制完整地重新建置 app
# 清理環境
docker compose down # 停止並移除容器
docker compose down -v # 停止、移除容器及磁碟卷 (具破壞性!)
docker system prune # 移除未使用的映像檔與容器
```
### 排除網路問題
```bash
# 在容器內檢查 DNS 解析
docker compose exec app nslookup db
# 檢查連通性
docker compose exec app wget -qO- http://api:3000/health
# 檢查 Docker 網路
docker network ls
docker network inspect <project名>_default
```
## 反模式 (Anti-Patterns)
```
# 不良做法:在生產環境使用 docker compose 而無任何調度機制
# 多容器的工作負載在生產環境應使用 Kubernetes, ECS 或 Docker Swarm
# 不良做法:在容器內存儲資料卻不使用磁碟卷 (Volumes)
# 容器是臨時性的 -- 若無磁碟卷,重啟後所有資料都會遺失
# 不良做法:以 root 使用者執行
# 務必建立並使用非 root 使用者
# 不良做法:使用 :latest 標籤
# 請固定具體版本,以確保建構的可重現性 (Reproducible builds)
# 不良做法:將所有服務塞進一個巨大的單一容器中
# 應落實關注點分離:一個容器原則上僅執行一個程序
# 不良做法:將敏感資訊寫在 docker-compose.yml 中
# 請使用 .env 檔案 (需 gitignore) 或 Docker secrets
```