気軽に試せるラップトップ環境で、チャットbotを提供するオールインワンの生成AI環境構築から始め、Kubernetesを活用した本格的なGPUクラスタの構築やモデルのファインチューニングまで解説する本連載。今回はNVIDIA製GPUを用いたKubernetesクラスタの構築方法をモニタリング環境の構築手順を交えて解説します。
この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。
前回の記事では、Visual Studio Codeで、生成AI(人工知能)を外部ツールと連携させるためのプロトコル「MCP」(Model Context Protocol)を利用したAIエージェントの高度な活用について概観しました。
今回は、「生成AI時代のAI基盤構築」と題して、NVIDIA製GPUを用いたKubernetesクラスタを構築する方法を解説します。従来のDevice Pluginに代わる、Kubernetesの新しいGPU管理機構である「Dynamic Resource Allocation」(DRA)の導入と、「GPU Operator」によるコンポーネントの自動セットアップ、「Grafana LGTMスタック」(LGTM:Loki〈ログ〉、Grafana〈ダッシュボード〉、Tempo〈トレーシング〉、Mimir〈メトリクス〉)によるGPUモニタリング環境の構築手順まで一挙に紹介します。
生成AIの本格的な学習、ファインチューニングおよび推論環境を構築するには、複数のGPUとノードで構成されるクラスタが不可欠です。GPUを活用した生成AIクラスタの基盤として、Kubernetesが注目されており、以下の理由から選択される機会が増えてきています。
NVIDIAのAIスタック「NVIDIA AI Enterprise」にも、Kubernetesが採用されています。また、Linux Foundation傘下でAIとデータ分析のOSS(オープンソースソフトウェア)をサポートするAI & Dataプロジェクトでは、KServe、Flyte、DatasimといったKubernetesを利用したプロジェクトが推進されています。
2025年に東京で開催された「KubeCon + CloudNativeCon Japan」では、GPUなどの特殊デバイスをPodへ割り当てるDRAに関する発表が特に注目されました。これに加え、GPUのスケーリング、マルチクラスタにおけるGPU管理、そしてKubernetes標準で開発されるモデルサービングに関する発表も行われました。こうした動向からも、データセンターにおけるGPUクラスタ運用において、Kubernetesが現在非常に注目されていることが分かります。
しかし、KubernetesでGPUを利用できるようにするには、さまざまなコンポーネントを矛盾なくセットアップする必要があります。NVIDIA GPUを利用する場合、下記のようなコンポーネントが必要です。
コンポーネント | 説明 |
---|---|
GPU Driver | GPUをOSから利用できるデバイスとして制御 |
Container Toolkit | コンテナランタイムがGPUを利用できるように設定 |
DRA Driver/Device Plugin | Podに公開するGPUリソースの管理 |
DCGM Exporter | GPUのモニタリング |
GPUの進化は目覚ましく、毎年、機能の拡充やコア数の増加が図られた新製品が登場しています。サーバ増設時に新しいGPUのノードを増やすと、ノードごとにGPUの種別を意識する必要があります。GPU Operatorは、ノード上のGPUを自動的に認識し、必要なコンポーネントを自動で用意するため、構築や運用の複雑さを解消します。
なお、上記はGPUのPodの割り当てに限った話です。PyTorchやOllamaなどの機械学習フレームワークや推論フレームワークなどのセットアップも別途Pod内で対応が必要です。
大規模なGPUクラスタを複数のジョブで利用するには、ジョブスケジューラなども必要となり、採用事例が既にあるVolcano、CNCF(Cloud Native Computing Foundation)で開発されているKueue、NVIDIAが2025年7月にOSSとして公開したKAI Schedulerなどがあります。
Kubernetes環境において、従来のGPUデバイス管理には「デバイスプラグイン」という仕組みが用いられていました。DRAはKubernetes v1.26で試験的に導入された、FPGA、NIC、DPUなどの特殊なハードウェアを管理する新しい手法であり、以下のような特徴を持っています。
【GPUの静的、動的な確保】
DRAでは、GPUを事前に確保することも、Pod作成時にTemplateから動的に確保することもできます。複数のPodによる分散処理をさせる場合、事前確保しておくことにより実行前にGPUを割り当てることができます
【GPUの分割と共有】
MIG(Multi-Instance GPU)を利用したGPUの論理分割、MPS(Multi-Process Service)による複数プロセスでのGPU共有、Time Slicing(時間分割によるPod間での共有)など、さまざまな方法でGPUを複数のコンテナやPodで共有できます。これにより、ワークロードやセキュリティ要件に応じて柔軟な選択が可能です
DRAは、2025年8月27日にリリースが予定されているKubernetes v1.34にて正式リリースされる予定です。本稿では、一足先にDRAの機能を利用します。
本稿にて構築するAI基盤環境は、Intelのサーバ1台に下記のコンポーネントをインストールしたものとなります。
GPUに「NVIDIA A100」を8枚利用していますが、GPU OperatorによりGPUが自動検出されるため、他のNVIDIA GPUであっても、本稿で紹介する手順で基盤を構築できるでしょう。GPU Operatorでサポートされるデバイスについては、NVIDIAの公式ドキュメント(NVIDIA GPU Operator / Supported NVIDIA Data Center GPUs and Systems)をご覧ください。
緑の枠で囲まれた部分がGPUで利用するコンポーネントとなり、点線のコンポーネントはGPU Operatorが自動的にインストールしてくれます。オレンジ色の部分はモニタリング基盤です。
端末からはUbuntuへSSH(Secure Shell)でログインし、kubectl、helm、kubeadmといったツールを利用してKubernetesクラスタの構築やPodの操作などを行います。WebブラウザからGrafanaへアクセスする際には、IngressコントローラーであるTraefikを介してアクセスします。
なお本稿で紹介した設定ファイルは、GitHubで公開していますので、ぜひご利用ください。
サーバ、OSは下記の通りです。
項目 | 利用コンポーネント |
---|---|
サーバ | Intelサーバ |
GPU | NVIDIA A100 x 8 |
OS | Ubuntu 22.04 |
RHEL(Red Hat Enterprise Linux)系OSでも利用できますが、今回はUbuntuを利用しました。
Kubernetesの環境構築には、下記のコンポーネントを利用しました。
項目 | 利用コンポーネント |
---|---|
クラスタ構築 | kubeadm |
コンテナランライム | containerd |
CNI(ネットワーク) | Calico |
Ingress(トラフィックルーティング) | Traefik |
CSI(ストレージ) | Local Path Provisioner |
今回は、1台の簡易環境なので、Local Path Provisioner(各ノードのローカルストレージ)を利用しました。大規模環境で利用する場合は、ストレージは別途ご検討ください。また、クラスタについては、クラウドベンダーのマネージドKubernetesやRed Hat OpenShiftなどKubernetesディストリビューションでも利用可能です。ベンダーの製品を利用する場合は、ベンダーが提供している手順も参考にしてください。
GPU関連のセットアップには、下記のコンポーネントを利用しています。
コンポーネント | 説明 |
---|---|
NVIDIA GPU Operator | GPUを検出しGPUに必要なコンポーネントのインストールと設定を行うOperator |
DRA for NVIDIA GPUs | GPUを管理しPodへの割り当てと解放を実施 |
またGPU Operatorによって、下記のコンポーネントがインストールされます。
コンポーネント | 説明 |
---|---|
NVIDIA GPU Driver | GPUドライバ |
NVIDIA Container Toolkit | containerdでGPUを利用可能にする |
DCGM(Data Center GPU Manager) Exporter | GPUメトリクスを取得する |
Node Feature Discovery | GPUの機能を検出し、ノードにラベル付けする |
MIG Manager | GPUのMIGによる分割を管理 |
本稿後半にて、Grafana LGTMを利用したモニタリング環境を紹介していますので、ここでは割愛します。クラウド上でGPUクラスタを構築する場合は、クラウドのモニタリングサービスを利用してもよいでしょう。
ここからは、下記の手順でAI基盤環境を構築していきます。
ここでは、Kubernetesクラスタを構築するためのOSおよびKubernetesのインストール手順を解説します。なお、既に構築済みのKubernetesのクラスタ環境にGPUの設定を追加する場合は、「GPUの設定」まで読み飛ばしてください。
スワップの無効化
スワップはディスクI/O(Input/Output)を発生させ、アプリケーションの応答を遅らせるだけでなく、メモリ不足の診断を困難にします。また、Kubernetesの厳密なリソース管理とスケジューリングの整合性を損なうため、スワップを無効化し、OOM(Out of Memory)キルによる明確な障害と再起動を促す方が、スワップによる不安定な動作よりも望ましいとされます。スワップの無効化により、デバッグも容易になります。
sudo swapoff -a sudo sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab
カーネルのネットワーク設定
Pod間通信を正しく機能させるには、Linuxのカーネルパラメーターの設定が必要です。
下記のコマンドを利用して、ブリッジ利用したネットワークトラフィックにiptablesを適用するカーネルモジュールをロードします。
sudo modprobe br_netfilter
上記の設定が、OS再起動時に自動反映されるように下記のファイルを作成しておきます。
br_netfilter
次に、カーネルパラメーターを設定します。下記のファイルを作成します。
# IPフォワーディングを有効にし、ノード間でPodのトラフィックをルーティング net.ipv4.ip_forward = 1 # ブリッジを通過するパケットに対してもiptablesルールを適用(kube-proxyがiptablesモードの場合必要) net.bridge.bridge-nf-call-iptables = 1 # 同IPv6への適用 net.bridge.bridge-nf-call-ip6tables = 1
作成したらsysctlコマンドで適用します。
sudo sysctl -p /etc/sysctl.d/99-kubernetes.conf
基本的なOSのパッケージをインストールします。
sudo apt update && sudo apt upgrade -y sudo apt install -y apt-transport-https ca-certificates curl gnupg lsb-release
次に、containerdをインストールします。
sudo apt install -y containerd sudo mkdir /etc/containerd containerd config default | sudo tee /etc/containerd/config.toml sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml sudo systemctl restart containerd sudo systemctl enable containerd
Kubernetes v1.33のコマンドをインストールします。
sudo mkdir -p -m 755 /etc/apt/keyrings curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.33/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.33/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list sudo apt update sudo apt install -y kubelet kubeadm kubectl sudo apt-mark hold kubelet kubeadm kubectl
kubeadmの設定ファイルを下記の通り作成します。
apiVersion: kubeadm.k8s.io/v1beta4 kind: ClusterConfiguration kubernetesVersion: v1.33.2 networking: serviceSubnet: "10.96.0.0/12" podSubnet: "192.168.0.0/16" dnsDomain: "cluster.local" # Kubernetes v1.33以前のバージョンはDRAを有効にするため下記の設定が必要 apiServer: extraArgs: - name: runtime-config value: "resource.k8s.io/v1beta1=true" - name: feature-gates value: "DynamicResourceAllocation=true" controllerManager: extraArgs: - name: feature-gates value: "DynamicResourceAllocation=true" scheduler: extraArgs: - name: feature-gates value: "DynamicResourceAllocation=true" --- apiVersion: kubelet.config.k8s.io/v1beta1 kind: KubeletConfiguration featureGates: DynamicResourceAllocation: true
Kubernetes v1.33では、DRAが正式リリース前のため、feature-gatesによる明示的な有効化が必要です。v1.34以降は、上記apiServer以降の設定は不要となります。
下記のコマンドでクラスタを作成します。
sudo kubeadm init --config kubeadm-config.yaml
出力される内容に従って、クラスタにアクセスするためのkubeconfigファイルをコピーします。
mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config
以下のエラーが発生した場合、カーネルモジュールおよびパラメーターが正しく設定されているかどうか確認してください。
$ sudo kubeadm init --pod-network-cidr=192.168.0.0/16 [init] Using Kubernetes version: v1.33.1 [preflight] Running pre-flight checks error execution phase preflight: [preflight] Some fatal errors occurred: [ERROR FileContent--proc-sys-net-ipv4-ip_forward]: /proc/sys/net/ipv4/ip_forward contents are not set to 1 [preflight] If you know what you are doing, you can make a check ? non-fatal with `--ignore-preflight-errors=...` To see the stack trace of this error execute with --v=5 or higher
ネットワークプラグイン(Calico)のインストール
Podがネットワーク接続できるように、Calicoをインストールします。
kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.0/manifests/calico.yaml
今回は1台構成なので、作成されたノードはコントロールプレーンとして認識され通常のPodをデプロイすることはできません。作成したPodをデプロイするワーカーとしても利用できるように、コントロールプレーンのTaintを解除します。
kubectl taint nodes $(kubectl get nodes -o name | cut -d/ -f2) node-role.kubernetes.io/control-plane-
既にコントロールプレーンのノードが存在し、そのノードにワーカーとして追加する場合は、上記の設定は不要です。
Persistent Volume Claim(PVC)をノードのローカルボリュームで利用できるようにlocal-path-provisionerをインストールします。
kubectl apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/v0.0.31/deploy/local-path-storage.yaml
以下のコマンドでlocal-path StorageClassをデフォルト(既定)として設定します。
kubectl patch storageclass local-path -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
KubernetesのパッケージをインストールするためのHelmをインストールします。
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | sudo bash
Ingressを利用できるように、Traefikをインストールします。
まず、下記のTraefikのHelmチャート用の設定ファイルを作成します。
ports: web: hostPort: 80 websecure: hostPort: 443 gateway: enabled: true listeners: web: namespacePolicy: All providers: kubernetesGateway: enabled: true
Ingressの他、Gateway APIも有効にしています。下記の手順でインストールを実行します。
helm repo add traefik https://traefik.github.io/charts helm repo update helm upgrade -i traefik traefik/traefik --version 36.3.0 -n traefik --create-namespace -f traefik-values.yaml
ここから、GPUの設定をしていきます。
GPUに必要なコンポーネントとともに、NVIDIA GPU OperatorとDRA for NVIDIA GPUsを下記の手順でインストールします。
helm repo add nvidia https://helm.ngc.nvidia.com/nvidia helm repo update kubectl create namespace gpu-operator helm install gpu-operator nvidia/gpu-operator \ --version=v25.3.1 \ -n gpu-operator \ --set driver.enabled=true \ --set toolkit.enabled=true \ --set devicePlugin.enabled=false \ --set cdi.enabled=true
従来のDevice Pluginの代わりにDRAを利用するので、devicePlugin.enabledはfalseに設定します。
次に、下記のコマンドでDRA for NVIDIA GPUsをインストールします。
helm upgrade -i my-nvidia-dra-driver-gpu nvidia/nvidia-dra-driver-gpu --version 25.3.0-rc.5 --set gpuResourcesEnabledOverride=true --create-namespace -nnvidia-dra-driver-gpu --set nvidiaDriverRoot=/run/nvidia/driver --set cdi.enabled=true
下記のコマンドでインストール結果を確認します(GPU関連のPodのみ表示)。
ubuntu@node1a-1:~$ kubectl get pods -A NAMESPACE NAME READY STATUS RESTARTS AGE gpu-operator gpu-feature-discovery-wsq8r 1/1 Running 0 2m14s gpu-operator gpu-operator-857fc9cf65-xxb5x 1/1 Running 0 13m gpu-operator gpu-operator-node-feature-discovery-gc-86f6495b55-gmtx8 1/1 Running 0 13m gpu-operator gpu-operator-node-feature-discovery-master-694467d5db-w7qtf 1/1 Running 0 13m gpu-operator gpu-operator-node-feature-discovery-worker-77qng 1/1 Running 0 13m gpu-operator nvidia-container-toolkit-daemonset-4jn6l 1/1 Running 0 7m38s gpu-operator nvidia-cuda-validator-dzl7c 0/1 Completed 0 2m3s gpu-operator nvidia-dcgm-exporter-8p2lb 1/1 Running 0 2m14s gpu-operator nvidia-driver-daemonset-hj98m 1/1 Running 0 2m14s gpu-operator nvidia-mig-manager-kqlqb 1/1 Running 0 2m14s gpu-operator nvidia-operator-validator-ft6dr 1/1 Running 0 2m14 ... nvidia-dra-driver-gpu nvidia-dra-driver-gpu-controller-589d5759f9-bwpfz 1/1 Running 0 1m5s nvidia-dra-driver-gpu nvidia-dra-driver-gpu-kubelet-plugin-z7dbp 2/2 Running 0 1m5s ...
次のようなNVIDIA Operatorのコンポーネントが実行されていることが確認できます。
コンポーネント名 | 説明 |
---|---|
gpu-operator | GPU Operatorのコア部分であり、他のGPU関連コンポーネントの設定とデプロイを管理 |
gpu-feature-discovery | GPUに関する詳細な情報を検出し、その情報をKubernetesノードのラベルとして公開する。これにより、GPUの種類や機能に基づいてワークロードをスケジュール |
gpu-operator-node-feature-discovery-gc | Node Feature Discovery(NFD)のガベージコレクションを担当。NFDによって作成された不要なリソースを削除 |
gpu-operator-node-feature-discovery-master | NFDのコントローラー。各ノードから検出されたハードウェア情報を集約し、KubernetesのノードにGPU用のラベルを付与 |
gpu-operator-node-feature-discovery-worker | NFDのワーカー。各ノード上で実行され、そのノードのハードウェア(GPUを含む)に関する情報を検出し、マスターコンポーネントに送信 |
nvidia-container-toolkit-daemonset | コンテナランタイムがGPUデバイスにアクセスできるようにするためのツールキットをインストールし、コンテナでGPUを利用可能に設定 |
nvidia-cuda-validator | GPUの設定が正しく行われていることを確認 |
nvidia-dcgm-exporter | NVIDIA DCGMからGPUのメトリクス(使用率、温度など)を抽出し、Prometheus形式のメトリクスとして公開 |
nvidia-mig-manager | MIG機能を管理するためのコンポーネント。MIGが有効なGPU上で、GPUを複数の独立したインスタンスに分割し、それぞれを異なるワークロードに割り当て |
nvidia-operator-validator | 各コンポーネントの動作を検証し、問題があればエラーを出力 |
nvidia-dra-driver-gpu-controller | GPUリソースの割り当て、解除、状態管理を制御 |
nvidia-dra-driver-gpu-kubelet-plugin | 各ノード上で動作し、GPUの初期化、割り当てを実施 |
正しくインストールされれば、下記のコマンドで各ノードで利用できるGPUを確認できます。
$ kubectl get resourceslices NAME NODE DRIVER POOL AGE node1a-1-compute-domain.nvidia.com-vk6h5 node1a-1 compute-domain.nvidia.com node1a-1 2m25s node1a-1-gpu.nvidia.com-h82tn node1a-1 gpu.nvidia.com node1a-1 2m25s
ResourceSliceリソースは、GPUの管理単位です。compute-domain.nvidia.comはMIGやMPSなどでGPUをリソース分割したときに利用されます。
今回は、通常の分割なのでgpu.nvidia.comの方を利用します。下記のコマンドで認識されたGPUの詳細を確認できます。
$ kubectl describe resourceslices node1a-1-gpu.nvidia.com-4j5ll Name: node1a-1-gpu.nvidia.com-4j5ll Namespace: Labels: <none> Annotations: <none> API Version: resource.k8s.io/v1beta1 Kind: ResourceSlice Metadata: Creation Timestamp: 2025-06-26T01:38:41Z Generate Name: node1a-1-gpu.nvidia.com- Generation: 1 Owner References: API Version: v1 Controller: true Kind: Node Name: node1a-1 UID: b490538b-85b2-49b0-b7f4-347c1a76563f Resource Version: 8914636 UID: 3d87eeb8-d80b-4706-9936-cef331d42d52 Spec: Devices: Basic: Attributes: Architecture: String: Ampere Brand: String: Nvidia Cuda Compute Capability: Version: 8.0.0 Cuda Driver Version: Version: 12.8.0 Driver Version: Version: 570.148.8 Index: Int: 5 Minor: Int: 7 Product Name: String: NVIDIA A100-PCIE-40GB Type: String: gpu Uuid: String: GPU-7274d657-2e08-df43-f06f-d4be16816916 Capacity: Memory: Value: 40Gi Name: gpu-5 Basic: Attributes: Architecture: String: Ampere Brand: String: Nvidia Cuda Compute Capability: Version: 8.0.0 Cuda Driver Version: Version: 12.8.0 Driver Version: Version: 570.148.8 Index: Int: 6 Minor: Int: 5 Product Name: String: NVIDIA A100-PCIE-40GB Type: String: gpu Uuid: String: GPU-d0f7b6db-6dcf-b4f2-f1a9-858a807e855e Capacity: Memory: Value: 40Gi Name: gpu-6 Basic: Attributes: Architecture: String: Ampere Brand: String: Nvidia Cuda Compute Capability: Version: 8.0.0 Cuda Driver Version: Version: 12.8.0 Driver Version: Version: 570.148.8 Index: Int: 7 Minor: Int: 4 Product Name: String: NVIDIA A100-PCIE-40GB Type: String: gpu Uuid: String: GPU-2945b7e8-3bb0-14c1-99b9-986b6ce57629 Capacity: Memory: Value: 40Gi ...(以下略)...
このコマンドにより、ノードで認識されている各GPUのモデル(NVIDIA A100-PCIE-40GB)、ドライバやCUDAのバージョン、メモリなどを確認できます。
GPU Operatorは、NFDと呼ばれるコンポーネントが、ノードのGPUを検出し、ラベルを付与します。付与されたラベルに応じて、DaemonSetのPodが該当するノードに配置され、コンポーネントのインストールやGPUメトリクスの取得が行われます。
また、GPU Operator ValidatorがGPU Operator各コンポーネントが正しく動作しているかどうかを検証し、CUDA Validatorがノード上のGPUの設定が正しく行われているかどうかを確認します。
この仕組みを理解しておくことで、トラブル発生時の原因切り分けが容易になります。
さて、GPUのセットアップが終わったら、GPUをPodから利用してみましょう。
今回、GPUのアサインにDRAを利用していますが、DRAでは、下記の3つのリソースが利用されます。CSI(Container Storage Interface)で対応する機能を併記しておくので、参考にしてください。
リソース | 説明 | CSIにおける対応機能 |
---|---|---|
DeviceClass | 要求するリソースのデバイスを定義する。種類を定義する。NVIDIA GPU DRA driverでは、gpu.nvidia.com と mig.nvidia.com(MIG用)が提供される | StorageClass |
ResourceClaimTemplate | リソース要求を定義したテンプレート。クラウドにおけるインスタスタイプに似ている | volumeClaimTemplate |
ResourceClaim | Podに割り当てられる実際のリソース、Pod作成時にResourceClaimTemplateを元に自動的にリソースの空きノードに作成され、Podに割り当てられる。もしくはPersistentVolumeClaimのように手動で作成することもできる | PersistentVolumeClaim |
まずは、Podに割り当てるリソースを定義するResourceClaimTemplateを作成します。
apiVersion: resource.k8s.io/v1beta1 kind: ResourceClaimTemplate metadata: name: single-gpu namespace: test spec: spec: devices: requests: - name: gpu deviceClassName: gpu.nvidia.com
Kubernetes v1.34以降の場合は、apiVersionを下記のようにv1beta1ではなくv1としてください。
ResourceClaimTemplateリソースを作成します。
$ kubectl create ns test $ kubectl apply -f rct-test.yaml $ kubectl get resourceclaimtemplate NAME AGE single-gpu 10s
ResourceClaimTemplateはあくまでテンプレートなので、ResourceClaimTemplateリソースを作成した時点ではGPUはアサインされません。
作成したテンプレートはアサインするGPU個数やタイプ(MIGの利用)などに応じて作成します。テンプレートは複数のPodで利用可能です。
ResourceClaimTemplateを利用したPodは、次のようにResourceClaimを利用して定義します。
apiVersion: v1 kind: Pod metadata: name: gpu-test namespace: test spec: restartPolicy: OnFailure containers: - name: gpu-container image: nvcr.io/nvidia/cuda:12.9.0-base-ubuntu24.04 command: ["/bin/bash", "-c"] args: ["nvidia-smi -L && sleep 3600"] resources: claims: - name: gpu # 定義したsingle-gpuのアサイン resourceClaims: - name: gpu resourceClaimTemplateName: single-gpu
下記のコマンドでPodを作成します。
$ kubectl apply -f gpu-test.yaml
Pod作成後、以下のようにGPUを割り当てるResourceClaimが作成されていることを確認できます。kubectl describeの結果から、node1a-1上のGPUが割り当てられていることも確認できます。
$ kubectl -n test get resourceclaim NAME STATE AGE gpu-test-gpu-ccxfx allocated,reserved 56s $ kubectl -n test describe resourceclaim gpu-test-gpu-ccxfx Name: gpu-test-gpu-ccxfx Namespace: test Labels: <none> Annotations: resource.kubernetes.io/pod-claim-name: gpu API Version: resource.k8s.io/v1beta1 Kind: ResourceClaim Metadata: Creation Timestamp: 2025-07-08T00:32:31Z Finalizers: resource.kubernetes.io/delete-protection Generate Name: gpu-test-gpu- Owner References: API Version: v1 Block Owner Deletion: true Controller: true Kind: Pod Name: gpu-test UID: c172503c-6010-47c2-bc8f-2ccb3455d53c Resource Version: 11761568 UID: 776facfe-de91-4769-91df-b794edf38300 Spec: Devices: Requests: Allocation Mode: ExactCount Count: 1 Device Class Name: gpu.nvidia.com Name: gpu Status: Allocation: Devices: Results: Admin Access: <nil> Device: gpu-5 Driver: gpu.nvidia.com Pool: node1a-1 Request: gpu Node Selector: Node Selector Terms: Match Fields: Key: metadata.name Operator: In Values: node1a-1 Reserved For: Name: gpu-test Resource: pods UID: c172503c-6010-47c2-bc8f-2ccb3455d53c Events: <none>
今回は、ResourceClaimはテンプレートから作成しましたが、手動で作成することもできます。Pod実行時のGPU不足を避けるには、手動であらかじめResourceClaimを作成しておくとよいでしょう。
Podのログを確認すると、nvidia-smiでGPUを認識していることが分かります。
$ kubectl logs -n test gpu-test GPU 0: NVIDIA A100-PCIE-40GB (UUID: GPU-7274d657-2e08-df43-f06f-d4be16816916)
利用可能なGPU総数、利用中のGPU数、利用可能なGPU数は以下のコマンドで確認できます。
$ USE=$(kubectl get resourceclaims -A -o json | jq '[.items[].status.allocation.devices.results | length]| add') $ TOTAL=$(kubectl get resourceslices -o json | jq '[.items[] | select(.spec.driver == "gpu.nvidia.com") | .spec.devices | length] | add') $ echo "Total GPU: " $TOTAL $ echo "Used GPU: " $USE Total GPU: 8 Used GPU: 7
DRA for NVIDIA GPUsのGitHubにてサンプルコードが公開されています。MPSやTime Slicingを用いて複数のPodで1つのGPUを共有したり、MIGでGPUを論理分割したりするコードが公開されていますので、興味がある人は参照してみてください。
ここからはメトリクス、ログ、トレーシングのオブザーバビリティ3要素を包括的に扱えるGrafana LGTMと、これと連携するGrafana Kubernetes Monitoringを利用して、KubernetesクラスタとGPUのモニタリング環境を構築します。
Grafana LGTMとKubernetes Monitoringを利用することにより、耐障害性とスケーラビリティに優れたモニタリング環境を簡単に実装できます。
今回、構築する構成の概要は、下記の図のようになります。
Kubernetes Monitoringは、オブザーバビリティにおけるシグナル(ログ、メトリクス、トレーシング)を収集するコンポーネントで、収集するシグナルごとに、alloy-metrics(メトリクス)、alloy-logs(ログ)、alloy-singleton(イベント)がデプロイされます。
GPUのメトリクスはGPU Operatorによりデプロイされるdcgm exporterにより取得されますが、本稿では、dcgm exporter専用のAlloy(alloy dcgm sender)を利用しMimirへ転送します。
LGTMの各コンポーネントはマイクロサービスにより実装され、複数のPodの組み合わせにより構成されます。その詳細については割愛します。
Kubernetes Monitoringは、Alloyを利用して各種情報を収集してLGTMに送信しています。以前は、ログ収集のPromtailや総合的なオブザーバビリティエージェントであるGrafana Agentが提供されていましたが、2025年7月時点で、新規機能についてはAlloyとして開発が継続されています。
今回は、Grafana Kubernetes Monitoringのアップグレードや構成変更の影響を受けないよう、GPUのメトリクスを送信するAlloyを個別にデプロイする構成を取りました。
以下、下記の手順でモニタリング環境の構築について紹介します。
最初にモニタリングするためのLGTMスタックを構築します。下記のコマンドを実行し、YAMLファイルを準備します。
helm repo add grafana https://grafana.github.io/helm-charts helm repo update
loki: loki: structuredConfig: # メタデータの構造化を行いGrafanaで読めるようにする limits_config: allow_structured_metadata: true # minio上のログデータの圧縮 compactor: shared_store: s3 working_directory: /var/loki/compactor storage_config: # インデックスをローカルのboltdbに保存しminioに同期 boltdb_shipper: active_index_directory: /var/loki/index cache_location: /var/loki/cache shared_store: s3 # S3ストレージをminioに設定 aws: bucketnames: loki-chunks endpoint: lgtm-minio:9000 access_key_id: ${MINIO_ACCESS_KEY_ID} secret_access_key: ${MINIO_SECRET_ACCESS_KEY} s3forcepathstyle: true insecure: true # boltdbの設定 schema_config: configs: - from: "2024-01-01" store: boltdb-shipper object_store: s3 schema: v12 index: prefix: index_ period: 24h extraEnv: # minioのシークレットの設定 - name: MINIO_ACCESS_KEY_ID valueFrom: secretKeyRef: name: lgtm-minio key: rootUser - name: MINIO_SECRET_ACCESS_KEY valueFrom: secretKeyRef: name: lgtm-minio key: rootPassword # GrafanaのIngressを設定 grafana: ingress: enabled: true hosts: - grafana.local
上記のYAMLファイルを使って、LGTMスタックをデプロイします。
$ helm upgrade -i lgtm grafana/lgtm-distributed --version 2.1.0 -n monitoring --create-namespace -f lgtm-values.yaml
インストールしたGrafanaのadminのユーザーのパスワードは、下記のコマンドで取得できます。
$ kubectl get secret -n monitoring lgtm-grafana -o jsonpath="{.data.admin-password}" | base64 --decode ; echo wOUZ5u7Gx....(adminのパスワード)
Grafanaにアクセスする端末のhostsファイルに、Ingressに指定したGrafanaのホスト名(ここではgrafana.local)を記述します。
<K8sをインストールしたホストのIPアドレス> grafana.local [例] 192.168.1.46 grafana.local
端末からブラウザでhttp://grafana.local/にアクセスするとGrafanaが表示されます。先ほど取得したadminパスワードを利用して、adminユーザーでログインしてください。
左のナビゲーションペインから「Connections」->「Data sources」を選択すると、Loki、Mimir、Tempoがデータソースとして設定されていることが確認できます。右側の「Explore」から各データソースのクエリを実行できます。
LGTMのセットアップが完了したら、KubernetesをモニタリングするGrafana Kubernetes MonitoringをHelmでインストールします。下記の設定ファイルを準備します。
cluster: name: kubernetes clusterMetrics: enabled: true clusterEvents: enabled: true alloy-metrics: enabled: true podLogs: enabled: true alloy-logs: enabled: true alloy-singleton: enabled: true destinations: - name: hostedMetrics type: prometheus url: http://lgtm-mimir-nginx/api/v1/push - name: localPrometheus type: prometheus url: http://lgtm-mimir-nginx/api/v1/push - name: hostedLogs type: loki url: http://lgtm-loki-gateway/loki/api/v1/push
Helmチャートをインストールします。
helm upgrade -i k8s-monitoring grafana/k8s-monitoring --version 3.1.0 -n monitoring -f k8s-monitoring-values.yaml
Grafana LGTMでGPUのモニタリングを行うには、AlloyでDCGM Exporterからメトリクスを取得し、Mimirに送信します。送信する設定を記述した下記のAlloy Helmチャートの設定ファイルを作成します。
alloy: configMap: create: true name: alloy-config key: config.alloy content: |- // メトリクスの書き込み先を定義 prometheus.remote_write "mimir" { endpoint { url = "http://lgtm-mimir-nginx/api/v1/push" } } // dcgm-exporterのPodを動的に検出 discovery.kubernetes "dcgm_exporter_pods" { role = "pod" namespaces { names = ["gpu-operator"] } selectors { role = "pod" label = "app=nvidia-dcgm-exporter" // label = "app.kubernetes.io/name=dcgm-exporter" } } // スクレイプの設定 prometheus.scrape "dcgm_scrape" { job_name = "nvidia/dcgm_exporter" targets = discovery.kubernetes.dcgm_exporter_pods.targets forward_to = [prometheus.remote_write.mimir.receiver] }
下記のコマンドでAlloyをデプロイします。
helm upgrade -i alloy-dcgm-sender grafana/alloy --version 1.1.2 -n monitoring -f alloy-dcgm-sender.yaml
Grafanaのサイトでは、DCGM Exporterに対応したダッシュボードが幾つか公開されています。ここでは、その中でも良さそうなBetter NVIDIA DCGM Dashboardを紹介します。
先ほど開いたGrafanaのサイト(http://grafana.local/)をWebブラウザで開きます。
画面上部の「+」から「Import」を選択しダッシュボードをインポートします。
ダッシュボードID「22515」を入力して「Load」をクリックします。次にデータソースにMimirを選択します。ダッシュボードのインポートに成功すると、下記のようなダッシュボードが表示されます。
NVIDIA GPUを活用した生成AI基盤をKubernetesで構築し、そのモニタリング環境を整備する一連のステップを解説しました。Helmを利用することでインストール作業は簡素化できているものの、全ての手順を見ると、依然として内容は多岐にわたります。
とはいえ、いったん構築してしまえば、PodへのGPU割り当ては自動化され、GPU稼働状況もモニタリングできることから、生成AI基盤の運用環境として運用上のメリットがあるといえるでしょう。
今回は、サーバ1台の環境で構築しましたが、GPU Operatorを利用していれば、GPUを自動検出するようになるため、サーバ数が増えたクラスタにノードを追加することも容易となります。
次回は、本稿で構築した生成AI基盤で「gpt-oss」を動かしながら、その運用をAIで効率化するアプローチも交えて解説します。次回もお楽しみに!
Copyright © ITmedia, Inc. All Rights Reserved.