仮想化通信

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

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

Kueueの概要については、前編にまとめています。

tech.virtualtech.jp

Kubernetes GPU + Kueue セットアップ手順書(Kueue入門 + Workload Lifecycle)

このドキュメントは GPUを搭載したKubernetesクラスタでGPUを利用し、Kueueを使ってジョブの順番実行(キューイング)と admission 制御を試す最小構成を説明する入門ガイドです。

Kueueは色々な環境に組み込んで利用できますが、本資料ではまず Kubernetes + GPU + Kueue の最小構成を構築し、Kueueの基本動作を理解することを目的としています。

なお環境構築は管理者権限でという前提で構築しているため、sudoをつかわない手順でセットアップしています。

想定環境

今回は基本的にこの構成を前提としてますが、適切なGPU Driverがインストール済みであればOSやバージョンは問いません。

  • Ubuntu Server 24.04
  • NVIDIA GPU 1枚
  • Kubernetes 1.29以上
  • CRI: containerd runtime
  • CNI: Flannel

なお、今回の記事の中編ではKubernetesとKueueを使うのを前提としています。後編ではKubeflowと連携した内容になる予定です。

Kueueのアーキテクチャ

重要なポイント

Kueueは Kubernetes Schedulerではありません。 Kueueの役割は「このJobを今実行してよいか?」を判断する Admission Controller です。

    User Job
       │
       ▼
    Kueue LocalQueue
       │
       ▼
    ClusterQueue admission
       │
       ▼
    Workload admitted
       │
       ▼
    Pod created
       │
       ▼
    Kubernetes Scheduler
       │
       ▼
    GPU Node
       │
       ▼
    GPU Container

Kueueの基本概念

Kueueについて抑えておくべき基本概念は次の通りです。

リソース 役割
ResourceFlavor ノードラベルや taint に紐づくリソース種類
ClusterQueue クラスタ全体のリソース割当ポリシー
LocalQueue namespace のユーザー入口
Workload Job の内部表現

内部処理の流れは次の通りです。

    Job
     ↓
    Workload
     ↓
    ClusterQueue admission
     ↓
    Pod作成

Kueueの内部アーキテクチャ(Workload Lifecycle)

Kueueでは Job を直接スケジュールしません。 Job を Workload オブジェクトに変換して管理します。

    Job
     │
     ▼
    Workload 作成
     │
     ▼
    Queueing
     │
     ▼
    Admission 判定
     │
     ▼
    Resource Reservation
     │
     ▼
    Pod 作成
     │
     ▼
    Kubernetes Scheduler
     │
     ▼
    Node

何となくわかってきたと思うので、この後から環境を構築していきます。 事前にそれなりのスペックのNVIDIA GPUを実装したサーバーに、Ubuntu Server 24.04をインストールしておきます。

1 GPUドライバのインストール

GPU ノードに NVIDIA ドライバをインストールします。NVIDIAのGPUドライバーはnvidia-driverというパッケージがUbuntu Archivesで提供されています。 今回はそれをインストールします。

$ sudo su -
# apt-get update && sudo apt-get install nvidia-driver-590-server-open
# reboot

再起動後にコマンドを実行して、GPUとGPU Driverが正常に動作していることを確認します。

# nvidia-smi -L

GPU 0: NVIDIA RTX A4000 (UUID: GPU-xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx)

2 Kubernetes クラスタ準備

すでに Kubernetes クラスタがある場合はスキップしてください。 クラスターはkubeadmを使ってインストールします。

コンテナランタイムはDockerのリポジトリーのcontainerd.ioパッケージを使ってインストールします。Ubuntu Archives版のcontainerdパッケージはバージョンが古く、NVIDIA Container Toolkitを介したGPUスケジューリング周りが思ったように動作しなかったためです。 CNIは何でも構いません。今回は主題ではなかったため、一番単純なFlannelを使いました。

今回はKueueだけを動かすことを前提としたため、Kubernetesはシングルノードで動かしています。いつか、Multikueueも試してみたいですね。

最小構成

  • kubeadm
  • containerd.io
  • kubernetes >= 1.29

構築したKubernetesクラスターの状態とバージョンを確認します。

# kubectl get node
NAME    STATUS   ROLES           AGE     VERSION
node1   Ready    control-plane   4d23h   v1.35.3

3 NVIDIA Container Toolkit

コンテナからGPUを使えるようにするために、NVIDIA Container Toolkitを導入します。

# apt-get update && sudo apt-get install -y --no-install-recommends \
   ca-certificates \
   curl \
   gnupg2

# curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \
  && curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
    sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
    tee /etc/apt/sources.list.d/nvidia-container-toolkit.list

# apt-get update
# export NVIDIA_CONTAINER_TOOLKIT_VERSION=1.19.0-1
# apt-get install -y \
      nvidia-container-toolkit=${NVIDIA_CONTAINER_TOOLKIT_VERSION} \
      nvidia-container-toolkit-base=${NVIDIA_CONTAINER_TOOLKIT_VERSION} \
      libnvidia-container-tools=${NVIDIA_CONTAINER_TOOLKIT_VERSION} \
      libnvidia-container1=${NVIDIA_CONTAINER_TOOLKIT_VERSION}

# nvidia-ctk runtime configure --runtime=containerd
# systemctl restart containerd
# systemctl enable containerd

4 NVIDIA Device Plugin

Kubernetes が GPU を認識するために NVIDIA Device Pluginを導入します。

# kubectl create -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v0.19.0/deployments/static/nvidia-device-plugin.yml

NVIDIA Device Pluginの導入ができたかを確認します。

# kubectl get pods -n kube-system

GPU リソースを確認します。

# kubectl describe node | grep nvidia.com/gpu

NVIDIA Device PluginとNVIDIA GPU Operator

GPUアクセラレーションされたワークロードを実行するためにKubernetesに対してNVIDIA Device PluginとNVIDIA GPU Operatorが使われますが、両者の違いは何でしょうか?どちらか一方だけを使うべきでしょうか、それとも両方を併用できるのでしょうか?

端的に説明すると、Device Pluginはコンテナワークロードのスケジュール設定を可能にするデーモンセットであり、GPUをリソースとして管理するものであり、GPU OperatorはGPUドライバとコンテナランタイムを使用してノードを構成し、Device Pluginをデプロイをするためのものです。今回はGPUドライバーとNVIDIA Device Plugin、NVIDIA Container Toolkitを手動でセットアップしたため、NVIDIA GPU Operatorは使っていませんが、スムーズにセットアップしたいのであればNVIDIA GPU Operatorを使うと良いと思います。

5 GPU Pod テスト

Kueue を使う前に Kubernetes 単体で GPU Podを実行できるか確認します。

apiVersion: v1
kind: Pod
metadata:
  name: gpu-test
spec:
  runtimeClassName: nvidia    
  restartPolicy: Never
  containers:
  - name: cuda
    image: nvidia/cuda:13.0.0-base-ubuntu22.04
    command: ["bash","-c","nvidia-smi"]
    resources:
      limits:
        nvidia.com/gpu: 1

このマニフェストを使ってPodを作ってみます。

# kubectl apply -f gpu-test.yaml
# kubectl logs gpu-test

GPU 情報が表示されれば成功です。

# kubectl logs gpu-test
Wed Apr  1 06:07:51 2026
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 590.48.01              Driver Version: 590.48.01      CUDA Version: 13.1     |
+-----------------------------------------+------------------------+----------------------+
| GPU  Name                 Persistence-M | Bus-Id          Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |           Memory-Usage | GPU-Util  Compute M. |
|                                         |                        |               MIG M. |
|=========================================+========================+======================|
|   0  NVIDIA RTX A4000               Off |   00000000:37:00.0 Off |                  Off |
| 41%   33C    P8             10W /  140W |       0MiB /  16376MiB |      0%      Default |
|                                         |                        |                  N/A |
+-----------------------------------------+------------------------+----------------------+

+-----------------------------------------------------------------------------------------+
| Processes:                                                                              |
|  GPU   GI   CI              PID   Type   Process name                        GPU Memory |
|        ID   ID                                                               Usage      |
|=========================================================================================|
|  No running processes found                                                             |
+-----------------------------------------------------------------------------------------+

6 Kueue インストール

Kueue のドキュメントに従って、インストールします。いくつかの方法がありますが、今回はマニフェストファイルを使ってインストールします。

# kubectl apply --server-side -f https://github.com/kubernetes-sigs/kueue/releases/download/v0.16.4/manifests.yaml
# kubectl wait deploy/kueue-controller-manager -nkueue-system --for=condition=available --timeout=5m

確認してみましょう。

# kubectl get pods -n kueue-system

7 Queue 設定

セットアップしたKueueを使ってQueueを定義してみましょう。

GPUラベルを設定する

ノードアクセラレーターとしてnvidiaを使う設定を加えます。 以下はノード名がnode1の場合の例です。

# kubectl label node node1 accelerator=nvidia

Namespace

ml-jobsという名前空間で作業します。

apiVersion: v1
kind: Namespace
metadata:
  name: ml-jobs

ResourceFlavor

リソースフレーバーを設定します。

apiVersion: kueue.x-k8s.io/v1beta2
kind: ResourceFlavor
metadata:
  name: gpu
spec:
  nodeLabels:
    accelerator: nvidia

ClusterQueue

ClusterQueueを定義します。

apiVersion: kueue.x-k8s.io/v1beta2
kind: ClusterQueue
metadata:
  name: gpu-clusterqueue
spec:
  namespaceSelector: {}
  resourceGroups:
  - coveredResources: ["cpu","memory","nvidia.com/gpu"]
    flavors:
    - name: gpu
      resources:
      - name: cpu
        nominalQuota: 8
      - name: memory
        nominalQuota: 32Gi
      - name: nvidia.com/gpu
        nominalQuota: 1

LocalQueue

LocalQueueを定義します。

apiVersion: kueue.x-k8s.io/v1beta2
kind: LocalQueue
metadata:
  name: gpu-queue
  namespace: ml-jobs
spec:
  clusterQueue: gpu-clusterqueue

Queue設定の適用

NamespaceからLocalQueueの設定までをクラスターに適用します。

# kubectl apply -f queue-conf-apply.yaml

8 GPU Job 実行

KueueでQueueを定義したら、GPU Jobを実行してみましょう。 metadata nameをcuda-job1とcuda-job2に変えただけのマニフェストを1つずつ作ってください。

apiVersion: batch/v1
kind: Job
metadata:
  name: cuda-job1
  namespace: ml-jobs
  labels:
    kueue.x-k8s.io/queue-name: gpu-queue
spec:
  backoffLimit: 0
  template:
    spec:
      restartPolicy: Never
      containers:
      - name: cuda
        image: nvidia/cuda:13.0.0-base-ubuntu22.04
        command: ["bash","-lc","sleep 30; exit 0"]
        resources:
          requests:
            cpu: "1"
            memory: "1Gi"
            nvidia.com/gpu: "1"
          limits:
            cpu: "1"
            memory: "1Gi"
            nvidia.com/gpu: "1"

まずは一つだけ実行して、Jobが動作する事を確認します。

# kubectl apply -f job1.yaml

9 状態確認

JobステータスがCompleteになっていますので、このJobの実行が成功しています。

# kubectl get workloads -n ml-jobs
NAME                           QUEUE       RESERVED IN        ADMITTED   FINISHED   AGE
job-cuda-job1-581dd            gpu-queue   gpu-clusterqueue   True                  5s

# kubectl get clusterqueues
NAME               COHORT   PENDING WORKLOADS
gpu-clusterqueue            0

# kubectl get localqueues -n ml-jobs
NAME        CLUSTERQUEUE       PENDING WORKLOADS   ADMITTED WORKLOADS
gpu-queue   gpu-clusterqueue   0                   1

# kubectl get jobs -n ml-jobs
NAME                 STATUS     COMPLETIONS   DURATION   AGE
cuda-job1            Complete   1/1           34s        42s

一度テストしたJobは消しておきます。

# kubectl delete -f job1.yaml
job.batch "cuda-job1" deleted from ml-jobs namespace

10 Queue 動作確認

次が本番です。 GPU が1枚の環境で、Jobを2つ実行してみましょう。

# kubectl apply -f job1.yaml -f job2.yaml
# kubectl get jobs -n ml-jobs -w

すると、次のように表示されるはずです。

job1 Running
job2 Suspended

job1 が終了すると、次のように変化するはずです。

job1 Complete
job2 Running

これで、Kueueを使ったKubernetesのQueue制御ができるようになりました。 最終的には次のようになります。

# kubectl get jobs -n ml-jobs -w
NAME        STATUS     COMPLETIONS   DURATION   AGE
cuda-job1   Complete   1/1           7m12s      15m
cuda-job2   Running    0/1           4s         4s
cuda-job2   Running    0/1           32s        32s
cuda-job2   SuccessCriteriaMet   0/1           33s        33s
cuda-job2   Complete             1/1           33s        33s

# kubectl get jobs -n ml-jobs
NAME        STATUS     COMPLETIONS   DURATION   AGE
cuda-job1   Complete   1/1           7m12s      16m
cuda-job2   Complete   1/1           33s        40s

ここまでのまとめ

今回は次の流れで環境構築しました。

  1. GPU driver
  2. Kubernetes cluster
  3. NVIDIA Container Toolkit
  4. NVIDIA Device Plugin
  5. GPU Pod テスト
  6. Kueue インストール
  7. Queue 設定
  8. Job 実行

これによりKueueを使って

  • GPUジョブの順番待ち
  • GPUリソース制御
  • Admission 管理

を体験する環境が手元に現れました。 せっかく環境を作ったので、早速使ってみるしかないですね!


PyTorchを用いたMNIST画像分類モデルの訓練Jobの試行

少し実践的なJobをKueueで流してみます。 次のようなマニフェストを作成してJobを流してみましょう。

apiVersion: batch/v1
kind: Job
metadata:
  name: pytorch-mnist-fast
  namespace: ml-jobs
  labels:
    kueue.x-k8s.io/queue-name: gpu-queue
spec:
  backoffLimit: 0
  ttlSecondsAfterFinished: 3600
  template:
    spec:
      restartPolicy: Never
      runtimeClassName: nvidia   # ここを追加
      containers:
      - name: pytorch
        image: docker.io/kubeflowkatib/pytorch-mnist-gpu:latest
        command: ["python", "mnist.py", "--epochs=1", "--batch-size=128"]
        resources:
          requests:
            cpu: "4"
            memory: "8Gi"
            nvidia.com/gpu: "1"
          limits:
            cpu: "4"
            memory: "8Gi"
            nvidia.com/gpu: "1"

# kubectl apply -f pytorch-mnist-fast-test.yaml
  • 大きめのイメージを使うため、めっちゃ時間がかかると思います
  • GPU処理できる環境では、Train Epochの処理は一瞬で終わります
    • Using CUDAと出力されている場合は、GPUで処理されます
  • Jobを適用後、kubectl get pods -n ml-jobs -wでPodがRunningになるのを確認
  • kubectl logs -n ml-jobs -f <pod-name> で訓練ログ(loss/accuracy)を確認

実行例は次のような感じです。CPUモードで動かすと結構時間がかかりますが、Using CUDA(GPU)モードではあっという間でした。

MNISTは「手書き数字の画像データセット」です。機械学習、とくにディープラーニングの入門・動作確認用によく使われます。 今回使ったのはその派生版であるFashionMNISTで、MNISTは手書きの数字を学習させますが、FashionMNISTは服の画像を学習させるものです。

今回はこれをKubernetesとKueue環境で動かすのを試しています。

# kubectl logs -n ml-jobs -f pytorch-mnist-fast-tbtcf
Using CUDA
Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/train-images-idx3-ubyte.gz
Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/train-images-idx3-ubyte.gz to ./data/FashionMNIST/raw/train-images-idx3-ubyte.gz
100%|██████████| 26421880/26421880 [00:04<00:00, 6009176.59it/s]
Extracting ./data/FashionMNIST/raw/train-images-idx3-ubyte.gz to ./data/FashionMNIST/raw

Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/train-labels-idx1-ubyte.gz
Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/train-labels-idx1-ubyte.gz to ./data/FashionMNIST/raw/train-labels-idx1-ubyte.gz
100%|██████████| 29515/29515 [00:00<00:00, 111754.46it/s]
Extracting ./data/FashionMNIST/raw/train-labels-idx1-ubyte.gz to ./data/FashionMNIST/raw

Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/t10k-images-idx3-ubyte.gz
Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/t10k-images-idx3-ubyte.gz to ./data/FashionMNIST/raw/t10k-images-idx3-ubyte.gz
100%|██████████| 4422102/4422102 [00:02<00:00, 2055680.69it/s]
Extracting ./data/FashionMNIST/raw/t10k-images-idx3-ubyte.gz to ./data/FashionMNIST/raw

Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/t10k-labels-idx1-ubyte.gz
Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/t10k-labels-idx1-ubyte.gz to ./data/FashionMNIST/raw/t10k-labels-idx1-ubyte.gz
100%|██████████| 5148/5148 [00:00<00:00, 17163972.17it/s]
Extracting ./data/FashionMNIST/raw/t10k-labels-idx1-ubyte.gz to ./data/FashionMNIST/raw

2026-03-30T06:57:07Z INFO     Train Epoch: 1 [0/60000 (0%)]     loss=2.3015
2026-03-30T06:57:07Z INFO     Train Epoch: 1 [1280/60000 (2%)]  loss=2.2863
2026-03-30T06:57:07Z INFO     Train Epoch: 1 [2560/60000 (4%)]  loss=2.2854
2026-03-30T06:57:08Z INFO     Train Epoch: 1 [3840/60000 (6%)]  loss=2.2727
2026-03-30T06:57:08Z INFO     Train Epoch: 1 [5120/60000 (9%)]  loss=2.2693
2026-03-30T06:57:08Z INFO     Train Epoch: 1 [6400/60000 (11%)] loss=2.2499
2026-03-30T06:57:08Z INFO     Train Epoch: 1 [7680/60000 (13%)] loss=2.2421
2026-03-30T06:57:08Z INFO     Train Epoch: 1 [8960/60000 (15%)] loss=2.2029
2026-03-30T06:57:08Z INFO     Train Epoch: 1 [10240/60000 (17%)]        loss=2.1879
2026-03-30T06:57:08Z INFO     Train Epoch: 1 [11520/60000 (19%)]        loss=2.1322
2026-03-30T06:57:08Z INFO     Train Epoch: 1 [12800/60000 (21%)]        loss=2.0497
2026-03-30T06:57:09Z INFO     Train Epoch: 1 [14080/60000 (23%)]        loss=1.9477
2026-03-30T06:57:09Z INFO     Train Epoch: 1 [15360/60000 (26%)]        loss=1.7657
2026-03-30T06:57:09Z INFO     Train Epoch: 1 [16640/60000 (28%)]        loss=1.5977
2026-03-30T06:57:09Z INFO     Train Epoch: 1 [17920/60000 (30%)]        loss=1.3737
2026-03-30T06:57:09Z INFO     Train Epoch: 1 [19200/60000 (32%)]        loss=1.3095
2026-03-30T06:57:09Z INFO     Train Epoch: 1 [20480/60000 (34%)]        loss=1.1860
2026-03-30T06:57:09Z INFO     Train Epoch: 1 [21760/60000 (36%)]        loss=1.0811
2026-03-30T06:57:09Z INFO     Train Epoch: 1 [23040/60000 (38%)]        loss=1.0569
2026-03-30T06:57:09Z INFO     Train Epoch: 1 [24320/60000 (41%)]        loss=1.0206
2026-03-30T06:57:10Z INFO     Train Epoch: 1 [25600/60000 (43%)]        loss=0.9029
2026-03-30T06:57:10Z INFO     Train Epoch: 1 [26880/60000 (45%)]        loss=1.0218
2026-03-30T06:57:10Z INFO     Train Epoch: 1 [28160/60000 (47%)]        loss=1.0534
2026-03-30T06:57:10Z INFO     Train Epoch: 1 [29440/60000 (49%)]        loss=0.9032
2026-03-30T06:57:10Z INFO     Train Epoch: 1 [30720/60000 (51%)]        loss=0.7920
2026-03-30T06:57:10Z INFO     Train Epoch: 1 [32000/60000 (53%)]        loss=1.1959
2026-03-30T06:57:10Z INFO     Train Epoch: 1 [33280/60000 (55%)]        loss=1.0278
2026-03-30T06:57:10Z INFO     Train Epoch: 1 [34560/60000 (58%)]        loss=0.9575
2026-03-30T06:57:10Z INFO     Train Epoch: 1 [35840/60000 (60%)]        loss=0.7868
2026-03-30T06:57:11Z INFO     Train Epoch: 1 [37120/60000 (62%)]        loss=0.9319
2026-03-30T06:57:11Z INFO     Train Epoch: 1 [38400/60000 (64%)]        loss=0.9490
2026-03-30T06:57:11Z INFO     Train Epoch: 1 [39680/60000 (66%)]        loss=0.8622
2026-03-30T06:57:11Z INFO     Train Epoch: 1 [40960/60000 (68%)]        loss=0.8083
2026-03-30T06:57:11Z INFO     Train Epoch: 1 [42240/60000 (70%)]        loss=0.8859
2026-03-30T06:57:11Z INFO     Train Epoch: 1 [43520/60000 (72%)]        loss=0.9461
2026-03-30T06:57:11Z INFO     Train Epoch: 1 [44800/60000 (75%)]        loss=0.9372
2026-03-30T06:57:11Z INFO     Train Epoch: 1 [46080/60000 (77%)]        loss=0.8243
2026-03-30T06:57:11Z INFO     Train Epoch: 1 [47360/60000 (79%)]        loss=0.7680
2026-03-30T06:57:12Z INFO     Train Epoch: 1 [48640/60000 (81%)]        loss=0.8689
2026-03-30T06:57:12Z INFO     Train Epoch: 1 [49920/60000 (83%)]        loss=0.8685
2026-03-30T06:57:12Z INFO     Train Epoch: 1 [51200/60000 (85%)]        loss=0.7834
2026-03-30T06:57:12Z INFO     Train Epoch: 1 [52480/60000 (87%)]        loss=0.6895
2026-03-30T06:57:12Z INFO     Train Epoch: 1 [53760/60000 (90%)]        loss=0.8083
2026-03-30T06:57:12Z INFO     Train Epoch: 1 [55040/60000 (92%)]        loss=0.8780
2026-03-30T06:57:12Z INFO     Train Epoch: 1 [56320/60000 (94%)]        loss=0.8591
2026-03-30T06:57:12Z INFO     Train Epoch: 1 [57600/60000 (96%)]        loss=0.9279
2026-03-30T06:57:12Z INFO     Train Epoch: 1 [58880/60000 (98%)]        loss=0.7688
2026-03-30T06:57:14Z INFO     {metricName: accuracy, metricValue: 0.6871};{metricName: loss, metricValue: 0.8291}

GPUを使えるようになりましたので、ニューラルネットワークの学習の数を増やすため、epochsを1から増やしていきます。CPUモードでは時間がかかる処理も、GPUであればあっという間です。

--

Kueueのアップグレード

Kueueは現在活発に開発中のソフトウェアです。直近を見ると早ければ2週間から1ヶ月間隔でリリースが行われています。

実はこの記事を書くに当たり、書き始めた時点で最新のバージョンをインストールしたのですが、記事の下書きが終わって推敲している間に新しいマイナーバージョンとメジャーリリースがリリースされました。

現時点では上書きインストールすればKueueのアップグレードは可能のようです。 ただし開発が活発であること、当然ながらアップグレードにはAPI変更などの注意するべきことがあることを意識する必要があります。 実行する前にリリースノードの確認派しておく必要がありそうです。

マイナーバージョンの更新は破壊的変更を伴うことは無いとは思いますが、基本的にはリリースノートを見てから実行するというのが何度も言いますが重要です。

セットアップしたのは0.16.4なので、2026/04/01時点でリリースされたv0.16.5のアップグレードについてはkubectl applyで新しいマニフェストを流すだけで良いそうです。

# kubectl apply --server-side -f https://github.com/kubernetes-sigs/kueue/releases/download/v0.16.5/manifests.yaml
# kubectl wait deploy/kueue-controller-manager -nkueue-system --for=condition=available --timeout=5m

今回の記事ではhelmを使ってKueueをセットアップしていませんが、仮にHelmを使ってKueueを導入した場合はhelm upgrade kueueをするだけで良いそうです。

# helm upgrade kueue oci://registry.k8s.io/kueue/charts/kueue \
  --version 0.16.5 \
  -n kueue-system

アップグレード後、kueue-controller-managerRunningになっていることを確認します。

# kubectl get pods -n kueue-system
NAME                                       READY   STATUS    RESTARTS   AGE
kueue-controller-manager-9c5b76fb7-smvs2   1/1     Running   0          107s
root@DL380G10-1:~# kubectl get all -n kueue-system
NAME                                           READY   STATUS    RESTARTS   AGE
pod/kueue-controller-manager-9c5b76fb7-smvs2   1/1     Running   0          2m47s

NAME                                               TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
service/kueue-controller-manager-metrics-service   ClusterIP   10.111.180.184   <none>        8443/TCP   2d
service/kueue-visibility-server                    ClusterIP   10.104.204.232   <none>        443/TCP    2d
service/kueue-webhook-service                      ClusterIP   10.110.150.8     <none>        443/TCP    2d

NAME                                       READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/kueue-controller-manager   1/1     1            1           2d

NAME                                                  DESIRED   CURRENT   READY   AGE
replicaset.apps/kueue-controller-manager-795b49676b   0         0         0       2d
replicaset.apps/kueue-controller-manager-9c5b76fb7    1         1         1       2m47s

メジャーバージョンへのアップグレードについては、少し慎重になったほうが良さそうです。

APIバージョンの変更もあるため、Queue 定義ファイルのAPIバージョンを次のように変更する必要がありそうです。

apiVersion: kueue.x-k8s.io/v1beta2
kind: ResourceFlavor
...
---
apiVersion: kueue.x-k8s.io/v1beta2
kind: ClusterQueue
...
---
apiVersion: kueue.x-k8s.io/v1beta2
kind: LocalQueue
...

CRDのバージョンは次の方法で確認できるようです。これは0.16.4でKueueを動かした後、0.16.5にアップグレードした後のKueueのCRDのバージョンを出力したものです。0.16.xではAPIバージョンとしてv1beta2を指定するのを推奨していますが、v1beta1も利用できます。 0.17.xではv1beta1が廃止されるそうなので、Queue 定義ファイルはv1beta2に変更して適用する必要があるということです。

すでにQueue 定義ファイルが適用済みの場合は次のような変更を加えて、新しいバージョンのマニフェストをkubectl applyするだけで修正完了になります。

# diff -u queue-conf-apply.yaml queue-conf-apply-v1beta2.yaml
--- queue-conf-apply.yaml       2026-03-30 02:02:10.418323275 +0000
+++ queue-conf-apply-v1beta2.yaml       2026-04-01 03:09:37.985986127 +0000
@@ -4,7 +4,7 @@
 metadata:
   name: ml-jobs
 ---
-apiVersion: kueue.x-k8s.io/v1beta1
+apiVersion: kueue.x-k8s.io/v1beta2
 kind: ResourceFlavor
 metadata:
   name: gpu
@@ -12,7 +12,7 @@
   nodeLabels:
     accelerator: nvidia
 ---
-apiVersion: kueue.x-k8s.io/v1beta1
+apiVersion: kueue.x-k8s.io/v1beta2
 kind: ClusterQueue
 metadata:
   name: gpu-clusterqueue
@@ -30,7 +30,7 @@
       - name: nvidia.com/gpu
         nominalQuota: 1
 ---
-apiVersion: kueue.x-k8s.io/v1beta1
+apiVersion: kueue.x-k8s.io/v1beta2
 kind: LocalQueue
 metadata:
   name: gpu-queue

Queue 定義に変更を適用

# kubectl apply -f queue-conf-apply-v1beta2.yaml

なおAPIバージョンの変更に伴い、マニフェストの書き方が変わると言うのがKubernetesあるあるですので、何か上手くいかない場合はそのあたりも公式のドキュメントなどを見て確認する必要があるかもしれません。

Kueueのメジャーアップグレード

メジャーアップグレードについても、一応まとめたものを書いておきます。 基本的にはこれだけで良いはずですが、実行前にリリースノートを確認してAPI変更やマニフェストの記述ルールに変更がないかを確認する事が重要です。

Kueueのメジャーアップグレードで重要なのは、対応するKubernetesのバージョンに変更がないかも確認が必要です。稼働中のクラスターバージョンよりも新しいバージョンを要求される場合もあります。その場合はまず、Kubernetesクラスターのバージョンアップグレードが必要になります。細かい話ですが、kubectl CLIのバージョンも場合によっては更新が必要になりますし、CNIアドオンやCRIランタイムの更新も必要になってくる場合があります。

//バックアップディレクトリーを作る
mkdir -p ~/kueue-backup

kubectl get resourceflavors.kueue.x-k8s.io -A -o yaml > ~/kueue-backup/resourceflavors.yaml
kubectl get clusterqueues.kueue.x-k8s.io -A -o yaml > ~/kueue-backup/clusterqueues.yaml
kubectl get localqueues.kueue.x-k8s.io -A -o yaml > ~/kueue-backup/localqueues.yaml
kubectl get workloads.kueue.x-k8s.io -A -o yaml > ~/kueue-backup/workloads.yaml

//アップグレード前のCRDを確認
for crd in $(kubectl get crd -o name | grep 'kueue.x-k8s.io'); do
  echo "=== $crd ==="
  kubectl get "$crd" -o jsonpath='{range .spec.versions[*]}{.name}{" served="}{.served}{" storage="}{.storage}{"\n"}{end}storedVersions={.status.storedVersions}{"\n\n"}'
done

//Kueueをアップグレードする
kubectl apply --server-side -f https://github.com/kubernetes-sigs/kueue/releases/download/v0.17.0/manifests.yaml
kubectl wait deploy/kueue-controller-manager -n kueue-system --for=condition=available --timeout=5m

//アップグレード後のCRDを確認
for crd in $(kubectl get crd -o name | grep 'kueue.x-k8s.io'); do
  echo "=== $crd ==="
  kubectl get "$crd" -o jsonpath='{range .spec.versions[*]}{.name}{" served="}{.served}{" storage="}{.storage}{"\n"}{end}storedVersions={.status.storedVersions}{"\n\n"}'
done

//Kueue リソースが普通に読めるか確認
kubectl get resourceflavors.kueue.x-k8s.io
kubectl get clusterqueues.kueue.x-k8s.io
kubectl get localqueues.kueue.x-k8s.io -A
kubectl get workloads.kueue.x-k8s.io -A

次回はこの環境にKubeflowを導入して、KubeflowとKueueの連携周りを試してみたいと思っています。

tech.virtualtech.jp