iT邦幫忙

2025 iThome 鐵人賽

DAY 11
0
生成式 AI

打造 AI 微調平台:從系統設計到 AI 協作的 30 天實戰筆記系列 第 11

[Day 11] K8s 資源調度基礎:PodSpec 與 requests/limits

  • 分享至 

  • xImage
  •  

完整程式碼可在 GitHub 專案中找到:Finetune-30-days-demo / day-11


在前幾天,我們用 Docker Compose 成功整合了 Redis、FastAPI、Worker、Streamlit UI,專案已經能跑通非同步訓練。但隨著服務越來越多,Docker Compose 的限制逐漸浮現:

  1. 資源共享問題

    • 所有容器都跑在同一台機器上,沒有明確的 CPU/記憶體隔離。
    • 某個 worker 吃掉太多資源,可能導致 API 卡住或 Redis 壞掉。
  2. 缺乏調度能力

    • Compose 沒有「排程器」的概念,只能在單機上啟動服務。
    • 一旦要擴展到多台機器(甚至 GPU 叢集),就力不從心。
  3. 難以應付異質工作負載

    • 小任務和大任務的需求差異很大,但在 Compose 下只能「一個 worker 設定打天下」。
    • 沒辦法做到「小任務跑在輕量 worker,大任務跑在重型 worker」。

👉 如果我們希望系統進一步邁向 平台化,就必須引入 Kubernetes (K8s),利用它的調度能力來解決上述問題。


PodSpec 與 requests/limits

K8s 的核心觀念是:每個 Pod 在啟動前,要先聲明自己需要多少資源。這讓排程器能根據需求,把 Pod 安排到合適的節點上。

這裡有兩個關鍵欄位:

  1. requests

    • 表示「保底」需求。
    • 系統保證至少分配這麼多 CPU/記憶體,確保 Pod 能正常運行。
  2. limits

    • 表示「上限」限制。
    • Pod 不能超過這個數值,避免資源被單一服務吃光。

以下是一個範例 PodSpec(以 worker 為例):

apiVersion: v1
kind: Pod
metadata:
  name: worker-small
spec:
  containers:
    - name: worker
      image: myapp/worker:latest
      resources:
        requests:
          cpu: "500m"       # 至少 0.5 CPU
          memory: "512Mi"   # 至少 512MB 記憶體
        limits:
          cpu: "1"          # 最多 1 CPU
          memory: "1Gi"     # 最多 1GB 記憶體

這樣的配置告訴 K8s:

  • Worker 啟動至少需要 0.5 CPU / 512MB RAM
  • 最多只能使用 1 CPU / 1GB RAM,超過就會被限制。

透過這種寫法,我們就能設計出:

  • 小資源 worker:處理輕量、快速任務。
  • 大資源 worker:處理長時間、大模型的訓練。

K8s 實作結構

為了落實資源調度,我把整個專案拆分成一組完整的 K8s manifests,集中放在 k8s/ 目錄下:

k8s/
├── manifests/           # Kubernetes 配置檔案
│   ├── namespace.yaml   # 命名空間定義
│   ├── configmap.yaml   # 環境變數配置(統一管理 Redis、API 等參數)
│   ├── pvc.yaml         # 持久化儲存,保存 results/ 與 DB
│   ├── redis.yaml       # Redis Broker/Backend
│   ├── worker.yaml      # Celery Worker(定義 requests/limits)
│   ├── api.yaml         # FastAPI 服務
│   └── ui.yaml          # Streamlit UI
├── Makefile             # 常用指令,啟動/停止/清理一行完成
└── README.md            # 使用說明文件

這樣一來,從環境變數、儲存、服務到資源限制,都有明確的 YAML 配置,不再需要額外的腳本或手動設定。開發者或團隊只要執行 make deploy,就能在 K8s 上啟動一整套平台。


Day 11 的價值不在於「跑得多快」,而是讓系統第一次擁有了雲原生的樣貌。透過 requests/limits,每個服務都有清楚的資源邊界;透過 manifests 與 Makefile,部署過程高度自動化、可重現。雖然現在只能在 Mac 上模擬 CPU,但邏輯已經完整,未來換成 GPU 叢集,只需要在 YAML 加上 nvidia.com/gpu 的 requests/limits,就能把整個微調平台搬上雲端。Day 11 是專案正式跨入「雲原生微調平台」的第一步。


📎 AI 協作記錄:今日開發指令

請幫我把以下 docker-compose.yml 轉換成 Kubernetes YAML,能在 minikube 上執行:

服務:
1. Redis
   - 使用官方 redis:latest image
   - Port 6379
   - PersistentVolumeClaim 保存資料
   - 健康檢查用 `redis-cli ping`

2. Celery Worker
   - 從本地 Dockerfile build 出來
   - command: python -m celery -A tasks worker --loglevel=info -P solo
   - 共用 PVC:results/
   - env 變數由 ConfigMap 提供(來自 .env)
   - 可 scale up 多個 replicas

3. FastAPI
   - 從本地 Dockerfile build 出來
   - command: python -m uvicorn api:app --host 0.0.0.0 --port 8000 --reload
   - Port 8000
   - 共用 PVC:results/
   - Service 暴露 NodePort,方便本地存取
   - 健康檢查 `GET /docs`

4. Streamlit UI
   - 從本地 Dockerfile build 出來
   - command: python -m streamlit run stepper_ui.py
   - Port 8501
   - 共用 PVC:results/
   - Service 暴露 NodePort,方便本地存取
   - 健康檢查 `/_stcore/health`

其他需求:
- 所有服務放在同一個 namespace: `lora-system`
- ConfigMap 匯入 `.env` 參數(提供 CELERY_BROKER_URL, CELERY_RESULT_BACKEND, API_URL, TZ...)
- PersistentVolumeClaim: `results-pvc`
- 輸出完整的 YAML:namespace.yaml, configmap.yaml, redis.yaml, worker.yaml, api.yaml, ui.yaml
- 驗收方式:使用 `kubectl apply -f .` 後,應能訪問
  - API: http://localhost:8000
  - UI: http://localhost:8501
  - Redis: cluster 內 redis:6379


上一篇
[Day 10] 系統互動解析:從提交任務到完成結果
下一篇
[Day 12] 訓練效能紀錄與分析:四個模型的比較與優化
系列文
打造 AI 微調平台:從系統設計到 AI 協作的 30 天實戰筆記15
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言