仮想化通信

日本仮想化技術株式会社の公式エンジニアブログ

KubernetesとKueueでGPUを賢く使う(前編)

このブログでずいぶん前に「KubernetesでGPUをつかう」と言った内容の記事を書いたことがあります。 最近の「KubernetesでGPU」って言うとどんな感じなのかなと思って公式のドキュメントを見たものの、標準の機能としては特に大きく変更は無いようでした。

KubernetesのSchedule GPUsはv1.26で、とうとうstable機能になったようですね。 最初はNVIDIA GPUだけでしたが、AMDやIntelのGPUもサポートしたので、アプリ側がそれらのGPUをサポートしていれば、KubernetesをGPUコンピューティングの基盤として使うのが可能になります。

kubernetes.io

機能としてはあるのですが、この方法でGPUを割り当てる場合は、どこのノードのGPUを使うとか、GPUをX個Podなどに割り当てるくらいの制御しかできません。 そこでもっと良い方法がないかと調べたところ、社内の別のプロジェクトでKueueというものを使っているのがわかりました。

Kueueは何者なのか

KueueはKubernetesでQueueを制御するためのもので、そのため名前の頭がQではなくてKになっています。

  • 一言で言うと Kueue = Kubernetes の Job admission controller(ジョブの入場管理) をするものです。
  • 重要なのは「スケジューラではない」という点みたいです。

ここまでは公式のドキュメントを読んだ上で、ChatGPTなどをつかって調べてもらった結果なのですが、上の説明だけではちょっとよくわかりませんでした。そこで、KubernetesにおけるKueueの役割を図にしてもらいました。

Kubernetes schedulerを代替するコンポーネントなのかなと最初は思ったのですが、そうでは無いようです。 少しだけわかった気になれました。

Kubernetes の役割を分解するとこうなります。
つまりKueueは、「このJobは今スタートしてよいか?」を判断するシステムです。

Job作成 
│
▼
Kueue (このJobを今実行してよいか判断)
│
▼
Kubernetes scheduler (どのノードに置くか)
│
▼ 
Pod起動

もう少し理解が進むために、Kueueがある場合とない場合の違いをまとめてもらいました。

Kubernetesだけの場合

GPUが1枚の環境で2つJobを投げると、 Job1 → Running, Job2 → Pending になります。

PodがPendingのまま溜まる状態です。

$ kubectl get pods

job1   Running
job2   Pending
job3   Pending
job4   Pending

この状態は

  • 見通しが悪い
  • リソース管理が難しい
  • 優先順位制御が難しい

という問題があります。

Kueueが導入されていると

KubernetesにKueueが導入されていると、Kueueが Jobの入場管理をします。

Job1 → admitted → Running, Job2 → waiting, Job3 → waiting

Pod自体がまだ作られません。

$ kubectl get workloads
NAME     STATUS
job1     admitted
job2     pending
job3     pending

Kueue内部フロー(概念図)

Kueueの内部動作を簡略化すると次のような流れになります。

    Job
     │
     ▼
    Workload
     │
     ▼
    LocalQueue
     │
     ▼
    ClusterQueue
     │
     ▼
    ResourceFlavor
     │
     ▼
    Resource Reservation
     │
     ▼
    Pod
     │
     ▼
    Kubernetes Scheduler

Kueueは Pod を直接スケジュールするのではなく、まず Job を Workloadとして管理し、その Workload が Queue と ClusterQueueのポリシーを満たしたときに Pod の作成を許可します。

そして

job1 完了 
↓
job2 admitted

という順番になります。 つまりKueueは「Jobの待ち行列を管理する」システムです。

ここまででなんとなくKueueの一番基本的な使い方が何となくわかってきました。 早速使ってみようと思い、KueueをGPUコンピューティングの基礎技術として使うメリットについて調べることにしました。

Kueueを知る

まず前提として冒頭でも少し触れたように、Kubernetes単体でもGPU Podは実行できます。 GPUノードにベンダーの device plugin を入れ、Pod側で resources.limits に GPU リソースを要求すれば、Kubernetes はその Pod を GPU ノードへ配置できます。GPU は通常 limits に指定し、requests と limits は同値として扱われます。

ただし、「GPUを使える」こと と 「GPUを効率よく運用できる」こと は別です。 Kubernetes標準の仕組みだけでも 1つずつの Pod/Job は動かせますが、GPUは高価でリアルな現場では使える台数も少ないため、実運用では次のような問題が出やすいです。

  • 先着順で重いJobが流れ込み、重要な学習Jobが待たされる
  • チームごとのGPU利用枠を公平に管理しづらい
  • 複数Podをまとめて同時に起動したい分散学習で、片方だけ動いて片方が詰まる
  • 空いている別クラスタへ逃がしたいが、標準Kubernetesだけでは弱い
  • 「今すぐPodを作る」のではなく、「リソースが揃ったら開始したい」というジョブ単位の待ち行列制御が弱い

このギャップを埋めるのがKueueやMultiKueueです。

KubernetesだけでGPUを使う場合の位置づけ

Kubernetes標準は、GPUをノード上の特殊リソースとして扱い、利用可能なノードに Pod を割り当てます。device plugin により GPU が kubeletに広告され、Pod/Job はその GPU 数を要求して実行されます。  この方式は、次の用途にはかなり向いています。

  • 単発の推論Pod
  • 小規模なバッチ
  • GPUノードが少数で、利用者も少ない環境
  • 先着順で困らない環境

つまり、「まずGPU Podを動かす」だけなら Kubernetes 標準で十分です。 Kueue はその上に載せる ジョブキューイング / クォータ /優先度制御レイヤー と考えると分かりやすいです。 Kueue自体は「どのノードに置くか」を直接やるというより、そのJobを今開始してよいかを管理します。 Kueue の説明でも、Jobが開始可能になる時点を制御し、必要に応じて停止させる仕組みだとされています。

Kueueを使うメリット

1. GPUジョブを「Pod単位」ではなく「Job単位」で入場制御できる

Kueue は Job-level managerとされており、Podを即時に作らせるのではなく、必要な割当条件が満たされるまで待たせることができます。 GPU学習のような重いバッチでは、この考え方がとても相性が良いです。  実務上の意味は次の通りです。

  • GPUが空くまでジョブを保留できる
  • 中途半端にPodだけ作って Pending を大量発生させにくい
  • 利用者には「キュー待ち」という形で見せやすい

2. チームごとのGPU枠を管理しやすい

Kueue の ClusterQueue は、CPU・メモリだけでなく hardware accelerators を含むリソースプールを管理し、使用量制限や Fair Sharingルールを定義できます。 GPUを「誰がどれだけ使ってよいか」を、namespace単位より一段運用寄りに扱えるのが強みです。

これにより、たとえば

  • Aチームは常時 8 GPU まで
  • Bチームは通常 4 GPU、空いていればバースト可
  • 本番推論より研究学習を低優先度にする

といった設計がしやすくなります。 標準の ResourceQuota でも namespace 単位の総量制限はできますが、Kueueのほうが待ち行列・優先度・共有ルールまで含めて扱いやすいです。

3. GPU種類を意識した運用がしやすい

Kueue の ResourceFlavorは、同じ「GPU」でもノード群の差異を表現するのに向いています。ドキュメントでは taint / tolerationをワークロード側へ反映させる例が示されており、特定ノード群への誘導に使えます。

たとえば

  • A100 ノード
  • L4 ノード
  • H100 ノード
  • MIG 有効ノード
  • 高速IB接続ノード

のような差を運用ルールに落とし込みやすいです。 単純なnodeSelector/affinityでも表現はできますが、 Kueueではキューと割当ルールの文脈で整理しやすいのが実務上の利点です。

4. 分散学習のような「まとまって起動したい」ワークロードと相性が良い

Kueue は Jobを入場管理するので、複数Podから成る分散ジョブで、必要資源が揃うまで待たせる設計に向いています。 公式にもbatch/JobやJobSet などのバッチワークロードを対象としています。 

特に GPU分散学習では、片方のワーカーだけ先に立っても意味が薄いことがあります。 そのため、「まとめて実行開始したい」という要求が出るなら、Kubernetes標準だけよりKueue の価値が大きくなります。

5. 優先度やプリエンプションを運用に組み込みやすい

Kueue は、どのJobを先に通すか、必要なら進行中ワークロードを止めるか、といった制御を行う仕組みです。 公式概要でも、Jobを待たせる・開始する・preempt することを扱うとされています。

そのため、GPUのような希少資源では

  • 緊急の推論復旧Jobを最優先
  • 研究用バッチは低優先度
  • 深夜バッチは空きGPUを自動活用

といったポリシー運用がしやすくなります。

MultiKueueを使うメリット

MultiKueue は、Kueueを複数クラスタに拡張する仕組みです。 公式では、manager cluster と 1つ以上の worker clusterから成る構成で、マルチクラスタにジョブをディスパッチします。 2026年1月時点の公式docsでは、beta 機能となっておりデフォルトで有効です。

1. GPUが空いている別クラスタへ逃がせる

これは MultiKueue の最大の価値です。 GPUクラスタを 1つだけでなく、たとえば:

  • オンプレGPUクラスタ
  • 開発用クラスタ
  • 別リージョンGPUクラスタ
  • 一時的なクラウドGPUクラスタ

に分けている場合、空いている worker cluster にジョブを出すという運用ができます。 

GPU不足が常態化している環境では、単一クラスタ最適化よりも全体最適に効きます。

2. 障害分散・メンテナンス耐性を持たせやすい

あるクラスタがメンテナンス中、あるいは GPUノード障害で逼迫しているときでも、別worker clusterに流せる設計にしやすいです。 これは特に長時間学習や夜間バッチで有効です。MultiKueue docsでも、管理クラスタからワーカークラスタへ実行を割り振る構成が前提です。

3. 地理分散や組織分散に対応しやすい

部門ごとにクラスタが分かれている、あるいは Regionごとにクラスタがある場合でも、ジョブ投入窓口をある程度一本化しやすくなります。 GPU資源を組織横断で融通したい場合に向いています。

Kueue / MultiKueueが不要なケース

次のような場合は、まず標準Kubernetesだけで始めてよいことが多いです。

  • GPUノードが少数で、利用者も少ない
  • Jobの競合がほぼ起きない
  • 優先度や公平性をまだ厳密に求めない
  • 単一クラスタ、単一チームでの利用
  • 主用途が常駐推論サービスで、バッチキューイング要件が弱い

この場合、まずは標準の機能だけで始め、運用で詰まってからKueueを足すのが自然です。

GPU用途ごとのざっくりした使い分け

  • Kubernetes標準だけで十分なことが多い
    • 推論API
    • 単発検証
    • 小規模ETLや推論バッチ
    • GPU利用者が少ない環境
  • Kueueを足す価値が高い
    • 学習Jobが複数チームで競合する
    • GPU台数が少なく取り合いになる
    • 優先度制御・公平性・クォータが必要
    • 分散学習や大きいバッチを運用する
  • MultiKueueまで考えたい
    • GPUクラスタが複数ある
    • オンプレ + クラウドをまたぐ
    • 空きクラスタへ振り分けたい
    • 障害時の逃がし先を持ちたい

補足: DRAとの関係

Kubernetes には Dynamic Resource Allocation (DRA)という仕組みもあり、GPU・TPU・NICなどのデバイスに対して、より柔軟な選択・割当・共有を行うための機能です。 Kubernetes v1.34 リリースでも、DRA は GPUなどのデバイスに対して、より強力な選択・割当・共有・設定を可能にするものとして触れられています。

整理すると:

  • Kubernetes標準 + device plugin
    • まずGPUを使えるようにする基礎
  • DRA
    • デバイス割当そのものをより高機能にする方向
  • Kueue
    • ジョブをいつ走らせるか、誰にどれだけ配るかを制御
  • MultiKueue
    • Kueue でできることを複数クラスタ対応にする

という住み分けです。

Kueueで実現できることの一例

KubernetesクラスターにKueueが導入されていればこんなことが可能です。

KubernetesクラスターとKueueがセットアップ済みで、つぎのようなKueueの設定がされているとします。

apiVersion: v1
kind: Namespace
metadata:
  name: ml-jobs
---
apiVersion: kueue.x-k8s.io/v1beta2
kind: ResourceFlavor
metadata:
  name: rtx-a1000
spec:
  nodeLabels:
    gpu.model: rtx-a1000
---
apiVersion: kueue.x-k8s.io/v1beta2
kind: ResourceFlavor
metadata:
  name: h100
spec:
  nodeLabels:
    gpu.model: h100
---
apiVersion: kueue.x-k8s.io/v1beta2
kind: ClusterQueue
metadata:
  name: gpu-cq
spec:
  namespaceSelector: {}
  resourceGroups:
    - coveredResources: ["cpu", "memory", "nvidia.com/gpu"]
      flavors:
        - name: rtx-a1000
          resources:
            - name: cpu
              nominalQuota: 32
            - name: memory
              nominalQuota: 128Gi
            - name: nvidia.com/gpu
              nominalQuota: 2
        - name: h100
          resources:
            - name: cpu
              nominalQuota: 64
            - name: memory
              nominalQuota: 512Gi
            - name: nvidia.com/gpu
              nominalQuota: 4
---
apiVersion: kueue.x-k8s.io/v1beta2
kind: LocalQueue
metadata:
  name: gpu-lq
  namespace: ml-jobs
spec:
  clusterQueue: gpu-cq

すると、このジョブは軽いジョブなのでRTX A1000で処理したり

---
apiVersion: batch/v1
kind: Job
metadata:
  name: light-job
  namespace: ml-jobs
  labels:
    kueue.x-k8s.io/queue-name: gpu-lq
spec:
  suspend: true
  template:
    spec:
      restartPolicy: Never
      nodeSelector:
        gpu.model: rtx-a1000
      containers:
        - name: cuda
          image: nvidia/cuda:12.4.1-runtime-ubuntu22.04
          command: ["bash", "-lc", "nvidia-smi && sleep 10"]
          resources:
            requests:
              cpu: "2"
              memory: "4Gi"
              nvidia.com/gpu: "1"
            limits:
              cpu: "2"
              memory: "4Gi"
              nvidia.com/gpu: "1"

これは重いJobなので、NVIDIA H100で処理する...のような使い方がKueueが導入されているKubernetesだと実現できます。

---
apiVersion: batch/v1
kind: Job
metadata:
  name: heavy-job
  namespace: ml-jobs
  labels:
    kueue.x-k8s.io/queue-name: gpu-lq
spec:
  suspend: true
  template:
    spec:
      restartPolicy: Never
      nodeSelector:
        gpu.model: h100
      containers:
        - name: cuda
          image: nvidia/cuda:12.4.1-runtime-ubuntu22.04
          command: ["bash", "-lc", "nvidia-smi && sleep 10"]
          resources:
            requests:
              cpu: "8"
              memory: "32Gi"
              nvidia.com/gpu: "1"
            limits:
              cpu: "8"
              memory: "32Gi"
              nvidia.com/gpu: "1"

前半では説明ばかりで、退屈になってしまったかもしれません。すみません。 続編では、実際に環境を構築して、Kueueを使ってみたいと思います。

tech.virtualtech.jp