☸️

WSL上のKubernetesでホストマシンのGPUを使ってみる

2023/02/25に公開

はじめに

みなさんはKubernetes上でGPUをつかったことはありますか?自分はありません。
そもそもKubernetesを個人利用している方が少ないと思うので、仕事で使う機会が無ければ無いと思います。GPUも個人利用している方はゲームか機械学習コンペ目的などでしょう。そういったわけでGPU on Kubernetesなんてお金持ちの遊びみたいなことしたことある方はそうそう居ないだろう思います。今回は自宅PCで簡単にGPU on Kubernetesをやってみてとりあえずこの世界に入門してみようと思います。

環境

環境はWSL(ubuntu20.04)にKubernetesを建てて、GPUは汎用nvidia GPU(Geforce RTXシリーズ)を使用します。

WSL上のコンテナからGPUを使う

WSL上のKubernetesからGPUを使う前にまずWSLから直接(or コンテナから)GPUが使えるようにしないと始まらないので、そのためのセッティングを行います。
CUDA on WSLのページを見てみるとWSL上でGPU(CUDA)を使うためのかなり詳細なガイドが載っています。このページに従って各コンポーネントを用意していきます。図によるとCUDA on WSLに必要な手順は

  1. ホストマシンにNVIDIA Windows Driverを入れる
  2. WSLにNVIDIA Container Toolkitを入れる

だとわかります。

ホストマシンにNVIDIA Windows Driverを入れる

今回初めてGPUを使用する場合でない限り基本的には既にインストールされているかと思いますが、ちょうどいい機会なのでversion updateを兼ねて新しいものを入れ直します。
https://www.nvidia.com/Download/index.aspx こちらのページでご自身のホストマシン(windows)の環境に合わせてインストールしてください。
どうやらドライバーのインストールと同時に/usr/lib/wsl/lib配下にnvidia-smi等インストールしてくれるみたいです。早速WSLでnvidia-smiコマンドを実行してみます。

$ nvidia-smi
Sat Feb 25 14:22:03 2023
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 525.89.02    Driver Version: 528.49       CUDA Version: 12.0     |
|-------------------------------+----------------------+----------------------+
| 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 GeForce ...  On   | 00000000:01:00.0  On |                  N/A |
|  0%   42C    P8    15W / 240W |    571MiB /  8192MiB |      1%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+

ご自身のGPU(今回はNVIDIA GeForce ...)が確認出来たらokです。

WSLにNVIDIA Container Toolkitを入れる

setting-up-nvidia-container-toolkitを参照してnvidia-container-toolkitをインストールします。こちらはwsl上で行ってください。 Kubernetesで管理されていないピュアなコンテナを使用してnvidia-smiができることを確認しておきます。ここではDockerDesktopが用意してくれているdockerを使用していますがKubernetesでのランタイムはcontainerdなので、もちろんnerdctlを使用しても大丈夫です。(参照: https://github.com/containerd/nerdctl/blob/main/docs/gpu.md)

$ docker run --rm --gpus all nvidia/cuda:11.6.2-base-ubuntu20.04 nvidia-smi

ランタイムにGPUを使用するためには--gpusフラグが必要になります。
参考: https://docs.docker.jp/config/containers/resource_constraints.html?highlight=gpu#gpu
さてこれで無事GPUを認識できていれば大丈夫です。あとはk8sを用意するだけです。

WSL上にk8sを建てる

初めてだとやや大変だと思いますが基本的には以下の手順です。今回は詳細は省きます。
参考: https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/, https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/

  1. prepare container runtime (今回はcontainerdを使用します)
  2. install kubeadm, kubelet, kubectl
  3. kubeadm init
  4. install cni (今回はweaveを使用します。)

適当なpodをデプロイしてみて動けばokです。今回はcontrol-planeの1ノードだけなのでpodがスケジュールされるようtaintを外すのを忘れないようにします。
nginxをデプロイしてみます。kubectl run nginx --image=nginx

NAMESPACE     NAME                                      READY   STATUS    RESTARTS   AGE
default       nginx                                     1/1     Running   0          78s
kube-system   coredns-787d4945fb-2hzhm                  1/1     Running   0          3m26s
kube-system   coredns-787d4945fb-n4hsz                  1/1     Running   0          3m26s
kube-system   etcd-desktop-2uoplus                      1/1     Running   1          3m35s
kube-system   kube-apiserver-desktop-2uoplus            1/1     Running   1          3m34s
kube-system   kube-controller-manager-desktop-2uoplus   1/1     Running   2          3m35s
kube-system   kube-proxy-qn8lx                          1/1     Running   0          3m27s
kube-system   kube-scheduler-desktop-2uoplus            1/1     Running   2          3m35s
kube-system   weave-net-d8hwd                           2/2     Running   0          2m3s

okです。最後にKubernetes上でGPUを使用するための設定を行います。

GPUノードの準備

参考: https://kubernetes.io/docs/tasks/manage-gpus/scheduling-gpus/
shceduling-gpusのページに従って必要な準備をしていきます。
https://github.com/NVIDIA/k8s-device-plugin#preparing-your-gpu-nodes

  1. install nvidia-container-toolkit (すでに終わっているので飛ばします)
  2. configure containerd (https://github.com/NVIDIA/k8s-device-plugin#configure-containerd)
  3. deploy gpu-device-plugin daemonset (https://github.com/NVIDIA/k8s-device-plugin#enabling-gpu-support-in-kubernetes)

deploy gpu device plugin Daemonset

今回はnvidiaのgpuを使用しているのでnvidiaのgpu device pluginをデプロイします。

$ kubectl create -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v0.13.0/nvidia-device-plugin.yml

logを見てみます。

$ kubectl logs nvidia-device-plugin-daemonset-vcklb
...
...
2023/02/25 08:44:36 Retreiving plugins.
2023/02/25 08:44:36 Detected NVML platform: found NVML library
2023/02/25 08:44:36 Detected non-Tegra platform: /sys/devices/soc0/family file not found
2023/02/25 08:44:36 No devices found. Waiting indefinitely.

No device foundになってます。。。。
もちろんgpu-podをdeployしてみても

Warning  FailedScheduling  15s (x2 over 97s)  default-scheduler  0/1 nodes are available: 1 Insufficient nvidia.com/gpu. preemption: 0/1 nodes are available: 1 No preemption victims found for incoming pod..

のようなエラーがでてきてしまいます。。
とりあえずnvidia-device-pluginのpodでnvidia-smiしてみると

$ kubectl exec nvidia-device-plugin-daemonset-vcklb -- nvidia-smi
Sat Feb 25 08:55:26 2023
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 525.89.02    Driver Version: 528.49       CUDA Version: N/A      |
|-------------------------------+----------------------+----------------------+
| 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 GeForce ...  On   | 00000000:01:00.0  On |                  N/A |
|  0%   43C    P8    16W / 240W |    636MiB /  8192MiB |      1%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+

ちゃんと認識できてます。うーんdevice-pluginで何か設定する必要がありそうですが疲れてしまったのでいったんこれで良しとします。。。
ちなみにrunning-gpu-jobsのページにWARNING: if you don't request GPUs when using the device plugin with NVIDIA images all the GPUs on the machine will be exposed inside your container. とあるようにresourceのrequest,limitを指定しなければpodは普通にスケジューリングされるので、もしそのノードにGPUがあれば一応GPUは使えます。

おわりに

解決策見つけ次第加筆します。もし原因がわかる・わかった方がいたら是非教えてください。
環境構築って大変。おわり。

追記 2023/03/28

どうやらこちらの記事にあるように、nvidia-device-plugingがWSLに対応していないのが原因のようでした : https://zenn.dev/rrrrrrryo/articles/0f86ca44b7044b

Discussion