☕ NEW! 完成新手任務即可參加抽獎!LINE 星巴克禮券等你拿,名額有限!        🎉 推廣活動:邀請好友註冊 DevLearn,累積推薦抽 LINE 星巴克禮券! 活動詳情 →        🔥 活動期間 2026/4/1 - 5/31 |已有 0 人參加       
Docker 中級

Docker 進階與 Compose

Multi-stage Build(多階段建置)

💡 比喻:搬家 你在舊家(Build 階段)有很多工具、材料, 但搬到新家(Runtime 階段)只帶需要的家具就好。 Multi-stage Build 就是只把「成品」帶到最終的 Image, 不帶開發工具和原始碼,讓 Image 更小更安全。

.NET Multi-stage Dockerfile

# === 階段 1:Build(建置)===
# 使用完整的 SDK Image(包含編譯工具)
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
# 設定工作目錄
WORKDIR /src
# 先複製 csproj 檔案(利用 Docker 快取層)
COPY *.csproj ./
# 還原 NuGet 套件(這層很少變動,可以快取)
RUN dotnet restore
# 複製所有原始碼
COPY . .
# 發佈應用程式(Release 模式)
RUN dotnet publish -c Release -o /app/publish

# === 階段 2:Runtime(執行)===
# 使用精簡的 ASP.NET Runtime Image(沒有 SDK)
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime
# 設定工作目錄
WORKDIR /app
# 只從 build 階段複製發佈結果
COPY --from=build /app/publish .
# 設定容器對外的 port
EXPOSE 8080
# 設定啟動命令
ENTRYPOINT ["dotnet", "MyApp.dll"]

Image 大小比較

方式                       Image 大小
────────────────────────────────────────
直接用 SDK Image           ≈ 800 MB(包含編譯工具)
Multi-stage(aspnet)      ≈ 220 MB(只有 Runtime)
Multi-stage(alpine)      ≈ 110 MB(精簡 Linux)

alpine 版本更小,但可能缺少某些 Linux 套件

Docker Compose

💡 比喻:樂團指揮 你的應用程式像一個樂團:

  • Web App = 主唱
  • Database = 鼓手
  • Redis Cache = 吉他手 Docker Compose 就是指揮,一個手勢讓所有人同時開始演奏。

docker-compose.yml 基本結構

# Docker Compose 設定檔版本
version: '3.8'

# 定義所有服務
services:
  # 服務 1:Web 應用程式
  web:
    # 從當前目錄的 Dockerfile 建置
    build: .
    # port 對應:主機 5000 → 容器 8080
    ports:
      - "5000:8080"
    # 環境變數
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
      - ConnectionStrings__Default=Server=db;Database=myapp;User=sa;Password=YourPassword123!
    # 相依性:web 會等 db 和 redis 啟動後才啟動
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    # 掛載磁碟區(開發時方便修改)
    volumes:
      - ./logs:/app/logs
    # 連接到自訂網路
    networks:
      - app-network
    # 健康檢查
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
      interval: 30s
      timeout: 10s
      retries: 3

  # 服務 2:SQL Server 資料庫
  db:
    # 使用官方 SQL Server Image
    image: mcr.microsoft.com/mssql/server:2022-latest
    environment:
      - ACCEPT_EULA=Y
      - SA_PASSWORD=YourPassword123!
    ports:
      - "1433:1433"
    # 持久化資料(容器刪除後資料還在)
    volumes:
      - sql_data:/var/opt/mssql
    networks:
      - app-network
    # 資料庫健康檢查
    healthcheck:
      test: ["CMD", "/opt/mssql-tools18/bin/sqlcmd", "-S", "localhost", "-U", "sa", "-P", "YourPassword123!", "-Q", "SELECT 1", "-C"]
      interval: 10s
      timeout: 5s
      retries: 5

  # 服務 3:Redis 快取
  redis:
    # 使用官方 Redis Alpine Image(精簡版)
    image: redis:alpine
    ports:
      - "6379:6379"
    networks:
      - app-network

# 定義持久化磁碟區
volumes:
  sql_data:

# 定義網路
networks:
  app-network:
    driver: bridge

常用 Docker Compose 命令

# 建置並啟動所有服務(背景執行)
docker compose up -d --build

# 查看所有服務狀態
docker compose ps

# 查看特定服務的 Log
docker compose logs web

# 即時追蹤 Log(像 tail -f)
docker compose logs -f web

# 停止所有服務
docker compose stop

# 停止並刪除所有容器、網路
docker compose down

# 停止並刪除所有容器、網路、磁碟區(資料也會刪除!)
docker compose down -v

# 只重建並重啟 web 服務
docker compose up -d --build web

# 進入容器內部執行命令
docker compose exec web bash

環境變數管理

.env 檔案

# .env 檔案(不要推上 Git!記得加到 .gitignore)
# 資料庫密碼
SA_PASSWORD=YourPassword123!
# JWT 金鑰
JWT_KEY=your-super-secret-jwt-key
# 應用程式環境
ASPNETCORE_ENVIRONMENT=Development
# docker-compose.yml 中使用 .env 變數
services:
  db:
    environment:
      # 用 ${} 引用 .env 中的變數
      - SA_PASSWORD=${SA_PASSWORD}
  web:
    environment:
      - JWT_KEY=${JWT_KEY}
      - ASPNETCORE_ENVIRONMENT=${ASPNETCORE_ENVIRONMENT}

Health Checks(健康檢查)

// ASP.NET Core 設定健康檢查端點
// Program.cs
// 加入健康檢查服務
builder.Services.AddHealthChecks()
    // 檢查資料庫連線
    .AddSqlServer(
        builder.Configuration.GetConnectionString("Default")!,
        name: "database")
    // 檢查 Redis 連線
    .AddRedis("localhost:6379", name: "redis");

// 設定健康檢查路由
app.MapHealthChecks("/health");

🤔 我這樣寫為什麼會錯?

❌ 錯誤 1:Image 太大(沒用 Multi-stage Build)

# ❌ 錯誤:用 SDK Image 來執行(超大!800MB)
FROM mcr.microsoft.com/dotnet/sdk:8.0
WORKDIR /app
COPY . .
RUN dotnet publish -c Release -o /out
# SDK 包含編譯器、NuGet 快取等,不需要出現在正式環境
ENTRYPOINT ["dotnet", "/out/MyApp.dll"]

# ✅ 正確:用 Multi-stage Build,最終 Image 只有 Runtime
# 參考上面的 Multi-stage Dockerfile 範例
# 最終 Image 只有 ~110-220 MB

❌ 錯誤 2:容器用 root 執行

# ❌ 錯誤:預設以 root 身份執行(安全風險)
FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "MyApp.dll"]

# ✅ 正確:建立非 root 使用者來執行
FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /app
COPY --from=build /app/publish .
# 建立一個叫 appuser 的非 root 使用者
RUN adduser --disabled-password --gecos "" appuser
# 切換到 appuser 身份執行
USER appuser
ENTRYPOINT ["dotnet", "MyApp.dll"]

❌ 錯誤 3:把密碼寫在 docker-compose.yml 中

# ❌ 錯誤:密碼直接寫在設定檔中(推上 Git 就洩漏了)
services:
  db:
    environment:
      - SA_PASSWORD=MyRealPassword123!

# ✅ 正確:用 .env 檔案或 Docker Secrets
# 1. 使用 .env(記得加入 .gitignore)
# 2. 使用 Docker Secrets(適合 Swarm 模式)
# 3. 使用 CI/CD 平台的環境變數功能

💡 重點整理

概念 說明
Multi-stage Build 分階段建置,最終 Image 只包含 Runtime
Docker Compose 一次管理多個容器的工具
depends_on 定義服務啟動順序
volumes 持久化資料,容器刪除後資料還在
networks 容器間的虛擬網路
Health Check 定期檢查服務是否正常運作
.env 環境變數檔案,不要推上 Git

💡 大家的想法 · 0

載入中...
💬 即時聊天室 🟢 0 人在線
😀 😎 🤓 💻 🎮 🎸 🔥
➕ 新問題
📋 我的工單
💬 LINE 社群
🔒
需要註冊才能使用此功能
註冊帳號即可解鎖測驗、遊戲、簽到、筆記下載等所有功能,完全免費!
免費註冊