結論先講

不要在本地跑所有微服務。 你正在改的功能只涉及 Order Service 和 Payment Service,那就只跑這兩個,其他的用 mock 或連到共用的 dev 環境。Docker Compose profile 可以輕鬆做到「選擇性啟動」。如果你的團隊已經上 K8s,Tilt 或 Skaffold 可以在本地 K8s cluster 跑,hot reload 不用重 build image。


本地全跑的慘況

docker compose up(全部啟動):

  order-service        256MB
  payment-service      256MB
  user-service         256MB
  notification-service 256MB
  search-service       512MB
  PostgreSQL           512MB
  Redis                128MB
  Kafka + Zookeeper    1.5GB
  Elasticsearch        2GB
  ─────────────────────────
  總計                  ~5.7GB

加上你的 IDE(VS Code + TypeScript LSP):1-2GB
加上 Chrome(20 個分頁):2-3GB
加上 OS:2GB
─────────────────────────
總計:12-13GB → 16GB 的筆電開始 swap → 卡到想哭

解法一:Docker Compose Profile(選擇性啟動)

如同 第 42 篇 介紹的 Docker Compose,profiles 可以分組服務:

# docker-compose.yml
services:
  # 核心服務——永遠啟動
  postgres:
    image: postgres:16-alpine
    profiles: ["core"]
    ports: ["5432:5432"]
 
  redis:
    image: redis:7-alpine
    profiles: ["core"]
    ports: ["6379:6379"]
 
  # 按功能分組
  order-service:
    build: ./services/order
    profiles: ["core", "order"]
    depends_on: [postgres, redis]
 
  payment-service:
    build: ./services/payment
    profiles: ["core", "payment"]
    depends_on: [postgres]
 
  # 重量級服務——需要時才啟動
  kafka:
    image: confluentinc/cp-kafka:7.6.0
    profiles: ["event"]
 
  elasticsearch:
    image: elasticsearch:8.13.0
    profiles: ["search"]
 
  search-service:
    build: ./services/search
    profiles: ["search"]
    depends_on: [elasticsearch]
# 只跑 Order + Payment(改訂單功能時)
docker compose --profile core --profile order --profile payment up
 
# 需要搜尋功能時才加
docker compose --profile search up
 
# RAM 使用:core + order + payment ≈ 1.2GB(省了 4.5GB)

解法二:Mock 不需要的服務

你在改 Order Service 的下單邏輯,需要呼叫 Payment Service。但你不需要「真的」跑 Payment Service:

// mock-payment-service.js(50 行搞定)
const express = require('express');
const app = express();
app.use(express.json());
 
// 模擬付款成功
app.post('/payments', (req, res) => {
  console.log(`[Mock] Payment created: ${JSON.stringify(req.body)}`);
  res.json({
    id: `pay_${Date.now()}`,
    status: 'succeeded',
    amount: req.body.amount,
  });
});
 
// 模擬付款查詢
app.get('/payments/:id', (req, res) => {
  res.json({
    id: req.params.id,
    status: 'succeeded',
  });
});
 
app.listen(3002, () => console.log('Mock Payment Service on :3002'));

RAM 使用:30MB(vs 真正的 Payment Service 256MB)。

進階:用 WireMock 做 Mock Server

# docker-compose.override.yml(本地開發用)
services:
  payment-mock:
    image: wiremock/wiremock:3.5.2
    volumes:
      - ./mocks/payment:/home/wiremock/mappings
    ports: ["3002:8080"]
// mocks/payment/create-payment.json
{
  "request": {
    "method": "POST",
    "url": "/payments"
  },
  "response": {
    "status": 200,
    "jsonBody": {
      "id": "pay_mock_123",
      "status": "succeeded"
    }
  }
}

解法三:連到共用 Dev 環境

本地只跑你正在改的服務,其他服務連到 team 的 dev 環境:

本地:
  order-service(你在改的)→ 連到 dev 環境的 payment-service

Dev 環境(K8s cluster):
  payment-service
  user-service
  notification-service
  kafka, postgres, redis, ES
# 設定環境變數,指向 dev 環境
PAYMENT_SERVICE_URL=https://dev.internal.mycompany.com/payment
USER_SERVICE_URL=https://dev.internal.mycompany.com/user

注意:dev 環境的資料可能跟你本地不一致,debug 時要小心。


解法四:Tilt / Skaffold(本地 K8s 開發)

如果你的 production 跑 K8s,本地用 minikube + Tilt 可以模擬真實環境:

Tilt

# Tiltfile
# 只跑你需要的服務
docker_compose('./docker-compose.yml')
 
# Hot reload:改了 Go 程式碼 → 自動重新 build + deploy
local_resource(
  'order-service',
  serve_cmd='cd services/order && go run .',
  deps=['services/order/'],
  resource_deps=['postgres'],
)
 
# 其他服務用 mock
local_resource(
  'payment-mock',
  serve_cmd='node mocks/payment-mock.js',
)

Tilt 的 dashboard 讓你一眼看到所有服務的狀態、log、build 進度。

Skaffold

# skaffold.yaml
apiVersion: skaffold/v4beta10
kind: Config
build:
  artifacts:
    - image: order-service
      context: services/order
      docker:
        dockerfile: Dockerfile.dev
      sync:
        manual:
          - src: '**/*.go'
            dest: /app
deploy:
  kubectl:
    manifests:
      - k8s/dev/*.yaml
skaffold dev  # 監聽檔案變更 → 自動 build + deploy 到本地 K8s

Hot Reload:改一行不要等 2 分鐘

最糟糕的開發體驗:
  改一行程式碼
  → docker build(2 分鐘)
  → docker compose restart(30 秒)
  → 測試
  → 發現寫錯 → 再來一次

目標:
  改一行程式碼 → 1-3 秒內生效

Docker Compose 的 Hot Reload

services:
  order-service:
    build:
      context: ./services/order
      dockerfile: Dockerfile.dev   # 用 dev 版 Dockerfile
    volumes:
      - ./services/order/src:/app/src   # 掛載原始碼
    command: npm run dev    # nodemon / ts-node-dev
# Dockerfile.dev(不做 COPY,靠 volume mount)
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
# 不 COPY src——靠 volume mount
CMD ["npx", "nodemon", "src/index.ts"]

我的建議:開發環境分層

Level 1 — 純本地(最快、最省 RAM):
  只跑你在改的 1-2 個服務
  其他全用 mock
  DB 用 SQLite 或本地 PostgreSQL
  RAM:1-2GB

Level 2 — 本地 + Dev 環境(平衡):
  你在改的服務跑本地
  其他服務連到 team 的 dev K8s cluster
  RAM:1-2GB 本地 + 共用 dev 環境

Level 3 — 全本地 Docker Compose(最真實):
  所有服務都跑本地
  用 profile 控制啟動哪些
  偶爾需要 end-to-end 測試時才用
  RAM:6-8GB

Level 4 — Remote Dev Environment(最奢侈):
  GitHub Codespaces / Gitpod / 自建 dev VM
  在雲端跑所有服務
  本地只跑 VS Code Remote
  RAM:本地幾乎不佔

下一篇

Debug 跨服務問題 — 服務跑起來了,但用戶回報 bug:「下單失敗」。5 個服務、50 個 container、100 萬行 log——從哪裡開始查?


本系列文章

完整 68 篇目錄見 系列首頁

← 上一篇:微服務安全(二):API Gateway 的安全功能 → 下一篇:開發者體驗(二):Debug 跨服務問題的 SOP