この記事は 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.packages
にlinux-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」として認識されます。このままでもパススルーデバイスとして構成し、仮想マシンに接続することが可能です。
2023年10月リリースされたESXi 8.0u2以降、「Intel Corporation Raptor Lake-P [Iris Xe Graphics]」として認識されるようになっています。
ノードに対するGPUの接続
ESXiのデバイスとしてRaptor Lake-P [Iris Xe Graphics]のパススルーを有効にして、ESXi上に作成されたノードにPCIデバイスとしてパススルーデバイスを追加します。メモリーの設定も「すべてのゲスト メモリを予約 (すべてロック)」をチェックします。
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-devicesとintel-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を認識できていることを確認します。
/236-stable-diffusion-v2/236-stable-diffusion-v2-text-to-image-demo.ipynb
を開いて、GPUを利用して実行すると、途中で「Kernel Restarting」が発生して画像の生成を完了することができませんでした。(CPU利用時は成功。Podのメモリー割当量を増やしましたが改善せず。)
Notebookの内容をPythonコードに修正して実行してみました。GPUを使った場合74秒程度でイメージ生成できましたが、CPU処理では440秒以上かかっています。
CPU利用時 | GPU利用時 | |
---|---|---|
イメージ作成時間 1 | 442.61 sec | 74.50 sec |
イメージ作成時間 2 | 443.07 sec | 74.70 sec |
生成したイメージは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"
まとめ
Kubernetes上のPodから、Intel CPUに統合されたGPUを仮想マシンにパススルーして、仮想マシンでKubernetesを構成してコンテナ上でGPUをつかって画像生成することができました。
ESXiでパススルーして、仮想マシン上内のプログラムからGPGPUとして利用する方法はこちらで説明しています。