經過前面 20 天的修煉,我們已經掌握了 Kubernetes 的各種武器。今天,是時候建造我們自己的「微服務王國」了!就像建造一座城市需要先規劃基礎設施一樣,我們要為接下來的可觀測性和混沌工程實驗,建立一個穩固的後端服務架構。
今天來建立一個基本的後端服務架構,分別有order-service與user-service以及一台資料庫。
這將作為我們接著幾天的實驗場。

simple-ecommerce/
.
├── database
│   ├── devspace.yaml
│   └── postgres.yaml
├── deployments
│   ├── deploy-namespace.sh
│   ├── order-service.yaml
│   └── user-service.yaml
├── deploy-services.sh
├── devspace.yaml
├── go.mod
├── go.sum
├── kind-config.yaml
├── order-service
│   ├── devspace.yaml
│   ├── Dockerfile
│   └── main.go
├── shared
│   ├── config
│   │   └── config.go
│   └── telemetry
│       └── telemetry.go
└── user-service
  ├── devspace.yaml
  ├── Dockerfile
  └── main.go
因為整個系統有 2 個 API 服務和依賴一個資料庫,我們能活用之前學到的 DevSpace Dependencies:微服務編排的藝術 🎭來編排這三個服務的依賴關係跟啟動順序。
然後每個服務裡面也都有自己的 devspace.yaml 跟對應的 K8s Deployment 、Service 等資源的宣告。根目錄的devspace.yaml 則是用來定義全局相關的設定。
建立 namespace,這次的 pod 就都在這 ecommerce 下操作。
kubectl create namespace ecommerce --dry-run=client -o yaml | kubectl apply -f -
# day21/kind-config.yaml
# kind-config.yaml - 遊樂場的設計圖
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: ecommerce-cluster
nodes:
- role: control-plane  # 🎪 馬戲團團長(管理一切)
- role: worker        # 🎭 表演者1(運行應用)
- role: worker        # 🎭 表演者2(運行應用)
kind create cluster --config=kind-config.yaml
# day21/devspace.yaml
# devspace.yaml - 總指揮官的配置
version: v2beta1
name: ecommerce-microservices
# 🎼 依賴關係編排 - 就像樂團的演奏順序
dependencies:
  database:           # 🥁 鼓手先開始(基礎服務)
    path: ./database
    
  user-service:       # 🎹 鋼琴跟上(核心服務)
    path: ./user-service
    
  order-service:      # 🎻 小提琴最後(業務服務)
    path: ./order-service
# 🎮 開發模式配置 - 讓你像玩遊戲一樣調試
dev:
  user-service:
    labelSelector:
      app: user-service
    ports:
      - port: "8080:8080"    # 🚪 為用戶服務開門
  
  order-service:
    labelSelector:
      app: order-service
    ports:
      - port: "8081:8081"    # 🚪 為訂單服務開門
  
  postgresql:
    labelSelector:
      app: postgresql
    ports:
      - port: "5432:5432"    # 🚪 為資料庫開門
# 🛠️ 便民服務指令 - 就像遙控器的快捷鍵
commands:
  migrate:              # 🔄 一鍵資料庫初始化
    command: |-
      kubectl exec -it deployment/postgresql -- psql -U postgres -d ecommerce -c "
      CREATE TABLE IF NOT EXISTS users (
          id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
          username VARCHAR(50) UNIQUE NOT NULL,
          email VARCHAR(100) UNIQUE NOT NULL,
          created_at TIMESTAMP DEFAULT NOW(),
          updated_at TIMESTAMP DEFAULT NOW()
      );
      -- 更多 SQL..."
  test:                 # 🧪 一鍵系統測試
    command: |-
      echo "🔍 測試用戶服務..."
      curl -X POST http://localhost:8080/users \
        -H "Content-Type: application/json" \
        -d '{"username":"testuser2","email":"test2@example.com"}'
  status:               # 📊 一鍵健康檢查
    command: |-
      echo "=== 🏥 系統健康檢查 ==="
      kubectl get pods -o wide
      curl -s http://localhost:8080/health || echo "❌ User service not ready"
資料庫就像銀行的金庫,需要特別小心地設置:
# day21/database/devspace.yaml
# database/devspace.yaml - 金庫管理員的手冊
version: v2beta1
name: database
deployments:
  postgresql:
    kubectl:
      manifests:
        - ./postgres.yaml   # 📋 金庫建造藍圖
# 🎣 生命週期鉤子 - 像釣魚一樣等待時機
hooks:
  - name: "wait-for-database"
    events: ["after:deploy"]     # 🎪 部署完成後的表演
    command: |
      echo "⏳ 等待金庫就緒..."
      kubectl wait --for=condition=ready pod -l app=postgresql --timeout=300s
      sleep 10  # 😴 讓資料庫喝杯咖啡醒醒腦
      echo "✅ 金庫已就緒!"
  - name: "run-migrations"
    events: ["after:deploy"]
    command: |
      echo "🔧 開始裝修金庫內部..."
      kubectl exec deployment/postgresql -- psql -U postgres -d ecommerce -c "
      -- 🏗️ 建造用戶資料的保險箱
      CREATE TABLE IF NOT EXISTS users (
          id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
          username VARCHAR(50) UNIQUE NOT NULL,
          email VARCHAR(100) UNIQUE NOT NULL,
          created_at TIMESTAMP DEFAULT NOW(),
          updated_at TIMESTAMP DEFAULT NOW()
      );
      
      -- 🏗️ 建造訂單資料的保險箱
      CREATE TABLE IF NOT EXISTS orders (
          id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
          user_id INTEGER REFERENCES users(id),
          product_name VARCHAR(200) NOT NULL,
          quantity INTEGER NOT NULL,
          total_amount DECIMAL(10,2) NOT NULL,
          status VARCHAR(20) DEFAULT 'pending',
          created_at TIMESTAMP DEFAULT NOW(),
          updated_at TIMESTAMP DEFAULT NOW()
      );
      
      -- 🎁 放入一些樣品資料
      INSERT INTO users (username, email) VALUES 
      ('admin', 'admin@example.com'),
      ('testuser', 'test@example.com')
      ON CONFLICT (username) DO NOTHING;
      "
      echo "🎉 金庫裝修完成!"
訂單服務就像餐廳的廚房,負責處理所有的訂單邏輯:
# day21/order-service/devspace.yaml
# order-service/devspace.yaml - 廚房主廚的操作手冊
version: v2beta1
name: order-service
images:
  order-service:
    image: order-service:latest
    dockerfile: ./Dockerfile
    context: ..
deployments:
  order-service:
    kubectl:
      manifests:
        - ../deployments/order-service.yaml
dev:
  order-service:
    imageSelector: order-service:latest
    workingDir: /app
    ports:
      - port: "8081:8081"          # 🚪 廚房的專用通道
    env:
      - name: DATABASE_URL         # 🗺️ 金庫位置
        value: "postgres://postgres:postgres123@postgresql:5432/ecommerce?sslmode=disable"
      - name: USER_SERVICE_URL     # 🗺️ 前台位置
        value: "http://user-service:8080"
      - name: PORT
        value: "8081"
hooks:
  - name: "wait-for-order-service"
    events: ["after:deploy"]
    command: |
      echo "👨🍳 檢查主廚是否準備就緒..."
      kubectl wait --for=condition=ready pod -l app=order-service --timeout=300s
      echo "✅ 主廚已準備就緒!"
用戶服務就像餐廳的前台接待,負責處理所有與客戶相關的事務:
# user-service/devspace.yaml - 前台接待員的工作手冊
version: v2beta1
name: user-service
# 🏭 映像工廠 - 打造服務的容器
images:
  user-service:
    image: user-service:latest
    dockerfile: ./Dockerfile
    context: ..                    # 📂 工廠的原料倉庫
deployments:
  user-service:
    kubectl:
      manifests:
        - ../deployments/user-service.yaml
# 🎮 開發者模式 - 讓你像超級瑪利歐一樣靈活
dev:
  user-service:
    imageSelector: user-service:latest
    workingDir: /app
    ports:
      - port: "8080:8080"          # 🚪 為客戶開啟專屬通道
    env:
      - name: DATABASE_URL         # 🗺️ 告訴服務金庫在哪裡
        value: "postgres://postgres:postgres123@postgresql:5432/ecommerce?sslmode=disable"
      - name: PORT
        value: "8080"
# 🎭 服務就緒檢查 - 像醫生檢查病人一樣仔細
hooks:
  - name: "wait-for-user-service"
    events: ["after:deploy"]
    command: |
      echo "🏥 檢查前台接待員是否就位..."
      kubectl wait --for=condition=ready pod -l app=user-service --timeout=300s
      echo "✅ 前台接待員已就位!"
# 🎮 進入開發模式(熱重載 + 端口轉發)
devspace dev --debug
20:55:50 info Using namespace 'ecommerce'
20:55:50 info Using kube context 'kind-demo'
# 🧪 測試用戶服務
curl -X POST http://localhost:8080/users \
  -H "Content-Type: application/json" \
  -d '{"username":"testuser2","email":"test2@example.com"}'
# 🧪 測試訂單服務
curl -X POST http://localhost:8081/orders \
  -H "Content-Type: application/json" \
  -d '{"user_id":1,"product_name":"Test Product","quantity":2,"total_amount":99.99}'
# 📊 檢查系統狀態
devspace run status
也可以透過 devspace ui 來觸發我們自定義的 command 有個 status 能檢查系統。
今天我們建立的這個微服務架構,就像一座精心設計的實驗室。在接下來的日子裡,我們預計將在這個實驗室中:
🎯 關鍵洞察
微服務不是銀彈,而是一把雙刃劍。 它帶來了靈活性和可擴展性,但也增加了複雜度。DevSpace Dependencies 這樣的工具,就是幫我們駕馭這把雙刃劍的利器。
好的架構不是一蹴而就的,而是在實踐中不斷演進的。 今天的基礎架構,將成為我們探索可觀測性和韌性工程的堅實基石。
PostgreSQL 的  id SERIAL 這是過渡的語法糖,已經聲明以後會淘汰了.
請改用
GENERATED ALWAYS AS IDENTITY
or
GENERATED BY DEFAULT AS IDENTITY
https://www.postgresql.org/docs/current/ddl-identity-columns.html
這語法才是符合ANSI SQL 的方式. serial 是ANSI SQL 還沒制定 IDENTITY 時過渡的語法糖.
...我沒發現(推罪給AI)
來改一下重跑看看
現在許多AI 因為訓練資料的關係,都會使用serial.
另外時間欄位,最好是使用 timestamp with timezone, 有時區資訊, 使用空間與 timestamp 一樣.
確實用 TIMESTAMPTZ 能知道時區資訊
TIMESTAMPT without timezone , 就全看應用程式怎搞了