11
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

TUNA-JPAdvent Calendar 2023

Day 20

Intel iGPUをパススルーして利用する - Kubernetes Pod編

Last updated at Posted at 2023-12-20

この記事は TUNA-JP Advent Calendar 2023 の12月20日分の投稿記事です。

はじめに

Intel NUC 13ではモバイル向け第13世代Coreプロセッサ(Raptor Lake)がCPUとして利用されています。第12世代Coreプロセッサ(Alder Lake)から、高性能コア(P-Core)と高効率コア(E-Core)の2種類のCPUを搭載するPerformance Hybrid Architectureで構成されており、内蔵GPU(iGPU)としてIris Xe Graphicsが搭載されています。
Intel NUC 13(IntelCore i7-1360P)にESXiをインストールして、Cluster APIが仮想マシンで構成するKubernetesクラスター上でIris Xe (iGPU)を使って、OpenVINOによりStable DiffusionでText-to-Imageを試してみます。

VMware ESXiはIntel コンシューマー向けCPUのPerformance Hybrid Architectureをサポートしていません。本記事の内容はVMwareがサポートする内容ではありません。

本記事は仮想マシン上のKubernetes PodからIntel iGPUを利用する方法を説明しています。仮想マシンから利用する場合はこちらの記事を参照してください。

Image Builderで仮想マシンイメージの作成

Cluster APIでKubernetesクラスターを作成する際に利用するUbuntu仮想マシンイメージはImage Builderによって作成することが可能です。Ubuntuの場合、18.04、20.04、22.04を利用することができますが、最新の22.04もLinuxカーネルとして 5.15 が利用されており、Raptor Lake-PのiGPUに対応していません。Raptor Lakeの-PのiGPUを利用するには、Linuxカーネル 5.19 以降が必要です。

GPUを利用するために、HWEカーネルを使いたいのでImage BuilderのUbuntu 22.04のuser-data.autoinstall.packageslinux-image-generic-hwe-22.04を追加し、ビルド時にHWEカーエネルを追加したカスタムイメージを作成しました。

#cloud-config
autoinstall:
  ssh:
    install-server: true
    allow-pw: true
  # Customize the list of packages installed.
  packages:
    - open-vm-tools
    - linux-image-generic-hwe-22.04

Image Builderを利用してカスタムイメージを作成しました。Image Builderについては@ttani03さんの記事が参考になります。

また、Stable Diffusionを利用するコンテナイメージは各種ライブラリが含まれるためサイズが大きく、ノードのディスクサイズも大きめの80GBにするため、イメージビルド時にオプションPACKER_FLAGS=-var=disk_size="8192"を指定して仮想マシンイメージを作成しました。

$ PACKER_FLAGS=-var=disk_size="8192" IB_OVFTOOL=1 IB_OVFTOOL_ARGS="--allowExtraConfig" make build-node-ova-vsphere-ubuntu-2204

作成されたイメージを利用して、Cluster APIによりKubernetesクラスターを作成しました。

$ ls -lh output/ubuntu-2204-kube-v1.27.2/
total 4.6G
-rw-rw-r-- 1 nos nos 1.3K Dec  9 19:51 packer-manifest.json
-rw-rw-r-- 1 nos nos 2.3G Dec  9 19:51 ubuntu-2204-kube-v1.27.2-disk-0.vmdk
-rw------- 1 nos nos 2.3G Dec  9 19:53 ubuntu-2204-kube-v1.27.2.ova
-rw-rw-r-- 1 nos nos   64 Dec  9 19:54 ubuntu-2204-kube-v1.27.2.ova.sha256
-rw-rw-r-- 1 nos nos  41K Dec  9 19:51 ubuntu-2204-kube-v1.27.2.ovf

ESXi上のIris Xeの認識

ESXi 8.0u1をインストールすると、Iris Xeは「Intel Corporation VGE compatible Controller」として認識されます。このままでもパススルーデバイスとして構成し、仮想マシンに接続することが可能です。

image.png

2023年10月リリースされたESXi 8.0u2以降、「Intel Corporation Raptor Lake-P [Iris Xe Graphics]」として認識されるようになっています。

image.png

ノードに対するGPUの接続

ESXiのデバイスとしてRaptor Lake-P [Iris Xe Graphics]のパススルーを有効にして、ESXi上に作成されたノードにPCIデバイスとしてパススルーデバイスを追加します。メモリーの設定も「すべてのゲスト メモリを予約 (すべてロック)」をチェックします。

image.png

Intel GPU device plugin for Kubernetes

Kubernetes上のPodでIris Xeを利用するには、Intel GPU device plugin for Kubernetesを利用します。DaemonSetとしてGPUプラグインが作成され、ノードでPCIデバイスとしてi915が認識されると、GPUドライバーが有効化されます。

Node Feature Discoveryをインストールします。

kubectl apply -k 'https://github.com/intel/intel-device-plugins-for-kubernetes/deployments/nfd?ref=v0.28.0'

NodeFeatureRuleリソースを作成して、Intel Xeが利用可能なノードにラベルを付与します。

kubectl apply -k 'https://github.com/intel/intel-device-plugins-for-kubernetes/deployments/nfd/overlays/node-feature-rules?ref=v0.28.0'

作成されたNodeFeatureRuleのintel-dp-devicesintel-gpu-platform-labelingにより、PCIデバイスとしてIris Xeが存在するノードにGPU機能とGPUの数等がラベルとして付与されます。

apiVersion: nfd.k8s-sigs.io/v1alpha1
kind: NodeFeatureRule
metadata:
  name: intel-dp-devices
spec:
  rules:
    - labels:
        intel.feature.node.kubernetes.io/gpu: "true"
      matchFeatures:
      - feature: pci.device
        matchExpressions:
          class:
            op: In
            value:
            - "0300"
            - "0380"
          vendor:
            op: In
            value:
            - "8086"
      - feature: kernel.loadedmodule
        matchExpressions:
          i915:
            op: Exists
      name: intel.gpu
apiVersion: nfd.k8s-sigs.io/v1alpha1
kind: NodeFeatureRule
metadata:
  name: intel-gpu-platform-labeling
spec:
  - labelsTemplate: |
      {{ range .pci.device }}gpu.intel.com/device-id.{{ .class }}-{{ .device }}.present=true
      {{ end }}
    matchFeatures:
    - feature: pci.device
      matchExpressions:
        class:
          op: In
          value:
          - "0300"
          - "0380"
        vendor:
          op: In
          value:
          - "8086"
    name: intel.gpu.generic.deviceid
  - labelsTemplate: gpu.intel.com/device-id.0300-{{ (index .pci.device 0).device }}.count={{
      len .pci.device }}
    matchFeatures:
    - feature: pci.device
      matchExpressions:
        class:
          op: In
          value:
          - "0300"
        vendor:
          op: In
          value:
          - "8086"
    name: intel.gpu.generic.count.300

Nodeに以下のラベルが追加されたことを確認します。(クラスID:0300とデバイスID:a7a0をキーに「generic rule for older and upcoming devices」ルールによりラベルが付与されています。Maxシリーズ等のプロダクト名までラベルが付与できるようです。)

  • "intel.feature.node.kubernetes.io/gpu": "true"
  • "gpu.intel.com/device-id.0300-a7a0.present": "true"
  • "gpu.intel.com/device-id.0300-a7a0.count": "1"
$ kubectl get node -o json | jq '.items[].metadata.labels'
{
  "beta.kubernetes.io/arch": "amd64",
  "beta.kubernetes.io/os": "linux",
  "gpu.intel.com/device-id.0300-a7a0.count": "1",
  "gpu.intel.com/device-id.0300-a7a0.present": "true",
  "intel.feature.node.kubernetes.io/gpu": "true",
  "kubernetes.io/arch": "amd64",
  "kubernetes.io/hostname": "ec-worker",
  "kubernetes.io/os": "linux"
}
$ kubectl get node -L intel.feature.node.kubernetes.io/gpu
NAME        STATUS   ROLES    AGE   VERSION   GPU
ec-worker   Ready    <none>   30m   v1.27.2   true

GPUプラグイン(intel-gpu-plugin)をインストールします。

kubectl apply -k 'https://github.com/intel/intel-device-plugins-for-kubernetes/deployments/gpu_plugin/overlays/nfd_labeled_nodes?ref=v0.28.0'

DaemonSetとしてintel-gpu-pluginが作成され、ラベルとしてintel.feature.node.kubernetes.io/gpu=trueが付いているノードでPodが起動します。

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: intel-gpu-plugin
      nodeSelector:
        intel.feature.node.kubernetes.io/gpu: "true"
        kubernetes.io/arch: amd64
$ kubectl get daemonsets
NAME               DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR                                                        AGE
intel-gpu-plugin   1         1         1       1            1           intel.feature.node.kubernetes.io/gpu=true,kubernetes.io/arch=amd64   18s
$ kubectl get pod -o wide
NAME                     READY   STATUS    RESTARTS   AGE     IP           NODE        NOMINATED NODE   READINESS GATES
intel-gpu-plugin-vpql2   1/1     Running   0          2m57s   10.95.0.12   ec-worker   <none>           <none>

ノードのAllocatableに"gpu.intel.com/i915": "1"が追加され、PodからGPUが利用可能になりました。

$ kubectl get node ec-worker -o=json | jq '.status.allocatable'
{
  "cpu": "6",
  "ephemeral-storage": "75559189584",
  "gpu.intel.com/i915": "1",
  "hugepages-1Gi": "0",
  "hugepages-2Mi": "0",
  "memory": "20367964Ki",
  "pods": "110"
}

OpenVINOコンテナイメージの作成

Kubernetes上で、OpenVINOのopenvino_notebooksを参考作成したDockerfilieを利用してコンテナイメージを作成します。

git clone --depth 1 --branch masanara https://github.com/masanara/openvino_notebooks.git
cd openvino_notebooks
docker build -t ov:2.1 -f Dockerfile.ubuntu .

Pod内でStable DiffusionのNotebookを実行すると、Stable DIffusionのモデルをHugging Faceからダウンロードしてくるため、Pod再作成のたびに数GBのモデルダウンロードが発生します。Pod内でダウンロードしたモデルのキャッシュをPVCに配置できるよう、DockerfileではENV HOME /modelとして、Kubernetesにデプロイする際に/modelディレクトリにPVCをマウントします。

OpenVINO Podの作成

今回はシングルノードのKubernetesを利用しているため、モデルキャッシュ用のPVCを利用するためにlocal-path-provisionerを有効にします。

$ kubectl apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/v0.0.25/deploy/local-path-storage.yaml
$ kubectl get pod -n local-path-storage
NAME                                     READY   STATUS    RESTARTS   AGE
local-path-provisioner-c79c98b44-pkmhp   1/1     Running   0           1m
$ kubectl get sc                                          <aws:m-nara@f22nos-og-ca>
NAME         PROVISIONER             RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
local-path   rancher.io/local-path   Delete          WaitForFirstConsumer   false                    2m

PersistentVolumeClaim、Deployment、Serviceを作成します。

$ kubectl apply -f https://raw.githubusercontent.com/masanara/openvino_notebooks/masanara/manifests/pvc.yaml
$ kubectl apply -f https://raw.githubusercontent.com/masanara/openvino_notebooks/masanara/manifests/openvino_notebooks.yaml

リソースが作成されたことを確認します。

$ kubectl get pod
NAME                                  READY   STATUS    RESTARTS   AGE
intel-gpu-plugin-vpql2                1/1     Running   0          111m
openvino-notebooks-6f44bf6ff9-jkztj   1/1     Running   0          7m55s
$ kubectl get pvc
NAME    STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
model   Bound    pvc-e67b4eb6-9372-4ba6-ac3f-0e86cef3e884   20Gi       RWO            local-path     24m
$ kubectl get svc
NAME                 TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
kubernetes           ClusterIP   10.96.0.1        <none>        443/TCP          144m
openvino-notebooks   NodePort    10.109.147.120   <none>        8888:30008/TCP   8m4s

Stable Diffusionによるイメージの生成

NodePort経由でJupyter Notebookにアクセスして、NotebookからGPUを認識できていることを確認します。

image.png

/236-stable-diffusion-v2/236-stable-diffusion-v2-text-to-image-demo.ipynbを開いて、GPUを利用して実行すると、途中で「Kernel Restarting」が発生して画像の生成を完了することができませんでした。(CPU利用時は成功。Podのメモリー割当量を増やしましたが改善せず。)

image.png

Notebookの内容をPythonコードに修正して実行してみました。GPUを使った場合74秒程度でイメージ生成できましたが、CPU処理では440秒以上かかっています。

CPU利用時 GPU利用時
イメージ作成時間 1 442.61 sec 74.50 sec
イメージ作成時間 2 443.07 sec 74.70 sec

image.png

生成したイメージはNotebook内で表示可能です。

from IPython.display import Image
file="result.png"
Image(file)

text_prompt = "Shibuya city filled by many cats, epic vista, beautiful landscape, 4k, 8k"

image.png

まとめ

Kubernetes上のPodから、Intel CPUに統合されたGPUを仮想マシンにパススルーして、仮想マシンでKubernetesを構成してコンテナ上でGPUをつかって画像生成することができました。
ESXiでパススルーして、仮想マシン上内のプログラムからGPGPUとして利用する方法はこちらで説明しています。

11
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?