Docker 完整指南(三):進階應用與生產實踐

🎯 前言

經過前兩篇文章的學習,我們已經掌握了 Docker 的基礎概念與指令操作。本文將深入探討 Docker 的進階應用,涵蓋從開發到生產環境的完整實踐。

本文重點:

  • Dockerfile 最佳實踐與優化
  • 多階段建立(Multi-stage Build)
  • Docker Compose 完整應用
  • 網路進階配置
  • 安全性強化
  • 效能調優
  • 生產環境部署策略

📝 Dockerfile 深度解析

Dockerfile 指令完整對照表

指令作用層級影響範例
FROM指定基礎映像FROM node:18-alpine
LABEL添加元資料LABEL version="1.0"
RUN執行指令RUN npm install
CMD容器啟動指令CMD ["npm", "start"]
ENTRYPOINT容器進入點ENTRYPOINT ["python"]
COPY複製檔案COPY app.py /app/
ADD複製並解壓ADD archive.tar.gz /app/
ENV設定環境變數ENV NODE_ENV=production
ARG建立時變數ARG VERSION=1.0
WORKDIR設定工作目錄WORKDIR /app
EXPOSE聲明埠EXPOSE 8080
VOLUME定義掛載點VOLUME ["/data"]
USER切換使用者USER appuser
HEALTHCHECK健康檢查HEALTHCHECK CMD curl -f http://localhost/
ONBUILD觸發器指令ONBUILD COPY . /app
SHELL設定 ShellSHELL ["/bin/bash", "-c"]
STOPSIGNAL停止信號STOPSIGNAL SIGTERM

Dockerfile 最佳實踐

1. 基礎映像選擇

 1# ❌ 不推薦:使用完整版本
 2FROM ubuntu:latest
 3
 4# ✅ 推薦:使用 Alpine 基礎映像
 5FROM node:18-alpine
 6
 7# ✅ 推薦:使用 Distroless(最小化)
 8FROM gcr.io/distroless/nodejs18-debian11
 9
10# ✅ 推薦:指定確切版本
11FROM python:3.11.5-slim-bookworm

映像大小對照:

基礎映像大小適用場景
ubuntu:latest~77 MB完整功能需求
node:18~900 MB開發環境
node:18-slim~170 MB較小生產映像
node:18-alpine~110 MB最小化生產映像
distroless~50 MB安全性要求高

2. 層級優化技巧

 1# ❌ 不推薦:每個 RUN 創建一層
 2FROM ubuntu:20.04
 3RUN apt-get update
 4RUN apt-get install -y python3
 5RUN apt-get install -y pip
 6RUN pip install flask
 7
 8# ✅ 推薦:合併 RUN 指令
 9FROM ubuntu:20.04
10RUN apt-get update && \
11    apt-get install -y \
12        python3 \
13        python3-pip && \
14    pip3 install flask && \
15    rm -rf /var/lib/apt/lists/*
16
17# ✅ 更好:使用 heredoc(Docker 23.0+)
18FROM ubuntu:20.04
19RUN <<EOF
20apt-get update
21apt-get install -y python3 python3-pip
22pip3 install flask
23rm -rf /var/lib/apt/lists/*
24EOF

3. 快取優化策略

 1# ❌ 不推薦:先複製所有檔案
 2FROM node:18-alpine
 3WORKDIR /app
 4COPY . .
 5RUN npm install
 6
 7# ✅ 推薦:先複製依賴檔案,利用快取
 8FROM node:18-alpine
 9WORKDIR /app
10
11# 先複製依賴定義檔案
12COPY package*.json ./
13RUN npm ci --only=production
14
15# 再複製程式碼
16COPY . .
17
18# 建立時快取 node_modules
19RUN npm run build

快取策略說明:

graph TB
    A[COPY package.json] -->|快取命中| B[使用快取的 npm install]
    A -->|檔案變更| C[重新執行 npm install]
    B --> D[COPY 原始碼]
    C --> D
    D -->|程式碼變更| E[重新建立]
    D -->|程式碼未變| F[使用快取]

    style B fill:#4ecdc4
    style F fill:#4ecdc4
    style C fill:#ff6b6b
    style E fill:#ff6b6b

4. .dockerignore 檔案

 1# .dockerignore 範例
 2
 3# 版本控制
 4.git
 5.gitignore
 6.svn
 7
 8# 依賴目錄
 9node_modules
10bower_components
11__pycache__
12*.pyc
13.Python
14
15# 建立產物
16dist
17build
18*.egg-info
19target
20
21# IDE 設定
22.idea
23.vscode
24*.swp
25*.swo
26*~
27
28# 日誌與臨時檔案
29*.log
30npm-debug.log*
31logs
32tmp
33temp
34
35# 測試相關
36coverage
37.nyc_output
38.pytest_cache
39*.test
40
41# 文件
42README.md
43CHANGELOG.md
44LICENSE
45docs
46
47# CI/CD
48.github
49.gitlab-ci.yml
50.travis.yml
51Jenkinsfile
52
53# Docker
54Dockerfile*
55docker-compose*.yml
56.dockerignore
57
58# 環境變數(敏感資訊)
59.env
60.env.local
61.env.*.local
62secrets.yml

完整的 Dockerfile 範例

Node.js 應用程式

 1# 使用官方 Node.js 18 Alpine 映像
 2FROM node:18-alpine AS base
 3
 4# 添加元資料
 5LABEL maintainer="devops@example.com" \
 6      version="1.0.0" \
 7      description="Node.js Application"
 8
 9# 安裝 dumb-init(正確處理信號)
10RUN apk add --no-cache dumb-init
11
12# 設定工作目錄
13WORKDIR /app
14
15# 設定環境變數
16ENV NODE_ENV=production \
17    PORT=3000
18
19# ===== 依賴階段 =====
20FROM base AS dependencies
21
22# 複製依賴定義檔案
23COPY package*.json ./
24
25# 安裝生產依賴
26RUN npm ci --only=production && \
27    npm cache clean --force
28
29# ===== 建立階段 =====
30FROM base AS build
31
32# 複製依賴定義檔案
33COPY package*.json ./
34
35# 安裝所有依賴(包含開發依賴)
36RUN npm ci && \
37    npm cache clean --force
38
39# 複製原始碼
40COPY . .
41
42# 執行建立(如果需要)
43RUN npm run build
44
45# ===== 生產階段 =====
46FROM base AS production
47
48# 創建非 root 使用者
49RUN addgroup -g 1001 -S nodejs && \
50    adduser -S nodejs -u 1001
51
52# 從依賴階段複製 node_modules
53COPY --from=dependencies --chown=nodejs:nodejs /app/node_modules ./node_modules
54
55# 從建立階段複製建立產物
56COPY --from=build --chown=nodejs:nodejs /app/dist ./dist
57COPY --chown=nodejs:nodejs package*.json ./
58
59# 切換到非 root 使用者
60USER nodejs
61
62# 暴露埠
63EXPOSE 3000
64
65# 健康檢查
66HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
67    CMD node healthcheck.js || exit 1
68
69# 使用 dumb-init 啟動應用
70ENTRYPOINT ["dumb-init", "--"]
71CMD ["node", "dist/server.js"]

Python Flask 應用

 1# ===== 建立階段 =====
 2FROM python:3.11-slim AS builder
 3
 4# 設定工作目錄
 5WORKDIR /app
 6
 7# 安裝建立依賴
 8RUN apt-get update && \
 9    apt-get install -y --no-install-recommends \
10        gcc \
11        python3-dev && \
12    rm -rf /var/lib/apt/lists/*
13
14# 複製依賴檔案
15COPY requirements.txt .
16
17# 安裝 Python 依賴到虛擬環境
18RUN python -m venv /opt/venv && \
19    /opt/venv/bin/pip install --no-cache-dir -r requirements.txt
20
21# ===== 生產階段 =====
22FROM python:3.11-slim
23
24# 設定標籤
25LABEL maintainer="devops@example.com"
26
27# 安裝運行時依賴
28RUN apt-get update && \
29    apt-get install -y --no-install-recommends \
30        curl && \
31    rm -rf /var/lib/apt/lists/* && \
32    rm -rf /tmp/* /var/tmp/*
33
34# 創建應用使用者
35RUN useradd -m -u 1000 -s /bin/bash appuser
36
37# 設定工作目錄
38WORKDIR /app
39
40# 從建立階段複製虛擬環境
41COPY --from=builder /opt/venv /opt/venv
42
43# 設定環境變數
44ENV PATH="/opt/venv/bin:$PATH" \
45    PYTHONUNBUFFERED=1 \
46    PYTHONDONTWRITEBYTECODE=1 \
47    FLASK_APP=app.py \
48    FLASK_ENV=production
49
50# 複製應用程式碼
51COPY --chown=appuser:appuser . .
52
53# 切換到非 root 使用者
54USER appuser
55
56# 暴露埠
57EXPOSE 5000
58
59# 健康檢查
60HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
61    CMD curl -f http://localhost:5000/health || exit 1
62
63# 啟動應用
64CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "4", "app:app"]

🔨 多階段建立(Multi-stage Build)

多階段建立的優勢

graph LR
    A[原始碼] --> B[建立階段]
    B --> C[編譯產物]
    C --> D[生產階段]
    D --> E[最終映像]

    B -.->|不包含| E
    C -->|只複製需要的| E

    style B fill:#ff6b6b
    style D fill:#4ecdc4
    style E fill:#a8e6cf

效益對照表:

項目單階段建立多階段建立
映像大小1-2 GB100-300 MB
建立工具包含不包含
安全性低(包含編譯器)高(只有執行檔)
建立時間較快稍慢(但可快取)
維護性

Go 應用多階段範例

 1# ===== 建立階段 =====
 2FROM golang:1.21-alpine AS builder
 3
 4# 安裝建立工具
 5RUN apk add --no-cache git ca-certificates tzdata
 6
 7# 設定工作目錄
 8WORKDIR /build
 9
10# 複製 go mod 檔案
11COPY go.mod go.sum ./
12
13# 下載依賴
14RUN go mod download && \
15    go mod verify
16
17# 複製原始碼
18COPY . .
19
20# 建立應用
21RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \
22    -ldflags='-w -s -extldflags "-static"' \
23    -a \
24    -o /app/server \
25    ./cmd/server
26
27# ===== 生產階段 =====
28FROM scratch
29
30# 從 builder 複製必要檔案
31COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
32COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo
33COPY --from=builder /app/server /server
34
35# 設定時區
36ENV TZ=Asia/Taipei
37
38# 暴露埠
39EXPOSE 8080
40
41# 非 root 使用者
42USER 65534:65534
43
44# 啟動應用
45ENTRYPOINT ["/server"]

映像大小對照:

  • 單階段建立:~800 MB
  • 多階段建立:~10 MB
  • 減少:98.75%

Java Spring Boot 多階段範例

 1# ===== 建立階段 =====
 2FROM maven:3.9-eclipse-temurin-17 AS build
 3
 4WORKDIR /build
 5
 6# 複製 pom.xml 並下載依賴(快取優化)
 7COPY pom.xml .
 8RUN mvn dependency:go-offline -B
 9
10# 複製原始碼並建立
11COPY src ./src
12RUN mvn clean package -DskipTests && \
13    java -Djarmode=layertools -jar target/*.jar extract
14
15# ===== 生產階段 =====
16FROM eclipse-temurin:17-jre-alpine
17
18# 設定標籤
19LABEL maintainer="devops@example.com"
20
21# 安裝工具
22RUN apk add --no-cache curl
23
24# 創建應用使用者
25RUN addgroup -g 1000 spring && \
26    adduser -D -u 1000 -G spring spring
27
28WORKDIR /app
29
30# 從建立階段複製分層
31COPY --from=build /build/dependencies/ ./
32COPY --from=build /build/spring-boot-loader/ ./
33COPY --from=build /build/snapshot-dependencies/ ./
34COPY --from=build /build/application/ ./
35
36# 設定擁有者
37RUN chown -R spring:spring /app
38
39USER spring
40
41EXPOSE 8080
42
43# 健康檢查
44HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
45    CMD curl -f http://localhost:8080/actuator/health || exit 1
46
47# 啟動應用
48ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]

🎭 Docker Compose 深入應用

Docker Compose 檔案結構

 1version: "3.8"  # Compose 檔案版本
 2
 3services:       # 服務定義
 4  service-name:
 5    build:      # 建立配置
 6    image:      # 映像名稱
 7    ports:      # 埠映射
 8    volumes:    # 資料卷掛載
 9    environment: # 環境變數
10    depends_on: # 依賴關係
11    networks:   # 網路配置
12    deploy:     # 部署配置
13    healthcheck: # 健康檢查
14
15volumes:        # 資料卷定義
16networks:       # 網路定義
17configs:        # 配置定義
18secrets:        # 密鑰定義

完整的生產級 Compose 範例

  1version: "3.8"
  2
  3# ========== 服務定義 ==========
  4services:
  5
  6  # Nginx 反向代理
  7  nginx:
  8    image: nginx:alpine
  9    container_name: nginx-proxy
 10    restart: unless-stopped
 11    ports:
 12      - "80:80"
 13      - "443:443"
 14    volumes:
 15      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
 16      - ./nginx/conf.d:/etc/nginx/conf.d:ro
 17      - ./nginx/ssl:/etc/nginx/ssl:ro
 18      - nginx-cache:/var/cache/nginx
 19      - nginx-logs:/var/log/nginx
 20    networks:
 21      - frontend
 22    depends_on:
 23      - web
 24    healthcheck:
 25      test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/health"]
 26      interval: 30s
 27      timeout: 10s
 28      retries: 3
 29      start_period: 40s
 30    labels:
 31      - "com.example.description=Nginx reverse proxy"
 32      - "com.example.department=ops"
 33      - "com.example.environment=production"
 34
 35  # Web 應用
 36  web:
 37    build:
 38      context: ./app
 39      dockerfile: Dockerfile
 40      target: production
 41      args:
 42        - NODE_ENV=production
 43        - BUILD_DATE=${BUILD_DATE}
 44        - VERSION=${VERSION}
 45    image: myapp:${VERSION:-latest}
 46    container_name: web-app
 47    restart: unless-stopped
 48    ports:
 49      - "3000:3000"
 50    environment:
 51      - NODE_ENV=production
 52      - PORT=3000
 53      - DATABASE_URL=postgres://postgres:${DB_PASSWORD}@postgres:5432/myapp
 54      - REDIS_URL=redis://redis:6379
 55      - LOG_LEVEL=${LOG_LEVEL:-info}
 56    env_file:
 57      - .env
 58    volumes:
 59      - app-logs:/app/logs
 60      - app-uploads:/app/uploads
 61    networks:
 62      - frontend
 63      - backend
 64    depends_on:
 65      postgres:
 66        condition: service_healthy
 67      redis:
 68        condition: service_started
 69    healthcheck:
 70      test: ["CMD", "node", "healthcheck.js"]
 71      interval: 30s
 72      timeout: 5s
 73      retries: 3
 74      start_period: 60s
 75    deploy:
 76      resources:
 77        limits:
 78          cpus: '2.0'
 79          memory: 1G
 80        reservations:
 81          cpus: '0.5'
 82          memory: 512M
 83    logging:
 84      driver: "json-file"
 85      options:
 86        max-size: "10m"
 87        max-file: "3"
 88
 89  # PostgreSQL 資料庫
 90  postgres:
 91    image: postgres:15-alpine
 92    container_name: postgres-db
 93    restart: unless-stopped
 94    ports:
 95      - "5432:5432"
 96    environment:
 97      - POSTGRES_USER=postgres
 98      - POSTGRES_PASSWORD=${DB_PASSWORD}
 99      - POSTGRES_DB=myapp
100      - POSTGRES_INITDB_ARGS=--encoding=UTF-8 --lc-collate=C --lc-ctype=C
101      - PGDATA=/var/lib/postgresql/data/pgdata
102    volumes:
103      - postgres-data:/var/lib/postgresql/data
104      - ./database/init:/docker-entrypoint-initdb.d:ro
105      - ./database/backup:/backup
106    networks:
107      - backend
108    healthcheck:
109      test: ["CMD-SHELL", "pg_isready -U postgres"]
110      interval: 10s
111      timeout: 5s
112      retries: 5
113      start_period: 30s
114    deploy:
115      resources:
116        limits:
117          memory: 2G
118    command:
119      - "postgres"
120      - "-c"
121      - "max_connections=200"
122      - "-c"
123      - "shared_buffers=256MB"
124      - "-c"
125      - "effective_cache_size=1GB"
126
127  # Redis 快取
128  redis:
129    image: redis:7-alpine
130    container_name: redis-cache
131    restart: unless-stopped
132    ports:
133      - "6379:6379"
134    volumes:
135      - redis-data:/data
136      - ./redis/redis.conf:/usr/local/etc/redis/redis.conf:ro
137    networks:
138      - backend
139    healthcheck:
140      test: ["CMD", "redis-cli", "ping"]
141      interval: 10s
142      timeout: 3s
143      retries: 5
144    command: redis-server /usr/local/etc/redis/redis.conf
145    deploy:
146      resources:
147        limits:
148          memory: 512M
149
150  # 背景工作器
151  worker:
152    build:
153      context: ./app
154      dockerfile: Dockerfile
155      target: production
156    image: myapp:${VERSION:-latest}
157    container_name: app-worker
158    restart: unless-stopped
159    environment:
160      - NODE_ENV=production
161      - WORKER_MODE=true
162      - DATABASE_URL=postgres://postgres:${DB_PASSWORD}@postgres:5432/myapp
163      - REDIS_URL=redis://redis:6379
164    volumes:
165      - app-logs:/app/logs
166    networks:
167      - backend
168    depends_on:
169      - postgres
170      - redis
171    command: ["node", "worker.js"]
172    deploy:
173      replicas: 2
174      resources:
175        limits:
176          cpus: '1.0'
177          memory: 512M
178
179  # 監控 - Prometheus
180  prometheus:
181    image: prom/prometheus:latest
182    container_name: prometheus
183    restart: unless-stopped
184    ports:
185      - "9090:9090"
186    volumes:
187      - ./monitoring/prometheus:/etc/prometheus:ro
188      - prometheus-data:/prometheus
189    networks:
190      - monitoring
191    command:
192      - '--config.file=/etc/prometheus/prometheus.yml'
193      - '--storage.tsdb.path=/prometheus'
194      - '--storage.tsdb.retention.time=30d'
195
196  # 監控 - Grafana
197  grafana:
198    image: grafana/grafana:latest
199    container_name: grafana
200    restart: unless-stopped
201    ports:
202      - "3001:3000"
203    environment:
204      - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD}
205      - GF_USERS_ALLOW_SIGN_UP=false
206    volumes:
207      - grafana-data:/var/lib/grafana
208      - ./monitoring/grafana/provisioning:/etc/grafana/provisioning:ro
209    networks:
210      - monitoring
211    depends_on:
212      - prometheus
213
214# ========== 資料卷定義 ==========
215volumes:
216  postgres-data:
217    driver: local
218    driver_opts:
219      type: none
220      o: bind
221      device: ./data/postgres
222  redis-data:
223  app-logs:
224  app-uploads:
225  nginx-cache:
226  nginx-logs:
227  prometheus-data:
228  grafana-data:
229
230# ========== 網路定義 ==========
231networks:
232  frontend:
233    driver: bridge
234    ipam:
235      config:
236        - subnet: 172.20.0.0/24
237  backend:
238    driver: bridge
239    internal: true
240    ipam:
241      config:
242        - subnet: 172.21.0.0/24
243  monitoring:
244    driver: bridge

Compose 環境變數管理

.env 檔案範例:

 1# 應用版本
 2VERSION=1.0.0
 3BUILD_DATE=2023-12-01
 4
 5# 資料庫設定
 6DB_PASSWORD=your_secure_password_here
 7POSTGRES_VERSION=15
 8
 9# Redis 設定
10REDIS_PASSWORD=your_redis_password
11
12# 應用設定
13NODE_ENV=production
14LOG_LEVEL=info
15JWT_SECRET=your_jwt_secret
16
17# Grafana 設定
18GRAFANA_PASSWORD=admin_password
19
20# 其他設定
21TZ=Asia/Taipei

Compose 實用指令

 1# 啟動所有服務(背景運行)
 2docker-compose up -d
 3
 4# 查看服務狀態
 5docker-compose ps
 6
 7# 查看日誌(實時)
 8docker-compose logs -f
 9
10# 查看特定服務日誌
11docker-compose logs -f web
12
13# 進入服務容器
14docker-compose exec web bash
15
16# 擴展服務
17docker-compose up -d --scale worker=3
18
19# 重新建立並啟動
20docker-compose up -d --build
21
22# 停止並刪除所有資源
23docker-compose down
24
25# 停止並刪除(包含資料卷)
26docker-compose down -v
27
28# 驗證配置檔
29docker-compose config
30
31# 只建立映像
32docker-compose build
33
34# 拉取所有映像
35docker-compose pull
36
37# 重啟特定服務
38docker-compose restart web
39
40# 查看資源使用
41docker-compose top

🔐 容器安全性最佳實踐

安全性檢查清單

類別檢查項目實施方法
基礎映像使用最小化映像Alpine, Distroless
漏洞掃描定期掃描映像docker scan, Trivy
非 root 使用者不使用 root 運行USER 指令
密鑰管理不在映像中存儲密鑰Docker Secrets, 環境變數
網路隔離最小權限網路自訂網路, 防火牆規則
資源限制限制CPU和記憶體deploy.resources
唯讀檔案系統盡可能使用唯讀–read-only
能力限制移除不必要的能力–cap-drop

1. 使用非 root 使用者

 1# ❌ 不推薦:使用 root 使用者
 2FROM nginx:alpine
 3COPY app /usr/share/nginx/html
 4
 5# ✅ 推薦:創建並使用非 root 使用者
 6FROM nginx:alpine
 7
 8# 創建使用者和群組
 9RUN addgroup -g 1001 -S appgroup && \
10    adduser -S appuser -u 1001 -G appgroup
11
12# 設定檔案權限
13COPY --chown=appuser:appgroup app /app
14
15# 切換使用者
16USER appuser
17
18# 使用非特權埠
19EXPOSE 8080

2. 最小化映像攻擊面

 1# 使用最小化基礎映像
 2FROM gcr.io/distroless/nodejs18-debian11
 3
 4# 或使用 Alpine
 5FROM node:18-alpine
 6
 7# 移除不必要的套件
 8RUN apk del apk-tools && \
 9    rm -rf /var/cache/apk/*
10
11# 唯讀檔案系統
12# docker run --read-only --tmpfs /tmp myapp

3. 密鑰管理

 1# 使用 Docker Secrets(Swarm 模式)
 2echo "my_secret_password" | docker secret create db_password -
 3
 4# 在 Compose 中使用
 5version: "3.8"
 6services:
 7  app:
 8    image: myapp
 9    secrets:
10      - db_password
11secrets:
12  db_password:
13    external: true
14
15# 在應用中讀取
16# cat /run/secrets/db_password

4. 映像掃描

 1# 使用 Docker Scan
 2docker scan myapp:latest
 3
 4# 使用 Trivy
 5docker run --rm \
 6  -v /var/run/docker.sock:/var/run/docker.sock \
 7  aquasec/trivy:latest \
 8  image myapp:latest
 9
10# 使用 Clair
11docker run -p 6060:6060 -d --name clair-db postgres:latest
12docker run -p 6061:6061 --link clair-db:postgres -d quay.io/coreos/clair:latest

5. 容器運行時安全

 1# 限制容器能力
 2docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE myapp
 3
 4# 使用安全選項
 5docker run \
 6  --security-opt=no-new-privileges:true \
 7  --security-opt=apparmor=docker-default \
 8  myapp
 9
10# 唯讀根檔案系統
11docker run --read-only --tmpfs /tmp --tmpfs /run myapp
12
13# 資源限制
14docker run \
15  --memory="512m" \
16  --cpus="1.0" \
17  --pids-limit=100 \
18  myapp

⚡ 效能優化策略

映像大小優化

graph TB
    A[映像優化策略] --> B[選擇精簡基礎映像]
    A --> C[多階段建立]
    A --> D[合併 RUN 指令]
    A --> E[清理快取與臨時檔案]
    A --> F[使用 .dockerignore]

    B --> B1[Alpine: ~5-10MB<br/>Distroless: ~20-50MB]
    C --> C1[只保留執行時檔案<br/>減少 80-95%]
    D --> D1[減少層數<br/>優化快取]
    E --> E1[減少 10-30%]
    F --> F1[加快建立速度<br/>減少內容]

    style A fill:#4ecdc4

優化前後對照:

項目優化前優化後改善
映像大小1.5 GB150 MB90%
建立時間10 分鐘2 分鐘80%
層數25 層8 層68%
啟動時間30 秒3 秒90%

建立優化技巧

 1# 1. 使用 BuildKit(Docker 18.09+)
 2# export DOCKER_BUILDKIT=1
 3
 4# 2. 使用快取掛載(BuildKit)
 5FROM golang:1.21-alpine
 6RUN --mount=type=cache,target=/go/pkg/mod \
 7    --mount=type=cache,target=/root/.cache/go-build \
 8    go build -o app
 9
10# 3. 使用秘密掛載
11RUN --mount=type=secret,id=npmrc,target=/root/.npmrc \
12    npm install
13
14# 4. 並行化建立步驟
15FROM base AS deps-stage1
16RUN npm install package1
17
18FROM base AS deps-stage2
19RUN npm install package2
20
21FROM base AS final
22COPY --from=deps-stage1 /app/node_modules ./
23COPY --from=deps-stage2 /app/node_modules ./

容器運行時優化

 1# docker-compose.yml 效能配置
 2version: "3.8"
 3
 4services:
 5  app:
 6    image: myapp:latest
 7    deploy:
 8      resources:
 9        limits:
10          cpus: '2.0'
11          memory: 2G
12        reservations:
13          cpus: '1.0'
14          memory: 1G
15    # 使用主機網路(效能最佳)
16    network_mode: "host"
17    # 或使用自訂網路
18    networks:
19      - app-network
20    # IPC 模式
21    ipc: "shareable"
22    # 日誌配置
23    logging:
24      driver: "json-file"
25      options:
26        max-size: "10m"
27        max-file: "3"
28        compress: "true"

網路效能優化

 1# 1. 使用主機網路(最佳效能)
 2docker run --network host myapp
 3
 4# 2. 自訂 MTU
 5docker network create --opt com.docker.network.driver.mtu=9000 mynetwork
 6
 7# 3. 禁用 iptables(內部網路)
 8docker network create --internal mynetwork
 9
10# 4. 使用 overlay 網路(Swarm)
11docker network create --driver overlay --attachable mynetwork

🚀 生產環境部署策略

部署架構

graph TB
    subgraph "負載均衡層"
        LB[Load Balancer<br/>Nginx/HAProxy]
    end

    subgraph "應用層"
        APP1[App Container 1]
        APP2[App Container 2]
        APP3[App Container 3]
    end

    subgraph "資料層"
        DB[(PostgreSQL<br/>Primary)]
        REDIS[(Redis<br/>Cache)]
        DB2[(PostgreSQL<br/>Replica)]
    end

    subgraph "監控層"
        PROM[Prometheus]
        GRAF[Grafana]
        ALERT[AlertManager]
    end

    LB --> APP1
    LB --> APP2
    LB --> APP3

    APP1 --> DB
    APP2 --> DB
    APP3 --> DB

    APP1 --> REDIS
    APP2 --> REDIS
    APP3 --> REDIS

    DB --> DB2

    PROM --> APP1
    PROM --> APP2
    PROM --> APP3
    PROM --> DB
    PROM --> REDIS

    GRAF --> PROM
    ALERT --> PROM

    style LB fill:#4ecdc4
    style DB fill:#ff6b6b
    style PROM fill:#feca57

高可用性配置

 1version: "3.8"
 2
 3services:
 4  # 應用服務(多實例)
 5  app:
 6    image: myapp:${VERSION}
 7    deploy:
 8      replicas: 3
 9      update_config:
10        parallelism: 1
11        delay: 10s
12        failure_action: rollback
13        order: start-first
14      rollback_config:
15        parallelism: 1
16        delay: 5s
17      restart_policy:
18        condition: on-failure
19        delay: 5s
20        max_attempts: 3
21        window: 120s
22      placement:
23        constraints:
24          - node.role == worker
25        preferences:
26          - spread: node.labels.zone
27    healthcheck:
28      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
29      interval: 30s
30      timeout: 5s
31      retries: 3
32      start_period: 60s
33
34  # 資料庫(主從複製)
35  postgres-primary:
36    image: postgres:15
37    environment:
38      - POSTGRES_REPLICATION_MODE=master
39    volumes:
40      - postgres-primary-data:/var/lib/postgresql/data
41    deploy:
42      placement:
43        constraints:
44          - node.labels.database == primary
45
46  postgres-replica:
47    image: postgres:15
48    environment:
49      - POSTGRES_REPLICATION_MODE=slave
50      - POSTGRES_MASTER_HOST=postgres-primary
51    volumes:
52      - postgres-replica-data:/var/lib/postgresql/data
53    deploy:
54      replicas: 2
55      placement:
56        constraints:
57          - node.labels.database == replica

滾動更新策略

 1# Docker Swarm 滾動更新
 2docker service update \
 3  --image myapp:v2.0 \
 4  --update-parallelism 1 \
 5  --update-delay 10s \
 6  --update-failure-action rollback \
 7  myapp
 8
 9# 使用 Compose
10docker-compose up -d --no-deps --build app
11
12# 藍綠部署
13# 1. 部署綠色環境
14docker-compose -f docker-compose.green.yml up -d
15
16# 2. 測試綠色環境
17curl http://green.example.com/health
18
19# 3. 切換流量(更新 Nginx 配置)
20docker exec nginx nginx -s reload
21
22# 4. 停止藍色環境
23docker-compose -f docker-compose.blue.yml down

監控與日誌

 1# Prometheus 配置
 2version: "3.8"
 3
 4services:
 5  prometheus:
 6    image: prom/prometheus:latest
 7    volumes:
 8      - ./prometheus.yml:/etc/prometheus/prometheus.yml
 9    command:
10      - '--config.file=/etc/prometheus/prometheus.yml'
11      - '--storage.tsdb.retention.time=30d'
12    ports:
13      - "9090:9090"
14
15  # 應用暴露 metrics
16  app:
17    image: myapp:latest
18    environment:
19      - METRICS_ENABLED=true
20    ports:
21      - "3000:3000"
22      - "9100:9100"  # Metrics 埠
23
24  # Node Exporter
25  node-exporter:
26    image: prom/node-exporter:latest
27    volumes:
28      - /proc:/host/proc:ro
29      - /sys:/host/sys:ro
30      - /:/rootfs:ro
31    command:
32      - '--path.procfs=/host/proc'
33      - '--path.sysfs=/host/sys'
34      - '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
35
36  # cAdvisor(容器監控)
37  cadvisor:
38    image: gcr.io/cadvisor/cadvisor:latest
39    volumes:
40      - /:/rootfs:ro
41      - /var/run:/var/run:ro
42      - /sys:/sys:ro
43      - /var/lib/docker/:/var/lib/docker:ro
44    ports:
45      - "8080:8080"

🎯 CI/CD 整合實踐

GitLab CI 範例

 1# .gitlab-ci.yml
 2stages:
 3  - test
 4  - build
 5  - deploy
 6
 7variables:
 8  DOCKER_DRIVER: overlay2
 9  DOCKER_TLS_CERTDIR: "/certs"
10  IMAGE_NAME: ${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHORT_SHA}
11
12# 測試階段
13test:
14  stage: test
15  image: node:18-alpine
16  script:
17    - npm ci
18    - npm run test
19    - npm run lint
20  coverage: '/Statements\s+:\s+(\d+\.\d+)%/'
21  artifacts:
22    reports:
23      coverage_report:
24        coverage_format: cobertura
25        path: coverage/cobertura-coverage.xml
26
27# 建立映像
28build:
29  stage: build
30  image: docker:24
31  services:
32    - docker:24-dind
33  before_script:
34    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
35  script:
36    - docker build --pull -t $IMAGE_NAME .
37    - docker tag $IMAGE_NAME ${CI_REGISTRY_IMAGE}:latest
38    - docker push $IMAGE_NAME
39    - docker push ${CI_REGISTRY_IMAGE}:latest
40  only:
41    - main
42    - develop
43
44# 部署到開發環境
45deploy-dev:
46  stage: deploy
47  image: docker/compose:latest
48  before_script:
49    - apk add --no-cache openssh-client
50    - eval $(ssh-agent -s)
51    - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
52    - mkdir -p ~/.ssh
53    - chmod 700 ~/.ssh
54    - ssh-keyscan $DEV_SERVER_IP >> ~/.ssh/known_hosts
55  script:
56    - ssh $DEV_USER@$DEV_SERVER_IP "
57        cd /app &&
58        docker-compose pull app &&
59        docker-compose up -d app
60      "
61  environment:
62    name: development
63    url: https://dev.example.com
64  only:
65    - develop
66
67# 部署到生產環境
68deploy-prod:
69  stage: deploy
70  image: docker/compose:latest
71  before_script:
72    - apk add --no-cache openssh-client
73    - eval $(ssh-agent -s)
74    - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
75  script:
76    - ssh $PROD_USER@$PROD_SERVER_IP "
77        cd /app &&
78        docker-compose pull app &&
79        docker-compose up -d --no-deps app
80      "
81  environment:
82    name: production
83    url: https://example.com
84  when: manual
85  only:
86    - main

GitHub Actions 範例

 1# .github/workflows/docker-build.yml
 2name: Docker Build and Push
 3
 4on:
 5  push:
 6    branches: [main, develop]
 7  pull_request:
 8    branches: [main]
 9
10env:
11  REGISTRY: ghcr.io
12  IMAGE_NAME: ${{ github.repository }}
13
14jobs:
15  test:
16    runs-on: ubuntu-latest
17    steps:
18      - uses: actions/checkout@v3
19
20      - name: Setup Node.js
21        uses: actions/setup-node@v3
22        with:
23          node-version: '18'
24          cache: 'npm'
25
26      - name: Install dependencies
27        run: npm ci
28
29      - name: Run tests
30        run: npm test
31
32      - name: Run linter
33        run: npm run lint
34
35  build-and-push:
36    needs: test
37    runs-on: ubuntu-latest
38    permissions:
39      contents: read
40      packages: write
41
42    steps:
43      - uses: actions/checkout@v3
44
45      - name: Set up Docker Buildx
46        uses: docker/setup-buildx-action@v2
47
48      - name: Log in to Container Registry
49        uses: docker/login-action@v2
50        with:
51          registry: ${{ env.REGISTRY }}
52          username: ${{ github.actor }}
53          password: ${{ secrets.GITHUB_TOKEN }}
54
55      - name: Extract metadata
56        id: meta
57        uses: docker/metadata-action@v4
58        with:
59          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
60          tags: |
61            type=ref,event=branch
62            type=ref,event=pr
63            type=semver,pattern={{version}}
64            type=semver,pattern={{major}}.{{minor}}
65            type=sha,prefix={{branch}}-            
66
67      - name: Build and push
68        uses: docker/build-push-action@v4
69        with:
70          context: .
71          push: true
72          tags: ${{ steps.meta.outputs.tags }}
73          labels: ${{ steps.meta.outputs.labels }}
74          cache-from: type=gha
75          cache-to: type=gha,mode=max
76
77  deploy:
78    needs: build-and-push
79    runs-on: ubuntu-latest
80    if: github.ref == 'refs/heads/main'
81
82    steps:
83      - name: Deploy to production
84        uses: appleboy/ssh-action@master
85        with:
86          host: ${{ secrets.PROD_HOST }}
87          username: ${{ secrets.PROD_USER }}
88          key: ${{ secrets.SSH_PRIVATE_KEY }}
89          script: |
90            cd /app
91            docker-compose pull
92            docker-compose up -d --no-deps app
93            docker system prune -f            

📊 總結與最佳實踐清單

核心知識回顧

本系列文章涵蓋了 Docker 從入門到實戰的完整內容:

第一篇:基礎概念

  • Docker 架構與核心元件
  • 容器 vs 虛擬機
  • 安裝與配置

第二篇:指令操作

  • 容器生命週期管理
  • 映像操作技巧
  • 網路與儲存配置

第三篇:進階實踐(本篇)

  • Dockerfile 優化
  • 多階段建立
  • Docker Compose 編排
  • 安全性與效能
  • 生產環境部署

Docker 最佳實踐檢查清單

📝 開發階段

  • 使用 .dockerignore 排除不必要的檔案
  • 利用建立快取優化建立速度
  • 使用 Bind Mount 實現程式碼熱重載
  • 為映像添加明確的標籤版本
  • 使用 Docker Compose 管理多容器應用

🏗️ 映像建立

  • 選擇最小化的基礎映像(Alpine, Distroless)
  • 使用多階段建立減少映像大小
  • 合併 RUN 指令減少層數
  • 在每個 RUN 末尾清理快取和臨時檔案
  • 先複製依賴檔案,再複製原始碼(快取優化)
  • 使用非 root 使用者運行容器
  • 添加健康檢查(HEALTHCHECK)
  • 使用 LABEL 添加元資料

🔐 安全性

  • 定期掃描映像漏洞
  • 不在映像中存儲敏感資訊
  • 使用 Docker Secrets 管理密鑰
  • 限制容器能力(–cap-drop)
  • 使用唯讀檔案系統
  • 設定資源限制(CPU、記憶體)
  • 使用官方或可信的基礎映像
  • 定期更新基礎映像

🌐 網路配置

  • 使用自訂網路替代預設 bridge
  • 為服務設定有意義的網路別名
  • 後端服務使用內部網路(internal)
  • 最小化暴露的埠
  • 使用反向代理(Nginx, Traefik)

💾 資料管理

  • 使用 Volume 而非 Bind Mount 持久化資料
  • 為 Volume 使用有意義的命名
  • 定期備份重要資料
  • 避免在容器內存儲狀態
  • 使用外部儲存服務(S3, NFS)

🚀 生產部署

  • 設定容器重啟策略(restart: unless-stopped)
  • 實作健康檢查和就緒探測
  • 配置日誌輪替避免磁碟填滿
  • 使用滾動更新策略
  • 準備回滾方案
  • 實作監控和告警(Prometheus, Grafana)
  • 設定資源限制和預留
  • 文件化部署流程

📊 監控與維護

  • 收集並分析容器日誌
  • 監控容器資源使用(CPU、記憶體、網路)
  • 設定關鍵指標告警
  • 定期清理未使用的映像和容器
  • 追蹤映像大小變化
  • 測試災難恢復流程

常見錯誤與解決方案

問題原因解決方案
映像太大包含不必要的檔案和工具使用多階段建立、Alpine 映像
建立緩慢沒有優化快取層調整 Dockerfile 指令順序
容器無法啟動權限或依賴問題檢查日誌、使用健康檢查
資料遺失未使用 Volume使用 Volume 持久化資料
網路連接失敗網路配置錯誤檢查網路設定和防火牆
效能不佳資源限制不當調整 CPU、記憶體限制
安全漏洞使用過時映像定期更新和掃描映像

學習資源推薦

官方文件

進階學習

  • Docker 認證考試(DCA)
  • Kubernetes(容器編排)
  • Docker Swarm(集群管理)
  • Helm(Kubernetes 套件管理)

社群與工具

  • Docker Community Forums
  • GitHub Docker 範例專案
  • Play with Docker(線上實驗環境)
  • Portainer(容器管理 UI)

🎉 結語

Docker 容器技術已經成為現代軟體開發和部署的標準工具。透過本系列三篇文章的學習,您已經掌握了:

  1. 基礎知識:理解容器化的概念和 Docker 架構
  2. 實務操作:熟練使用 Docker CLI 管理容器和映像
  3. 進階技能:掌握 Dockerfile 優化、Compose 編排和生產部署

下一步建議

  • 實踐專案:將現有專案容器化
  • 學習編排:深入學習 Kubernetes 或 Docker Swarm
  • 持續優化:關注效能和安全性
  • 社群參與:貢獻開源專案,分享經驗

Docker 的學習是一個持續的過程,隨著實踐經驗的累積,您將能夠構建更高效、更安全、更可靠的容器化應用。

祝您在容器化技術的道路上不斷進步!🚀