Virtuelle Umgebung mit OpenStack

OpenStack ist ein weit verbreitetes Produkt zur Realisierung einer virtuellen Umgebung. Diese arbeitet im Standard mit QEMU/KVM als Hypervisor. Kolla packt die OpenStack Komponenten in Container. In dieser Kombination ist ein Aufbau mit einer handelsüblichen Nvidia Grafikkarte wie unten beschrieben möglich. Dazu wird mindestens ein Hypervisor mit passendem PCI Slot, Nvidia Grafikkarte und ausreichend Platz im Gehäuse benötigt.

Hardware

  • HP ProLiant DL380 Gen9
  • Intel(R) Xeon(R) CPU E5-2678
  • 192 GB RAM
  • GeForce RTX 2080 Ti
  • Ubuntu 18.04.2 (Hypervisor)
  • Ubuntu 16.04 (Instanz)
  • CUDA 10.2

Grafikkarte

Sobald die Karte in einem passenden Server (Bauhöhe) verbaut und dieser als Hypervisor in OpenStack aufgenommen wurde, kann es auch schon losgehen. Um an die für die weitere Konfiguration notwendigen Infos zu gelangen, gehen Sie wie folgt vor:

  • PCI Adresse der Grafikkarte
$ lspci | grep NVIDIA
84:00.0 VGA compatible controller: NVIDIA Corporation GV102 (rev a1)
84:00.1 Audio device: NVIDIA Corporation Device 10f7 (rev a1)
84:00.2 USB controller: NVIDIA Corporation Device 1ad6 (rev a1)
84:00.3 Serial bus controller [0c80]: NVIDIA Corporation Device 1ad7 (rev a1)

$ lshw -numeric -C display
[...]
  *-display
       description: VGA compatible controller
       product: GV102 [10DE:1E07]
       vendor: NVIDIA Corporation [10DE]
[...]

Hier sind die Werte 10DE:1E0710DE:10F710DE:1AD610DE:1AD7 und 84:00.084:00.1 wichtig. Die ersten vier Werte werden im weiteren Verlauf benötigt. Mit den beiden letzten Werten fragen wir nach den verwendeten Treibern.

  • verwendete Treiber
$ lspci -kk -s 84:00.1
84:00.1 Audio device: NVIDIA Corporation Device 10f7 (rev a1)
        Subsystem: Gigabyte Technology Co., Ltd Device 37a9
        Kernel driver in use: snd_hda_intel
        Kernel modules: snd_hda_intel
$ lspci -kk -s 84:00.0
84:00.0 VGA compatible controller: NVIDIA Corporation GV102 (rev a1)
        Subsystem: Gigabyte Technology Co., Ltd Device 37a9
        Kernel driver in use: nouveau
        Kernel modules: nvidiafb, nouveau
...

Hierbei ist die Zeile Kernel driver in use und die Einträge snd_hda_intel und nouveau darin wichtig. Sie beschreiben den Treiber, der vom Kernel verwendet wird, um mit dieser Hardware zu sprechen. Später übergeben sie sie dem Kernel, sodass diese Treiber nicht mehr geladen werden.

IOMMU

Mit dem folgenden Befehl fragen Sie ab, ob IOMMU aktiviert ist:

$ virt-host-validate
[...]
  QEMU: Checking for device assignment IOMMU support                         : PASS
  QEMU: Checking if IOMMU is enabled by kernel                               : PASS
[...]

Finden Sie folgende Ausgabe vor

$ virt-host-validate
[...]
QEMU: Checking for device assignment IOMMU support                         : PASS
QEMU: Checking if IOMMU is enabled by kernel                               : WARN (IOMMU appears to be disabled in kernel. Add intel_iommu=on to kernel cmdline arguments)
[...]

setzen Sie für AMD Prozessoren die folgenden Optionen in Kolla

AMD
grub_kernel_options:
  - iommu=pt
  - iommu=1
  [...]

und diese Optionen für Prozessoren von Intel:

Intel
grub_kernel_options:
  - intel_iommu=on
  [...]

Treiberzugriff auf Host unterbinden

Deaktivieren Sie den geladenen Treiber der Grafikkarte:

$ cat /etc/modprobe.d/blacklist-nvidia-nouveau.conf
blacklist nouveau
options nouveau modeset=0

In unserem Beispiel ist eine Soundkarte in der Grafikkarte, die Sie wie folgt deaktivieren:

$ cat /etc/modprobe.d/blacklist-nvidia-nouveau.conf
blacklist snd_hda_intel
blacklist nouveau
options nouveau modeset=0

Abschließend aktualisieren Sie die Initramfs und starten den Server neu:

$ sudo update-initramfs -u
$ sudo reboot

PCI Passthrough

Für die Passthrough-Konfiguration übergeben Sie die oben ermittelten PCI-Adressdaten an das Kernelmodul vfio.

$ cat /etc/modprobe.d/vfio.conf
options vfio-pci ids=10de:1e07
options vfio-pci disable_vga=1

In unserem Beispiel hat die Grafikkarte mehrere Komponenten, dann sieht die Konfiguration wie folgt aus:

$ cat /etc/modprobe.d/vfio.conf
options vfio-pci ids=10de:1e07,10de:10f7,10de:1ad6,10de:1ad7
options vfio-pci disable_vga=1

Nun aktivieren Sie das Kernelmodul vfio:

$ cat /etc/modules
...
vfio-pci

zudem muss im Nova Scheduler der ‘PciPassthroughFilter’ aktiviert und ausgerollt werden.

$ cat kolla/files/overlays/nova/nova-scheduler.conf
[filter_scheduler]
enabled_filters = ..., PciPassthroughFilter

Nun noch die Informationen für Nova konfigurieren:

$ cat kolla/files/overlays/nova/nova-api.conf
[pci]
alias = { "vendor_id":"10de", "product_id":"1e07", "device_type":"type-PCI", "name":"nvidia" }

$ cat kolla/files/overlays/nova/nova-compute.conf
[pci]
alias = { "vendor_id":"10de", "product_id":"1e07", "device_type":"type-PCI", "name":"nvidia" }
passthrough_whitelist = { "vendor_id": "10de", "product_id": "1e07" }

Ist nur ein Hypervisor mit einer Grafikkarte ausgestattet, wird Nova Compute auch nur auf diesem Hypervisor konfiguriert:

$ cat kolla/files/overlays/nova/gpuserver/nova.conf
[pci]
alias = { "vendor_id":"10de", "product_id":"1e07", "device_type":"type-PCI", "name":"nvidia" }
passthrough_whitelist = { "vendor_id": "10de", "product_id": "1e07" }

Bei unserer Grafikkarte sind mehrere Geräte in der Karte verbaut und wenn all diese durchgereicht werden sollen, sieht die Konfiguration so aus:

$ cat kolla/files/overlays/nova/nova-api.conf
[pci]
alias = { "vendor_id":"10de", "product_id":"1e07", "device_type":"type-PCI", "name":"nvidia" }
alias = { "vendor_id":"10de", "product_id":"10f7", "device_type":"type-PCI", "name":"nvidia" }
alias = { "vendor_id":"10de", "product_id":"1ad6", "device_type":"type-PCI", "name":"nvidia" }
alias = { "vendor_id":"10de", "product_id":"1ad7", "device_type":"type-PCI", "name":"nvidia" }

$ cat kolla/files/overlays/nova/gpuserver/nova.conf
[pci]
alias = { "vendor_id":"10de", "product_id":"1e07", "device_type":"type-PCI", "name":"nvidia" }
alias = { "vendor_id":"10de", "product_id":"10f7", "device_type":"type-PCI", "name":"nvidia" }
alias = { "vendor_id":"10de", "product_id":"1ad6", "device_type":"type-PCI", "name":"nvidia" }
alias = { "vendor_id":"10de", "product_id":"1ad7", "device_type":"type-PCI", "name":"nvidia" }
passthrough_whitelist = [{ "vendor_id":"10de", "product_id":"1e07" },
                         { "vendor_id":"10de", "product_id":"10f7" },
                         { "vendor_id":"10de", "product_id":"1ad6" },
                         { "vendor_id":"10de", "product_id":"1ad7" }]

OpenStack Flavor

Nachdem Sie die OpenStack Nova Konfiguration ausgerollt haben, fehlt noch ein OpenStack Flavor, das Sie mit folgendem Befehl anlegen:

$ openstack flavor set 1C-1G-1GB-10GB-GPU --property "pci_passthrough:alias"="nvidia:1"

Die Zahl bei nvidia:1 beschreibt die Anzahl der durchzureichenden Geräte-Komponenten, folglich ist diese in unserem Beispiel 4 und damit ist der Befehl:

$ openstack flavor set 1C-1G-1GB-10GB-GPUall --property "pci_passthrough:alias"="nvidia:4"

Hypervisor verstecken

Zum Schluss muss der Hypervisor “versteckt” werden. Dies ist notwendig, damit der nvidia Treiber später auch in der virtuellen Maschine geladen wird und die Grafikkarte ansteuern kann. Ohne diesen Trick funktioniert das Ganze nicht und die Grafikkarte wird vom Treiber “ignoriert”.

$ openstack flavor set 1C-1G-1GB-10GB-GPU --property hide_hypervisor_id=true

Virtuelle Maschine

Neben den GPU Treibern auf der Download-Seite des Herstellers [1], existiert mit CUDA ein spezielles Toolkit von Nvidia [2], das u.a. auch das nvidia Treiber Binary mit an Bord hat. Nach der Auswahl für Operating SystemArchitectureDistribution und Distributions-Version wählen Sie den Installer Type (runfile(local), deb(local), deb(network) und cluster(local)) aus. Details zur Installation des CUDA Toolkits finden Sie hier [3].

CUDA installieren

Nachdem Sie CUDA mit dem runfile(local) installiert haben

$ sudo sh cuda_<cudaversion>_<gpudriverversion>_linux.run
===========
= Summary =
===========

Driver:   Installed
Toolkit:  Installed in /usr/local/cuda-10.2/
Samples:  Installed in /root/, but missing recommended libraries

Please make sure that
 -   PATH includes /usr/local/cuda-10.2/bin
 -   LD_LIBRARY_PATH includes /usr/local/cuda-10.2/lib64, or, add /usr/local/cuda-10.2/lib64 to /etc/ld.so.conf and run ldconfig as root

To uninstall the CUDA Toolkit, run cuda-uninstaller in /usr/local/cuda-10.2/bin
To uninstall the NVIDIA Driver, run nvidia-uninstall

Please see CUDA_Installation_Guide_Linux.pdf in /usr/local/cuda-10.2/doc/pdf for detailed information on setting up CUDA.
Logfile is /var/log/cuda-installer.log

und die Anpassungen an $PATH und für ld vorgenommen haben, verifizieren Sie die korrekte Zusammenarbeit von Treiber in der virtuellen Maschine und Grafikkarte auf dem Host an folgenden Stellen:

  • Im proc Verzeichnis sehen Sie die Version des Kernelmoduls für Nvidia
$ cat /proc/driver/nvidia/version
NVRM version: NVIDIA UNIX x86_64 Kernel Module  440.33.01  Wed Nov 13 00:00:22 UTC 2019
GCC version:  gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.12)
  • Sie müssen drei Geräte unter dev sehen
$ ls -l /dev/nvidia*
crw-rw-rw- 1 root root 195,   0 Mar  4 16:49 /dev/nvidia0
crw-rw-rw- 1 root root 195, 255 Mar  4 16:49 /dev/nvidiactl
crw-rw-rw- 1 root root 244,   0 Mar  4 16:49 /dev/nvidia-uvm
  • Zuletzt sehen Sie mit nvidia-smi die Informationen zur Grafikkarte
$ nvidia-smi
Tue Mar 24 11:44:44 2020
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 440.33.01    Driver Version: 440.33.01    CUDA Version: 10.2     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  GeForce RTX 208...  Off  | 00000000:00:08.0 Off |                  N/A |
|  0%   55C    P0    53W / 300W |      0MiB / 11019MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID   Type   Process name                             Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+
  • Mit dem Toolkit CUDA werden Samples mitgeliefert, die nach einem make für einen ersten Test zur Verfügung stehen, weitere Informationen finden Sie unter [4].
$ cd ~/NVIDIA_CUDA-10.2_Samples
$ make

~/NVIDIA_CUDA-10.2_Samples/bin/x86_64/linux/release/deviceQuery
~/NVIDIA_CUDA-10.2_Samples/bin/x86_64/linux/release/bandwithTest

Blick in die Zukunft

Es ist nicht ausgeschlossen, dass das hier beschriebene Vorgehen in einer zukünftigen Version des Treibers nicht funktioniert.

Nachtrag (14.4.2021)

Seit dem 30. März 2021 hat Nvidia einen Windows-Treiber im Program (Version 465.89), welcher das Durchreichen nun offiziell unterstützt. Nähere Informationen finden sich hier.

Bernd Müller
Bernd Müller beschäftigt sich seit 2009 bei B1 Systems mit den Themen Monitoring, Hochverfügbarkeit, Virtualisierung und Automatisierung. Weiterhin betreut er Support- und Operating-Kunden.