delta_lt_0

delta_lt_0

人生苦短 及时行乐

KubeVirt 仮想マシン GPU パススルー

NVIDIA GPU を Kubernetes に公開する#


k8s は Pod に提供するリソースとして Memory や CPU をデフォルトで提供しますが、GPU は含まれていません。しかし、K8S は自分でDevice Pluginを開発してリソースを公開することを許可しています。大まかな流れは次のとおりです:

image

Device Pluginのドキュメントに記載されている方法で開発すれば、k8s 内で必要なリソースを Pod に割り当てることができます。

kubevirt-gpu-device-pluginはそのようなプラグインで、DaemonSet としてデプロイされ、NVIDIA GPU を割り当て可能なリソースとして kubelet に公開します。この nvidia-kubevirt-gpu-dp をクラスターにインストールすると、以下のような効果が見られます:

root@node01:~# kubectl describe node node01
Name:               node01
Roles:              worker
CreationTimestamp:  Thu, 24 Nov 2022 15:13:21 +0800
Addresses:
  InternalIP:  172.16.33.137
  Hostname:    node01
Capacity:
  cpu:                                       72
  ephemeral-storage:                         921300812Ki
  hugepages-1Gi:                             0
  hugepages-2Mi:                             0
  memory:                                    131564868Ki
  nvidia.com/TU106_GEFORCE_RTX_2060_REV__A:  4
  pods:                                      110
Allocatable:
  cpu:                                       71600m
  ephemeral-storage:                         921300812Ki
  hugepages-1Gi:                             0
  hugepages-2Mi:                             0
  memory:                                    127462015491
  nvidia.com/TU106_GEFORCE_RTX_2060_REV__A:  4
  pods:                                      110
...

上記の出力結果では、Capacity において node01 ノードに 4 枚のnvidia.com/TU106_GEFORCE_RTX_2060_REV__Aグラフィックカードがあることが示され、Allocatable では node01 ノードに 4 枚のnvidia.com/TU106_GEFORCE_RTX_2060_REV__Aグラフィックカードが Pod に割り当て可能であることが示されています。

このプラグインの Pod が実行されると、各ノードの/var/lib/kubelet/device-plugins/ディレクトリにkubevirt-TU106_GEFORCE_RTX_2060_REV__A.sock=という名前のソケットファイルが新たに作成されます。

root@node01:~# ll /var/lib/kubelet/device-plugins/
total 44
drwxr-xr-x 2 root root  4096 Dec  8 19:54 ./
drwx------ 8 root root  4096 Nov 24 15:13 ../
-rw-r--r-- 1 root root     0 Dec  5 09:13 DEPRECATION
-rw------- 1 root root 35839 Dec  8 19:54 kubelet_internal_checkpoint
srwxr-xr-x 1 root root     0 Dec  5 09:13 kubelet.sock=
srwxr-xr-x 1 root root     0 Dec  8 19:54 kubevirt-kvm.sock=
srwxr-xr-x 1 root root     0 Dec  8 19:54 kubevirt-sev.sock=
srwxr-xr-x 1 root root     0 Dec  8 19:52 kubevirt-TU106_GEFORCE_RTX_2060_REV__A.sock=
srwxr-xr-x 1 root root     0 Dec  8 19:54 kubevirt-tun.sock=
srwxr-xr-x 1 root root     0 Dec  8 19:54 kubevirt-vhost-net.sock=

kubelet はこのソケットとの通信を通じて、デバイスプラグインの登録やリソースの割り当てなどの操作を行います。これにより、Pod を作成する際に resource request/limit でnvidia.com/TU106_GEFORCE_RTX_2060_REV__Aというリソースを要求できるようになります。

注 1: 拡張リソースのリソース使用量は整数でなければなりませんが、現在 nvidia-kubevirt-gpu-dp は 1000m = 1 core、500m = 0.5 core のような効果を提供していません。現在のところ「1」は 1 枚のグラフィックカードを意味します。

注 2: nvidia-kubevirt-gpu-dp をデプロイする前に、クラスターのノードの grub で iommu を有効にするなどの操作が必要です。これにより、GPU をゲストに直通させる能力が得られます。

注 3: 残りの kubevirt-kvm.sock などのファイルは、virt-handler 内のデバイスプラグインによって作成されたものです。

KubeVirt VM で GPU を使用する#


VM を作成するには、以下のように nvidia.com/TU106_GEFORCE_RTX_2060_REV__A グラフィックカードを使用することを指定します:

apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
  name: vm-with-gpu
  namespace: cxl
spec:
  runStrategy: RerunOnFailure
  template:
    spec:
      domain:
        cpu:
          cores: 1
        devices:
          disks:
          # ...
          gpus:
          - deviceName: nvidia.com/TU106_GEFORCE_RTX_2060_REV__A
            name: gpu0
        machine:
          type: q35
        resources:
          requests:
            memory: 1Gi
      networks:
      # ...
      volumes:
      # ...

virt-controller は vmi の CREATE を監視し、それに対応する Pod(virt-launcher)を構築します。ソースコードの一部はこちらで確認できます。最終的に構築された virt launcher Pod の yaml は次のようになります:

apiVersion: v1
kind: Pod
metadata:
  labels:
    kubevirt.io: virt-launcher
    vm.kubevirt.io/name: vm-with-gpu
  name: virt-launcher-vm-with-gpu-vvl4q
  namespace: cxl
spec:
  automountServiceAccountToken: false
  containers:
  - command:
    - ...
    image: quay.io/kubevirt/virt-launcher:v0.54.0
    imagePullPolicy: IfNotPresent
    name: compute
    resources:
      limits:
        devices.kubevirt.io/kvm: "1"
        devices.kubevirt.io/tun: "1"
        devices.kubevirt.io/vhost-net: "1"
        nvidia.com/TU106_GEFORCE_RTX_2060_REV__A: "1"
      requests:
        devices.kubevirt.io/kvm: "1"
        devices.kubevirt.io/tun: "1"
        devices.kubevirt.io/vhost-net: "1"
        cpu: 100m
        ephemeral-storage: 50M
        memory: 2262Mi
        nvidia.com/TU106_GEFORCE_RTX_2060_REV__A: "1"
...

仮に VM がノード node05 に割り当てられ、node05 に 4 枚の nvidia.com/TU106_GEFORCE_RTX_2060_REV__A グラフィックカードがある場合、kubevirt はどのグラフィックカードを VM に割り当てるのでしょうか?

VM Pod 内のコンテナが起動する際、kubelet は nvidia-kubevirt-gpu-dp のAllocate()インターフェースを呼び出します。このインターフェースは、レスポンスデータ内のenvに割り当てられた GPU デバイスのアドレスを書き込む必要があります。最終的にはコンテナの環境変数に反映されます。以下のようになります:

root@node01:~# kubectl exec -it virt-launcher-vm-with-gpu-vvl4q -n cxl -- bash
bash-4.4# env
PCI_RESOURCE_NVIDIA_COM_TU106_GEFORCE_RTX_2060_REV__A=0000:86:00.0

同型のグラフィックカードを複数申請した場合、環境変数内のアドレスはカンマで区切られます:

root@node01:~# kubectl exec -it virt-launcher-vm-with-gpu-vvl4q -n cxl -- bash
bash-4.4# env
PCI_RESOURCE_NVIDIA_COM_TU106_GEFORCE_RTX_2060_REV__A=0000:86:00.0,0000:af:00.0

つまり、GPU はデバイスプラグインの Allocate インターフェースのロジックによって割り当てられます。

virt-launcher Pod が実行されると、libvirt ドメインの xml を構築し、その際に Pod の env 内の環境変数を読み取り、対応する xml 要素に構築されます。以下のようになります:

<hostdev mode='subsystem' type='pci' managed='yes'>
  <source>
    <address domain='0x0000' bus='0x04' slot='0x00' function='0x0'/>
  </source>
  <address type='pci' domain='0x0000' bus='0x00' slot='0x08' function='0x0'/>
  <alias name='ua-gpu0'>
</hostdev>

これにより、libvirt はホストの PCI デバイス(つまりグラフィックカード)を直接使用できるようになります。

KubeVirt の許可されたホストデバイス#


デフォルトでは、すべてのホストデバイスは仮想マシンで使用できますが、kubevirt は kubevirt CR 内でどのホストデバイスが VM で使用できるかを設定することを許可しています。以下のように設定できます:

...
configuration:
  permittedHostDevices:
    pciHostDevices:
    - pciVendorSelector: "10DE:1F08"
      resourceName: "nvidia.com/GeForce RTX 2060 Rev. A"
      externalResourceProvider: true
    - pciVendorSelector: "8086:6F54"
      resourceName: "intel.com/qat"
    mediatedDevices:
    - mdevNameSelector: "GRID T4-1Q"
      resourceName: "nvidia.com/GRID_T4-1Q"

このように設定した場合、仮想マシンに割り当てる GPU は上記の設定の Selector ルールに従う必要があります。

pciVendorSelector は vendorID(ベンダー)と productID(製品)で構成されており、10DEは Nvidia を、1F08は GeForce RTX 2060 Rev. A という製品を示しています(詳細は: https://pci-ids.ucw.cz/read/PC/10de)。つまり、このホストデバイスは kubevirt によって仮想マシンでの使用が許可されています。

このモデルの GPU に対して、externalResourceProvider: trueは外部デバイスプラグイン(つまり nvidia-kubevirt-gpu-dp)がこのデバイスを管理することを示します。もしこのデバイスのexternalResourceProvider: falseを指定した場合、virt-handler 内のデバイスプラグインマネージャーがこのデバイスを管理し、このデバイスのためにデバイスプラグインを起動します。

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。