🐳 微服務容器化:Docker 與 Docker Compose
📌 每個微服務一個容器
容器化是微服務的最佳搭檔:每個服務打包成獨立的 Docker Image,確保開發、測試、生產環境一致。
微服務容器化架構:
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Product │ │ Order │ │ Payment │
│ Service │ │ Service │ │ Service │
│ :5002 │ │ :5003 │ │ :5004 │
└────┬────┘ └────┬────┘ └────┬────┘
│ │ │
└───────────┼───────────┘
│
┌──────┴──────┐
│ API Gateway │
│ :5000 │
└─────────────┘
📌 Dockerfile 撰寫:多階段建置
# ── ProductService/Dockerfile ──
# 階段 1:建置 (Build)
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
# 先複製 csproj 並還原套件(利用 Docker 快取層)
COPY ["ProductService.csproj", "."]
RUN dotnet restore
# 複製所有程式碼並建置
COPY . .
RUN dotnet publish -c Release -o /app/publish --no-restore
# 階段 2:執行 (Runtime) — 用小型映像檔
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime
WORKDIR /app
# 建立非 root 用戶(安全性)
RUN adduser --disabled-password --gecos "" appuser
USER appuser
COPY --from=build /app/publish .
# 健康檢查
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1
EXPOSE 8080
ENV ASPNETCORE_URLS=http://+:8080
ENTRYPOINT ["dotnet", "ProductService.dll"]
.dockerignore
# .dockerignore — 不要把不必要的檔案複製進容器
**/bin/
**/obj/
**/.vs/
**/.git
**/node_modules/
**/*.user
**/*.dbmdl
**/Dockerfile*
**/docker-compose*
📌 Docker Compose 編排多個服務
# docker-compose.yml
version: '3.8'
services:
# ── API Gateway ──
api-gateway:
build:
context: ./ApiGateway
dockerfile: Dockerfile
ports:
- "5000:8080"
depends_on:
product-service:
condition: service_healthy
order-service:
condition: service_healthy
environment:
- ASPNETCORE_ENVIRONMENT=Development
networks:
- eshop-network
# ── Product Service ──
product-service:
build:
context: ./ProductService
dockerfile: Dockerfile
environment:
- ConnectionStrings__DefaultConnection=Host=product-db;Database=ProductDb;Username=postgres;Password=postgres123
- ASPNETCORE_ENVIRONMENT=Development
depends_on:
product-db:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 10s
timeout: 5s
retries: 3
start_period: 15s
networks:
- eshop-network
# ── Order Service ──
order-service:
build:
context: ./OrderService
dockerfile: Dockerfile
environment:
- ConnectionStrings__DefaultConnection=Host=order-db;Database=OrderDb;Username=postgres;Password=postgres123
- RabbitMq__Host=rabbitmq
- ASPNETCORE_ENVIRONMENT=Development
depends_on:
order-db:
condition: service_healthy
rabbitmq:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 10s
timeout: 5s
retries: 3
networks:
- eshop-network
# ── Payment Service ──
payment-service:
build:
context: ./PaymentService
dockerfile: Dockerfile
environment:
- ConnectionStrings__DefaultConnection=Host=payment-db;Database=PaymentDb;Username=postgres;Password=postgres123
- RabbitMq__Host=rabbitmq
depends_on:
- payment-db
- rabbitmq
networks:
- eshop-network
# ── 資料庫 ──
product-db:
image: postgres:16-alpine
environment:
POSTGRES_DB: ProductDb
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres123
volumes:
- product-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 3s
retries: 5
networks:
- eshop-network
order-db:
image: postgres:16-alpine
environment:
POSTGRES_DB: OrderDb
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres123
volumes:
- order-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 3s
retries: 5
networks:
- eshop-network
payment-db:
image: postgres:16-alpine
environment:
POSTGRES_DB: PaymentDb
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres123
volumes:
- payment-data:/var/lib/postgresql/data
networks:
- eshop-network
# ── 基礎設施 ──
rabbitmq:
image: rabbitmq:3-management-alpine
ports:
- "15672:15672" # 管理介面
environment:
RABBITMQ_DEFAULT_USER: guest
RABBITMQ_DEFAULT_PASS: guest
healthcheck:
test: ["CMD", "rabbitmq-diagnostics", "check_port_connectivity"]
interval: 10s
timeout: 5s
retries: 5
networks:
- eshop-network
redis:
image: redis:7-alpine
ports:
- "6379:6379"
networks:
- eshop-network
volumes:
product-data:
order-data:
payment-data:
networks:
eshop-network:
driver: bridge
📌 常用 Docker Compose 指令
# 啟動所有服務(背景執行)
docker compose up -d
# 查看所有服務的狀態
docker compose ps
# 查看特定服務的日誌
docker compose logs -f product-service
# 只重建並重啟某個服務
docker compose up -d --build product-service
# 停止並移除所有容器
docker compose down
# 停止並移除所有容器 + 資料卷(清除資料庫)
docker compose down -v
# 擴展某個服務的實例數量
docker compose up -d --scale product-service=3
📌 環境變數管理
# docker-compose.override.yml(開發環境覆蓋設定)
services:
product-service:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- Logging__LogLevel__Default=Debug
ports:
- "5002:8080" # 開發時暴露埠口方便偵錯
// Program.cs — 在程式碼中讀取環境變數
var connectionString = builder.Configuration
.GetConnectionString("DefaultConnection")
?? throw new InvalidOperationException("Missing connection string");
var rabbitHost = builder.Configuration["RabbitMq:Host"] ?? "localhost";
📌 服務間網路通訊
# 在 docker-compose 中,服務名稱就是 DNS 名稱
# product-service 可以透過 http://order-service:8080 呼叫 order-service
# 不需要知道實際的 IP 地址
# 自訂網路讓服務隔離
networks:
eshop-network:
driver: bridge
monitoring-network:
driver: bridge
下一章: 我們將學習微服務的韌性模式,用 Polly 處理網路故障和服務不可用的狀況。