JobSet:讓 Kubernetes 真正駕馭多 Job 工作負載

JobSet:讓 Kubernetes 真正駕馭多 Job 工作負載

一句話理解 JobSet: 如果 Kubernetes Job 是「單兵作戰」,JobSet 就是「協同作戰的整支部隊」,一個 API 物件,統一管理多組 Job 的生命週期、網路與故障恢復。

為什麼要用 JobSet?不是已經有 Kubernetes Job API 了嗎?

Kubernetes 原生 Job 的設計假設是:一個 Job = 一個獨立的批次任務。但現實中的分散式工作負載遠比這複雜。

以大型模型訓練為例,你可能同時需要:

  • Parameter Server — 儲存與同步模型權重
  • Worker Node — 並行處理訓練資料
  • Coordinator — 控制訓練流程與 checkpoint

如果用原生 Job 分別建立,你必須自行處理:

  1. 多個 Job 的啟動順序與依賴
  2. 跨 Job 的 Pod 間網路發現
  3. 任一 Job 失敗後的全域重啟邏輯

這就像試圖用三個獨立的 crontab 來編排一場交響樂——技術上可行,但維運成本極高。

JobSet 把這些協調邏輯內建到一個 CRD 裡,讓你只需要描述「我要什麼」,而非「怎麼串起來」。

JobSet Diagram (圖片來源:JobSet Conceptual Diagram)

核心能力一覽

能力 做了什麼 為什麼重要
ReplicatedJob 分組 在同一個 JobSet 裡定義多組 Job,各自擁有獨立的 Pod Template 與副本數 一個 YAML 即可描述多個相關聯的任務而不是拆成個別的 Job 管理
協調式生命週期 所有 Job 共同啟動,JobSet 追蹤整體成功/失敗 避免「半成品」狀態:不會出現 worker 跑完但 PS 還沒啟動的窘境
自動建立 Headless Service 為每個 ReplicatedJob 建立 headless Service,Pod 可透過可預測的 DNS 互相發現 PyTorch torchrun、TensorFlow MultiWorkerMirroredStrategy 等框架可直接使用
靈活的失敗策略 支援 FailJobSet 屬性(任一 Job 失敗即全域重啟)或選擇性容忍個別失敗 在「訓練一致性」與「容錯彈性」之間自由取捨

典型使用場景

🧠 分散式 ML 訓練(最熱門場景)

大規模模型訓練——無論是 Parameter Server 架構還是 Data-Parallel ——都需要多個角色的 Pod 同時運行並互相通訊。JobSet 天然支援這種多角色拓撲,搭配 Kueue 可進一步實現 GPU/TPU 資源的排隊與公平調度。

🔬 HPC 與科學計算

MPI 應用需要所有 rank 在可預測的網路位址上同時啟動。JobSet 的 headless Service + coordinated startup 正好解決這個痛點。

🔄 多階段資料處理

ETL pipeline 中「抽取 → 轉換 → 載入」的不同 worker 群組可以封裝在同一個 JobSet 內,共享生命週期管理。

實際範例:大規模日誌分析系統

讓我們用一個實際範例來理解為什麼需要 Leader-Worker 架構。以 Apache Spark 分散式資料處理為例,假設你需要處理每天產生的 TB 級網站存取日誌,進行即時異常偵測與統計分析。這個工作負載天然需要 Leader-Worker 架構:

為什麼需要 Leader(Driver)?

  • 任務分配與協調:Driver 負責將大型資料集切分成小塊,分配給不同的 Worker 處理
  • 狀態追蹤:監控哪些資料塊已處理完成、哪些失敗需要重試
  • 結果彙總:收集各 Worker 的處理結果,進行最終的聚合計算(如統計 95th percentile 回應時間)
  • 資源管理:動態調整 Worker 數量,處理反壓(backpressure)情況

為什麼需要多個 Worker?

  • 平行處理:100 個 Worker 同時處理不同的資料分片,大幅縮短處理時間
  • 資源隔離:每個 Worker 處理自己的資料子集,避免記憶體競爭
  • 容錯能力:單一 Worker 失敗不影響其他 Worker,Leader 可重新分配該任務

架構示意

┌─────────────────────────────────────────┐
│  Leader (Spark Driver)                  │
│  - 讀取任務設定                           │
│  - 切分資料成 1000 個分片                 │
│  - 分配給 Worker 處理                     │
│  - 追蹤進度:652/1000 完成                │
└──────────────┬──────────────────────────┘
               │
    ┌──────────┼──────────┬──────────┐
    │          │          │          │
┌───▼───┐  ┌──▼────┐  ┌──▼────┐  ┌──▼────┐
│Worker1│  │Worker2│  │Worker3│  │Worker4│
│處理    │  │處理    │  │處理    │  │處理    │
│分片1-25│  │分片26  │  │分片51  │  │分片76  │
│       │  │  -50  │  │  -75  │  │ -100  │
└───────┘  └───────┘  └───────┘  └───────┘

真實世界的挑戰

  1. 網路相依性:Worker 需要知道 Leader 的位址才能回報進度(LEADER_HOST 環境變數)
  2. 啟動順序:Leader 必須先啟動並準備好接收連線,Worker 才能開始工作
  3. 失敗處理:如果 Leader 掛了,整個任務都要重來;如果只是 Worker 掛了,Leader 可以重新分配該部分工作
  4. 資源爭奪:在 Kubernetes 中,如何確保 Leader 和 Worker 的 Pod 能同時取得足夠資源?

這就是 JobSet 的價值所在:

原生 Job 無法表達「Leader 必須先啟動,且 Worker 需要透過穩定的 DNS 名稱找到它」這種拓撲關係。你需要自己寫 init container、readiness probe、手動建立 Service——這些都是重複且容易出錯的工作。

JobSet 透過 replicatedJobs + 自動 headless Service,讓你用 20 行 YAML 就能描述這個架構,而非 200 行的手動配置。

其他常見的 Leader-Worker 使用場景

  • 機器學習訓練:Parameter Server (Leader) 儲存模型參數,Worker 計算梯度並回傳更新
  • 爬蟲系統:Coordinator (Leader) 管理待爬取 URL 佇列,Worker 執行實際的網頁下載與解析
  • 影片轉檔:Orchestrator (Leader) 切分影片片段,Worker 平行處理不同時間段的編碼工作
  • 基因定序分析:Master (Leader) 協調基因序列比對任務,Worker 執行計算密集的比對演算法

在 Kubernetes 中運行的 Leader-Worker 架構

如果今天使用 Job,你需要手動管理 Service、協調啟動順序,並且在任一 Job 失敗時自行處理清理與重啟邏輯:

apiVersion: batch/v1
kind: Job
metadata:
  name: worker-job
spec:
  template:
    spec:
      containers:
      - name: worker
        image: bash:latest
        command: ["bash", "-xc", "sleep 1000"]
        env:
        - name: LEADER_HOST
          value: "leader-service"
      restartPolicy: Never
---
apiVersion: batch/v1
kind: Job
metadata:
  name: leader-job
spec:
  template:
    spec:
      containers:
      - name: leader
        image: bash:latest
        command: ["bash", "-xc", "echo 'Leader is running'; sleep 1000"]
      restartPolicy: Never
---
apiVersion: v1
kind: Service
metadata:
  name: leader-service
spec:
  selector:
    job-name: leader-job
  ports:
  - port: 8080

如果使用 JobSet:不僅能一次定義 Leader 與 Worker,還能透過 failurePolicy 針對特定角色進行錯誤處理(例如:Leader 失敗時,直接 FailJobSet 中止任務)。

apiVersion: jobset.x-k8s.io/v1alpha2
kind: JobSet
metadata:
  name: failjobset-action-example
spec:
  failurePolicy:
    maxRestarts: 3
    rules:
    # 當 leader job 失敗時,JobSet 會立刻標記為失敗
    - action: FailJobSet
      targetReplicatedJobs:
      - leader
  replicatedJobs:
  - name: leader
    replicas: 1
    template:
      spec:
        # 設為 0 確保如果任何 pod 失敗,job 立即失敗
        backoffLimit: 0
        completions: 2
        parallelism: 2
        template:
          spec:
            containers:
            - name: leader
              image: bash:latest
              command:
              - bash
              - -xc
              - |
                echo "JOB_COMPLETION_INDEX=$JOB_COMPLETION_INDEX"
                if [[ "$JOB_COMPLETION_INDEX" == "0" ]]; then
                  for i in $(seq 10 -1 1)
                  do
                    echo "Sleeping in $i"
                    sleep 1
                  done
                  exit 1
                fi
                for i in $(seq 1 1000)
                do
                  echo "$i"
                  sleep 1
                done
  - name: workers
    replicas: 1
    template:
      spec:
        backoffLimit: 0
        completions: 2
        parallelism: 2
        template:
          spec:
            containers:
            - name: worker
              image: bash:latest
              command:
              - bash
              - -xc
              - |
                sleep 1000

🔍DNS 發現範例: Worker Pod 可透過 leader-0.failjobset-action-example.default.svc.cluster.local 直接連線到 Leader,無需額外的 Service 定義。

綜合對照表:原生 Job vs JobSet

比較維度 / 工作流程 原生 Job JobSet
1. 角色拓撲定義 單一角色;需為 Leader 和 Worker 寫獨立 YAML 並手動管理 支援多角色;單一 Manifest 內定義多組角色與副本
2. 網路服務與發現 ❌ 手動建立 Service 與 DNS,需自行注入環境變數 自動建立 Headless Service;透過穩定 DNS 直接連線
3. 啟動順序協調 需寫 init container/readiness probe 確保 Leader 先啟動 內建協調啟動;自動處理多角色的依賴關係
4. 失敗處理策略 ❌ 各 Job 獨立失敗;需自行寫腳本偵測並重啟關聯 Job 全域/針對性 FailurePolicy;支援 Leader 失敗全域重啟
5. 監控與清理 需分別查看多個 Job;刪除時需逐一清除 Job/Service 單一物件管理;一鍵查看與自動回收所有關聯資源
6. 開發與維護成本 高(維護多個 YAML 與關聯腳本,難以滿足複雜需求) 極低(純 YAML 宣告式定義)
7. 社群生態支援 Kubernetes 核心 SIG Scheduling 官方子專案(能整合 Kueue 進行更進階的 Pod 調度)

開始使用 JobSet

0. 安裝前置條件 (Prerequisites)

  • 一個運行中且版本為最近三個次要版本之一的 Kubernetes 叢集。
  • 資源要求:叢集中至少需要一個節點擁有 2+ CPU 與 512+ MB 記憶體,供 JobSet Controller Manager 運行(在某些雲端環境下,預設節點類型可能資源不足,請特別留意)。
  • 已安裝並配置好連線的 kubectl,或選擇使用 helm

1. 安裝 JobSet CRD 與 Controller

你可以根據需求選擇使用 kubectl 或 Helm 來安裝,你可以選擇指定固定版本(例如 v0.10.1)以確保生產環境的穩定性:

方法 A:使用 kubectl

VERSION=v0.10.1
kubectl apply --server-side -f https://github.com/kubernetes-sigs/jobset/releases/download/$VERSION/manifests.yaml

方法 B:使用 Helm

VERSION=v0.10.1
helm install jobset oci://registry.k8s.io/jobset/charts/jobset \
  --version $VERSION \
  --create-namespace \
  --namespace=jobset-system

2. 撰寫 JobSet Manifest

根據你的需求撰寫 YAML,定義你的 ReplicatedJob 群組、副本數、以及對應的 Pod Template(如上方的 Leader-Worker 架構範例)。

以下為一個簡易的 jobset.yaml 範本:

apiVersion: jobset.x-k8s.io/v1alpha2
kind: JobSet
metadata:
  name: coordinator-example
spec:
  # label and annotate jobs and pods with stable network endpoint of the designated
  # coordinator pod:
  # jobset.sigs.k8s.io/coordinator=coordinator-example-driver-0-0.coordinator-example
  coordinator:
    replicatedJob: driver
    jobIndex: 0
    podIndex: 0
  replicatedJobs:
  - name: workers
    template:
      spec:
        parallelism: 4
        completions: 4
        backoffLimit: 0
        template:
          spec:
            containers:
            - name: sleep
              image: busybox
              command: 
                - sleep
              args:
                - 100s
  - name: driver
    template:
      spec:
        parallelism: 1
        completions: 1
        backoffLimit: 0
        template:
          spec:
            containers:
            - name: sleep
              image: busybox
              command: 
                - sleep
              args:
                - 100s

3. 部署到叢集

kubectl apply -f jobset.yaml

4. 監控與除錯

一旦部署,你可以使用以下指令來追蹤任務進度:

  • 查看 JobSet 整體狀態kubectl get jobsets
  • 查看特定 JobSet 的詳細事件與狀態kubectl describe jobset <name>
  • 觀察底層建立出來的 Jobskubectl get jobs -l jobset.sigs.k8s.io/jobset-name=<name>

總結

JobSet 的設計,標誌著 Kubernetes 從「無狀態微服務編排」正式跨入「複雜分散式拓撲」的成熟階段。它並非取代原生 Job,而是精準補足了多節點、異質角色(如 Leader-Worker、Parameter Server)協同運作的拼圖。

透過建立這一層宣告式的協調層,工程團隊終於能從繁瑣的啟動順序、DNS 發現與錯誤重試腳本中解放。特別是在搭配 Kueue 進行 GPU/TPU 等昂貴運算資源的進階調度時,JobSet 讓你只需專注於描述「要什麼」,而非痛苦地處理「怎麼串」——這才是 Kubernetes 真正該有的優雅模樣。

如果你正在 Kubernetes 上建構下一代 AI 訓練平台或大規模資料處理管線,JobSet 絕對是不可或缺的核心基礎架構。

Eason Cao
Eason Cao Eason is an engineer working at FANNG and living in Europe. He was accredited as AWS Professional Solution Architect, AWS Professional DevOps Engineer and CNCF Certified Kubernetes Administrator. He started his Kubernetes journey in 2017 and enjoys solving real-world business problems.
comments powered by Disqus