Skip to content

第二十章:Docker 容器化與部署 (Containerization & Deployment)

20.1 引言:為什麼需要容器化?

容器化解決了 "在我的機器上可以跑" 的經典問題。

  • 一致性:開發、測試、生產環境完全一致。
  • 隔離性:每個應用程式有自己的依賴,互不干擾。
  • 可移植性:可以在任何支援 Docker 的平台上執行。
  • 擴展性:輕鬆水平擴展 (Scale Out)。

20.2 Dockerfile 最佳實踐

1. 多階段建置 (Multi-Stage Build)

減少最終映像大小,提升安全性。

dockerfile
# Stage 1: Build
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src

# 複製專案檔案並還原 (利用 Docker 快取層)
COPY ["src/BookStore.Web/BookStore.Web.csproj", "src/BookStore.Web/"]
COPY ["src/BookStore.Application/BookStore.Application.csproj", "src/BookStore.Application/"]
COPY ["src/BookStore.Domain/BookStore.Domain.csproj", "src/BookStore.Domain/"]
COPY ["src/BookStore.EntityFrameworkCore/BookStore.EntityFrameworkCore.csproj", "src/BookStore.EntityFrameworkCore/"]

RUN dotnet restore "src/BookStore.Web/BookStore.Web.csproj"

# 複製所有原始碼並建置
COPY . .
WORKDIR "/src/src/BookStore.Web"
RUN dotnet build "BookStore.Web.csproj" -c Release -o /app/build

# Stage 2: Publish
FROM build AS publish
RUN dotnet publish "BookStore.Web.csproj" -c Release -o /app/publish /p:UseAppHost=false

# Stage 3: Runtime
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS final
WORKDIR /app

# 建立非 root 使用者 (安全性最佳實踐)
RUN adduser --disabled-password --gecos '' appuser && chown -R appuser /app
USER appuser

EXPOSE 8080
COPY --from=publish /app/publish .

# 健康檢查
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
  CMD curl -f http://localhost:8080/health || exit 1

ENTRYPOINT ["dotnet", "BookStore.Web.dll"]

2. .dockerignore

避免將不必要的檔案複製到映像中。

**/bin/
**/obj/
**/out/
**/.vs/
**/.vscode/
**/*.user
**/.git/
**/node_modules/

20.3 Docker Compose (本地開發)

使用 Docker Compose 編排多個容器 (應用程式 + 資料庫 + Redis)。

yaml
version: "3.8"

services:
  web:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "8080:8080"
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
      - ConnectionStrings__Default=Server=db;Database=BookStore;User Id=sa;Password=YourStrong@Passw0rd;TrustServerCertificate=True
      - Redis__Configuration=redis:6379
    depends_on:
      - db
      - redis
    networks:
      - bookstore-network

  db:
    image: mcr.microsoft.com/mssql/server:2022-latest
    environment:
      - ACCEPT_EULA=Y
      - SA_PASSWORD=YourStrong@Passw0rd
    ports:
      - "1433:1433"
    volumes:
      - sqldata:/var/opt/mssql
    networks:
      - bookstore-network

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    networks:
      - bookstore-network

volumes:
  sqldata:

networks:
  bookstore-network:
    driver: bridge

執行:

bash
docker-compose up -d
docker-compose logs -f web

20.4 Kubernetes 部署

1. Deployment

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: bookstore-web
  namespace: production
spec:
  replicas: 3
  selector:
    matchLabels:
      app: bookstore
      tier: web
  template:
    metadata:
      labels:
        app: bookstore
        tier: web
    spec:
      containers:
        - name: web
          image: myregistry.azurecr.io/bookstore:1.0.0
          ports:
            - containerPort: 8080
          env:
            - name: ASPNETCORE_ENVIRONMENT
              value: "Production"
            - name: ConnectionStrings__Default
              valueFrom:
                secretKeyRef:
                  name: db-secret
                  key: connection-string
          resources:
            requests:
              memory: "256Mi"
              cpu: "250m"
            limits:
              memory: "512Mi"
              cpu: "500m"
          livenessProbe:
            httpGet:
              path: /health/live
              port: 8080
            initialDelaySeconds: 30
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /health/ready
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 5

2. Service

yaml
apiVersion: v1
kind: Service
metadata:
  name: bookstore-web-svc
spec:
  type: LoadBalancer
  selector:
    app: bookstore
    tier: web
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080

3. ConfigMap & Secret

yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  ASPNETCORE_ENVIRONMENT: "Production"

---
apiVersion: v1
kind: Secret
metadata:
  name: db-secret
type: Opaque
stringData:
  connection-string: "Server=sql-server;Database=BookStore;User Id=sa;Password=YourSecurePassword"

部署:

bash
kubectl apply -f k8s/
kubectl get pods -n production
kubectl logs -f deployment/bookstore-web -n production

20.5 Helm Charts

Helm 是 Kubernetes 的套件管理器,讓部署更加靈活。

1. 建立 Chart

bash
helm create bookstore

2. values.yaml

yaml
replicaCount: 3

image:
  repository: myregistry.azurecr.io/bookstore
  tag: "1.0.0"
  pullPolicy: IfNotPresent

service:
  type: LoadBalancer
  port: 80

resources:
  limits:
    cpu: 500m
    memory: 512Mi
  requests:
    cpu: 250m
    memory: 256Mi

autoscaling:
  enabled: true
  minReplicas: 2
  maxReplicas: 10
  targetCPUUtilizationPercentage: 80

database:
  host: "sql-server.database.svc.cluster.local"
  name: "BookStore"

3. 部署

bash
# 安裝
helm install bookstore ./bookstore -f values-production.yaml

# 升級
helm upgrade bookstore ./bookstore --set image.tag=1.1.0

# 回滾
helm rollback bookstore 1

20.6 CI/CD 整合

1. GitHub Actions 範例

yaml
name: Build and Deploy

on:
  push:
    branches: [main]
    tags: ["v*"]

env:
  REGISTRY: myregistry.azurecr.io
  IMAGE_NAME: bookstore

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Log in to Azure Container Registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ secrets.ACR_USERNAME }}
          password: ${{ secrets.ACR_PASSWORD }}

      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=ref,event=branch
            type=semver,pattern={{version}}
            type=sha

      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}

  deploy:
    needs: build-and-push
    runs-on: ubuntu-latest
    if: startsWith(github.ref, 'refs/tags/v')
    steps:
      - uses: azure/k8s-set-context@v3
        with:
          method: kubeconfig
          kubeconfig: ${{ secrets.KUBE_CONFIG }}

      - name: Deploy to Kubernetes
        run: |
          helm upgrade --install bookstore ./helm/bookstore \
            --set image.tag=${{ github.ref_name }} \
            --namespace production

20.7 實戰練習

練習 1:容器化應用

  1. 為您的 ABP 應用程式撰寫 Dockerfile。
  2. 使用 Docker Compose 在本地啟動完整環境 (App + DB + Redis)。
  3. 驗證應用程式正常運作。

練習 2:Kubernetes 部署

  1. 建立 Deployment, Service, ConfigMap, Secret。
  2. 部署到本地 Kubernetes (如 Docker Desktop 或 Minikube)。
  3. 測試 Pod 的自動重啟與負載均衡。

練習 3:CI/CD

  1. 設定 GitHub Actions 自動建置並推送映像。
  2. 實作自動部署到 Kubernetes。

20.8 總結

容器化與 Kubernetes 是現代應用程式部署的標準。

  • Docker 提供了一致的執行環境。
  • Kubernetes 提供了強大的編排能力。
  • Helm 簡化了複雜的部署配置。
  • CI/CD 自動化了整個流程。

掌握這些技術,您就能構建出可靠、可擴展的生產級系統。


參考資源

Released under the MIT License.