# KEP-4671: Gang Scheduling using Workload Object [Kubernetes Enhancement Proposal (KEP) 4671](https://github.com/kubernetes/enhancements/tree/master/keps/sig-scheduling/4671-gang-scheduling)。sig-scheduling 主導(sig-apps 参加)、著者は erictune・wojtek-t・helayoty・44past4・andreyvelich・thockin・macsko・mm4tt の8名(いずれも GitHub ハンドル表記で個別の所属記載なし)。2025-09-17 作成、`status: implementable`。マイルストーンは alpha v1.35 → beta v1.37(当初 v1.36 予定から延期)→ stable v1.38 で、2026-08-19 時点の latest-milestone は v1.37。[KEP-583(coscheduling)](https://github.com/kubernetes/enhancements/tree/master/keps/sig-scheduling/583-coscheduling) と [KEP-5832(PodGroup API 分離)](https://github.com/kubernetes/enhancements/issues/5832) を置き換える(KEP-5832 は2026-03に本KEPへ統合された)。(Source: kep.yaml, Implementation History) kube-scheduler にギャングスケジューリング[^1](全メンバーPodが揃うまでスケジューリング/バインディングの同一段階で待機させ、タイムアウトすれば全ポッドを解放するall-or-nothing方式)のフレームワーク支援を組み込む。理想的なギャングスケジューリングアルゴリズムそのものではなく、ビルディングブロックの提供に焦点を当てる。分散学習・推論のようにPod間の密結合通信(バリア・all-reduce)を要するワークロードは、全Podがほぼ同時に起動しないと高価な計算資源が遊休化するか、アプリケーションレベルの通信タイムアウトで失敗する。 ## 動機:PodGroupをWorkloadから切り離す理由 当初設計は `PodGroup` を `Workload` spec 内に埋め込んでいたが、以下の構造的な問題を生んだ。 - `Workload` は長寿命の設定意図(configuration-intent)、`PodGroup` は一時的なスケジューリング単位(transient scheduling unit)であり、ランタイム実行単位を永続的な定義オブジェクトに結びつけることは関心の分離に反する。 - ライフサイクル結合により、スタンドアロンの `PodGroup` オブジェクトが個別のスケジューリング単位ごとに他リソース(ResourceClaim等)を所有してガベージコレクションすることができない。 - 全 `PodGroup` のランタイムstatusを `Workload` に追跡させると、大規模Workload(PodGroup数が多い)で **etcd 1.5MBオブジェクト上限**に容易に抵触し、単一の `PodGroup` statusを更新するだけで巨大な `Workload` オブジェクト全体への read-modify-write が必要になる**競合(contention)**が生じる。 この分解により、`Workload` はスケジューリング制約・要件を定義するポリシーオブジェクト、`PodGroupTemplate` はランタイム `PodGroup` 生成の設計図、`PodGroup` は独自のライフサイクルを持つスタンドアロンのランタイムオブジェクト(典型的にはコントローラが管理)という役割分担になる。対象ワークロードはビルトインの `Job`([KEP-5547])・`StatefulSet` と、カスタムの `JobSet`・`LeaderWorkerSet`・`MPIJob`・`TrainJob` で、いずれもAI訓練・推論ユースケースで使われる。 Non-Goal として、kube-scheduler への複数ワークロードキュー・公平性の導入(Kueue・Volcano.sh が引き続き担う)、クラスタオートスケーリングとの統合、ワークロードレベルのプリエンプション、異なるスケジューラ間のリソース競合解決は明示的に本KEPのスコープ外とされ、将来KEPへ委ねられる。 ## API設計:Workload・PodGroup・Pod の3層構造 ```yaml apiVersion: scheduling.k8s.io/v1beta1 kind: Workload metadata: namespace: ns-1 name: job-1 spec: podGroupTemplates: - name: "worker" schedulingPolicy: gang: minCount: 100 --- apiVersion: scheduling.k8s.io/v1beta1 kind: PodGroup metadata: name: training-worker-0 spec: workloadRef: workloadName: training-policy templateName: worker schedulingPolicy: gang: minCount: 100 --- apiVersion: v1 kind: Pod spec: schedulingGroup: podGroupName: pg1 ``` `Workload.spec.podGroupTemplates` は最大8個の `PodGroupTemplate`(作成後の追加・削除は不可、既存フィールドの更新は個別に許可)を持ち、各テンプレートは `Basic`(標準スケジューリング)または `Gang`(`minCount` によるall-or-nothing)のいずれか一方の `PodGroupSchedulingPolicy` を選ぶ。`PodGroup.spec.schedulingPolicy` はテンプレートからのコピー/インライン方式(Reference方式ではなく)を採用し、Alpha時点でこれを採用した理由は、Reference方式が持つ「Workload変更が既存の全PodGroupへ波及する action-at-a-distance」を避け、`PodGroup` を自己完結オブジェクトにしてデバッグを容易にするためである。`minCount` はワークロードスケーリングをサポートするため両オブジェクトで可変フィールドとされる一方、`PodGroupTemplates` の追加・削除自体は不変。 Pod は `spec.schedulingGroup.podGroupName`(immutable、`+union` の `PodSchedulingGroup` 型)で直接 `PodGroup` を参照する。ラベルセレクタや `ownerReferences` ではなくPodSpecの明示フィールドとした理由は、PodをPodGroup間で移動させるユースケースが存在せず、可変にすると「ギャングをスケジュール中に1Podだけ除去された」ようなコーナーケースへの対処が必要になるためである。検討された代替案(`PodGroup` 側に `PodGroupSelector` を持たせる方式)は、リプリケートされたギャングで完全なラベルセレクタを使えない(`MatchLabelKeys` 類似の部分指定しかできない)等の理由で却下された。 `PodGroup` のownershipは真のワークロードコントローラ[^6](Job・JobSet・LeaderWorkerSet等)が持ち、`Workload` → `PodGroup` → `Pods` の順に作成する厳格な順序が要求される。非存在の `PodGroup` を参照するPodは `UnschedulableAndUnresolvable` としてマークされ、`PodGroup` 作成時のinformer Addイベントで再エンキューされる。 ```mermaid graph TB TW["Job / JobSet / LWS"] subgraph Objects W[Workload API] end PG[PodGroup] P[Pods] P -.->|ref| PG TW ==>|"1. creates and owns"| W TW ==>|"2. creates and owns"| PG TW ==>|"3. creates and owns"| P PG -.->|ref| W ``` (KEP本文のownership図。真のワークロードコントローラがWorkload・PodGroup・Podsの3つを全て作成・所有し、PodはPodGroupを参照、PodGroupはWorkloadを参照する) `PodGroup.status.conditions` は `PodGroupInitiallyScheduled` を持ち、一度 `True` へ遷移すると終端状態として扱われ `False` へ後退しない(`minCount` 充足後の追加Pod失敗はconditionを退行させない)。deletion保護は専用finalizerで実装され、`schedulingGroup.podGroupName` インデックスで参照Podを効率的に列挙し、全参照Podが終端フェーズ(`Succeeded`/`Failed`)に達してからfinalizerを外す。 ## GangSchedulingプラグイン(Alpha)とWorkload Scheduling Cycle(Beta) **Alpha(v1.35)** は単純なバリア方式で実装された。新設の `GangScheduling` プラグインが `PreEnqueue`(`PodGroup` の存在確認+`minCount` Pod数の充足確認)・`WaitOnPermit`(全Podがpermit段階に達するまで待機)・`EventsToRegister` の3拡張点のみを実装し、要求(1): 「ギャングの全Podがスケジュール可能でなければバインドしない」の充足に焦点を絞る。 **Beta(v1.37、当初v1.36予定から延期)** では、`scheduleOne` ループに新フェーズ **Workload Scheduling Cycle** を導入し、`PodGroup` に属する全Podを個別のpod-by-pod処理ではなく単一クラスタスナップショット上でバッチ処理する。手順は (1) キューから `PodGroup` を取り出す (2) 単一スナップショットを取得 (3) [Opportunistic Batching](https://kep.k8s.io/5598)を利用したアルゴリズムで配置を探索 (4) `schedulableCount >= minCount` なら成功しバインディングへ、未達ならワークロード対応プリエンプション([KEP-5710])を試行し、それでも不可なら標準の失敗処理へ。この判定のため新しい拡張点 `PlacementFeasiblePlugin`(`Wait`/`Unschedulable`/`Success` の3状態を返す)を導入し、当初転用していた `Permit` 拡張点(挙動が段階ごとに一貫しない・高速却下パスが必要)から切り替えた。`Basic` ポリシーのPodGroupではWorkload Scheduling Cycle自体は適用されるが `minCount` チェックはスキップされ(バッチング性能の恩恵のみ享受)、バインディングは常にプリエンプションより優先される(pod-by-pod挙動との互換性維持)。 **アルゴリズムの限界**: 決定論的な処理順序に依存するため、同種(homogeneous)なPodGroupは配置が存在すれば必ず発見できるが、異種(heterogeneous)なPodGroupやPod間アフィニティ/anti-affinity・トポロジ分散を持つPodGroupは配置発見を保証しない。intra-group依存(PodGroupメンバー間のアフィニティ)がある場合は決定論的処理順序ゆえにクラスタ状態に関わらず配置を発見できないことがあり、依存Podに低優先度を割り当てる回避策がドキュメント化される。PodGroup内の全Podは同一 `.spec.schedulerName` を共有しなければならず、不一致は全Pod却下となる。 **キューイング設計**: スケジューリングキューを明示的にworkload-aware化する4つの代替案(0: 現行維持、1: ソートロジック変更、2: `QueuedPodGroupInfo` を既存キュー構造に格納、3: `PodGroup` 専用の独立キュー)を比較検討し、**代替案3(専用PodGroupキュー)** を長期的ベストとして採用した。将来のワークロードレベル優先度・観測性の実装が容易になる一方、`scheduleOne` ループとスケジューリングキューへの非自明なアーキテクチャ変更を伴う。 ## 昇格条件(Graduation Criteria)とフィーチャーゲート - **Alpha(v1.35→v1.36)**: `GenericWorkload` フィーチャーゲート下でWorkload API導入。v1.36でPodGroup APIが `v1alpha2` として分離され、kube-scheduler実装がPodGroup API基準へ切替。 - **Beta**: 「optimal enough」な配置・複数ワークロード同時スケジューリング時のlivelock回避・ワークロード対応プリエンプション実装・PodGroupキューイングアルゴリズム実装・少なくとも1つの真のワークロードコントローラとの統合・PodGroup deletion保護・全e2eテスト・性能回帰テストのCI組み込みを要求。 - **GA**: beta中に判明した全課題の解消、e2e APIテストのconformanceテストへの昇格。 v1.37で、当初分離されていた `GenericWorkload`・`GangScheduling` の2フィーチャーゲートは1つの `GenericWorkload` へ統合され、[KEP-5710]のワークロード対応プリエンプションを制御する `WorkloadAwarePreemption` ゲートも同時に統合された。これにより本KEPと[KEP-5710]のライフサイクルは完全に同期し、両者の卒業基準を同時に満たす必要がある(実装自体は別ファイル・別関心事のため2つのKEPのまま維持)。`scheduling.k8s.io/v1alpha2` はv1.37で `v1alpha3` に完全に置き換えられ、後方互換変換はサポートされない(アルファAPIのため無警告での破壊的変更が許容される)。 アップグレード/ダウングレードは完全加算的(additive)で、フィーチャーゲートを無効化すればPodGroup作成不可・`schedulingGroup` 設定不可・スケジューラはpod-by-pod方式へ復帰するが、既存の `PodGroup`/`schedulingGroup` は削除・クリアされない。監視指標として `scheduler_podgroup_schedule_attempts_total`・`scheduler_podgroup_scheduling_attempt_duration_seconds`・`scheduler_pending_entities{type="podgroup"}` が新設される。 ## 代替案として検討・却下されたAPI設計 初期の `PodGroup` API定義は `GangMode`(`Off`/`Single`/`Replicated`)という列挙型と `SchedulingTimeoutSeconds` フィールドを持っていたが、コミュニティ議論を経て(a) タイムアウトの適切な設定値がユーザーに自明でない (b) Beta以降の有用性が不明、という2理由でAlphaから削除された。`Embedded PodGroups`(status quo維持、市場投入最速だがetcd 1.5MB制限・statusの誤解を招く粒度という欠点)、`Support both embedded and standalone PodGroup`(2種類のトップレベルオブジェクトで責任分担が不明瞭になる)も検討され、いずれも却下されて現行の分離設計に至った。 **Drawbacks**: Volcano.sh・Co-scheduling plugin・Preferred Networks Plugin・Kueue の少なくとも4つの実装がすでにkube-scheduler外でギャングスケジューリングを提供している。それでも本KEPを進める理由は、既存実装が本KEPの目指す各種レース・デッドロック問題の全てには対処できていないこと、AI時代にはこれらの概念が十分基盤的でありユーザーが拡張機能をインストールせずに済むべきという判断である。 ## 出典 - erictune ほか, "KEP-4671: Gang Scheduling using Workload Object", kubernetes/enhancements, `keps/sig-scheduling/4671-gang-scheduling/README.md`, creation-date 2025-09-17, 参照日 2026-08-19。 - 同ディレクトリ `kep.yaml`(著者・SIG・マイルストーン・フィーチャーゲート・メトリクス定義)。 [^1]: Kubernetesコミュニティは「ギャングスケジューリング」を "all-or-nothing scheduling of a set of pods" の意で用い、時分割多重(time-multiplexing)は含意しない([Feitelson and Rudolph](https://doi.org/10.1016/0743-7315(92)90014-E) やSlurmのGang Schedulingとは異なる用法)。 [^6]: 「真のワークロードコントローラ」は Job・JobSet・LeaderWorkerSet 等のin-tree/out-of-treeのコントローラを指す。