TL;DR:
Cet article détaille la configuration d'un PC perso servant de lab principal pour faire tourner des machines virtuelles exécutant des modèles d'IA locaux à l'appui de tests de diverses natures (LLM, txt2img, RAG, etc.) pour s'initier à l'IA self-hosted.
Update du sujet posté initialement sur le forum Ubuntu-fr: https://forum.ubuntu-fr.org/viewtopic.php?id=2092134
Concepts:
Virtualisation, IOMMU, Passthrough, GPU
Outils:
Ubuntu Server, KVM/libvirt (QEMU), dkms, scripting shell
Planification et choix techniques
Préparation et installation OS
Déploiement d'une machine virtuelle
Mise à jour des drivers Nvidia côté VM
Liste du matériel assemblé:
Après avoir comparé les performances et les prix des CPU, le choix du moment (juillet 2025) fait qu'on s'oriente vers de l'AMD (Ryzen 9).
J'ai l'intention de mettre 4 SSD NVME dont deux Gen5, il faut donc assez de slots intégrés, et du bon type.
Après une rapide recherche je tombe rapidement sur ce modèle qui semble correspondre: Gigabyte AORUS X870E Pro.
Ce que je souhaite faire, c'est passer en "passthrough" à mes VMs la carte graphique et les deux SSD NVME Gen5, +éventuellement un contrôleur USB (intégré ou bien sur une carte PCIe dédiée).
Il est temps de vérifier les IOMMU groups.
En cherchant le modèle de la carte mère "aorus x870e pro manual" sur internet je tombe directement sur le manuel de cette carte mère sur le site du fabricant (lien: https://download.gigabyte.com/FileList/Manual/mb_manual_x870e-aorus-pro-ice_1004_e.pdf). En page 5 figure le diagramme des composants intégrés (motherboard block diagram):
Vérification des IOMMU groups
Une problématique pour passer du matériel en direct à une VM est de tenir compte des groupes IOMMU.
En gros, chaque composant qui est sur une ligne (un bus) qui sort directement du CPU sur le diagramme de la carte mère, est isolé dans sa propre zone d'adressage de mémoire: on peut le passer à une VM. Pour les composants qui ne sont pas directement reliés au CPU c'est plus compliqué: ils sont probablement regroupés par chipset, et il ne sera pas possible de donner le contrôle d'un seul d'entre eux à une VM (il faut lui présenter le groupe en entier). Parfois ça peut fonctionner, mais c'est incertain.
Sur le schéma, on voit que le slot PCI Express 16x est derrière un switch PCI sur son propre bus, donc c'est OK: on pourra installer une carte graphique dans ce slot et la passer à une VM.
Les deux SSD NVME à passer aux VM sont des Gen5. Sur la carte mère il y a trois slots M.2 Gen5 et un slot Gen4. D'après le plan de la carte mère qui se trouve en page 4 du manuel, les slots Gen5 sont ceux nommés M2A_CPU, M2B_CPU et M2C_CPU. Ils sont tous sur un bus directement relié au CPU, donc on pourra installer dedans des SSD NVME et les passer individuellement aux machines virtuelles.
En revanche, les deux autres slots PCIe physiques de la carte mère sont sur un autre chipset et n'ont pas de switch PCIe dédié, cela veut dire qu'ils risquent de partager un groupe IOMMU commun avec tous les devices gérés par ce chipset (donc: le Wifi, 9 ports USB, et le port RJ45 2.5 GbE). On devrait passer tous ces périphériques en un seul lot à la VM, la machine hôte n'en aurait plus le contrôle, donc on perdrait le réseau: ça ne peut pas fonctionner (testé !).
C'est plutôt souhaitable que le fabricant de la carte mère publie dans sa documentation un diagramme des composants intégrés. Pour des modèles équivalents que j'ai trouvé, ASRock affiche dans sa documentation le diagramme d'archi de sa carte mère, mais Asus et MSI ne fournissent pas l'information.
Normalement, il y a toujours moyen de s'en sortir d'une façon ou d'une autre mais j'ai déjà vu passer sur les forums des messages inquiétants de setups avec du passthrough impossibles à faire marcher sur certaines combinaisons de matériel, donc j'ai préféré être certain au moment de commander.
L'isolation des groupes IOMMU ne dépend pas que de la carte mère, elle dépend aussi du CPU, donc en fonction du modèle choisi, il faut s'assurer en parcourant les forums, que quelqu'un a déjà réussi à le faire. Pour la combinaison Aorus X870E Pro + Ryzen 9950X + RTX 5090, je valide.
Il y a aussi des cartes mères plus haut de gamme (les versions "Extreme" ou "Platinum") qui apportent une fonctionnalité "ACS" (pour Access Control System) qui permet d'activer une isolation logique au niveau du BIOS (sur les cartes mères à chipset AMD cela se configure dans le menu "AMD CBS") si cela peut aider.
Enfin dans le pire des cas, il est toujours possible de séparer les devices dans le système en patchant le noyau Linux (avec un patch "ACS override"), c'est très intéressant mais aussi un peu risqué et on n'est pas là pour ça donc je veux absolument l'éviter (gestion des mises à jour...).
Dernière chose: lorsqu'une VM avec un GPU en passthrough est démarrée, la machine hôte ne peut plus rien afficher sur cette carte graphique car elle n'y a plus accès. Si on veut qu'elle puisse afficher quelque chose (pour continuer d'utiliser la machine hôte en direct avec un clavier/écran, ou pour du diagnostic) il faut une carte mère avec un port HDMI intégré et, soit un chipset graphique intégré, soit une compatibilité avec un chipset graphique intégré au CPU (et bien sûr un CPU adéquat).
Au moment du choix (juillet 2025), la dernière distribution Debian (12.11) vient avec un noyau 6.1 (le 6.8 est dans la Debian "testing") et toujours sans support natif de ZFS ce qui oblige à charger un module DKMS pour en bénéficier (avec quelques faibles risques que ça ne fonctionne plus pareil après un upgrade de noyau).
Le choix pragmatique est donc Ubuntu Server 24.04, pour le noyau plus récent + les drivers matériels non-libres inclus + les mises à jour de sécurité pendant 10 ans + le support natif de ZFS.
Il y a peut-être mieux ailleurs, notamment du côté de Arch ou Fedora mais je ne les connais pas.
Avec Ubuntu on a les "snaps", un système de containers maniéré, lourd, propriétaire et imposé. Du côté des avantages, ça "juste marche" et ça se met à jour tout seul.
Dès l'installation on peut choisir l'option "HWE kernel" (= Hardware Extension. On passe à un noyau 6.11 avec un support matériel encore meilleur) et l'option "Search for third-party drivers" qui installe des drivers non libres.
Le support LTS de base est de 5 ans, et le support étendu "pro" semble être de 5 ans supplémentaires (pour les mises à jour de sécurité). Cela oblige à créer un compte "Ubuntu Pro" mais pas de panique, on a droit à 5 souscriptions gratuites pour des serveurs perso.
Ubuntu Pro n'est absolument pas indispensable au fonctionnement du lab, on peut zapper cette partie.
Avant d'installer le système, on commence par mettre à jour la carte mère dans le dernier niveau de firmware disponible. Pour ça je me suis rendu dans le "Download Center" sur le site de Gigabyte et j'ai tapé le nom de la carte "X870E" dans le champ de recherche pour atterir directement sur la page de téléchargement des firmwares (dans la section "BIOS").
Ma carte est une rev. 1.1 livrée en firmware "FA", donc avec 5 versions de retard.
Malheureusement avec ce modèle il m'a été impossible d'installer directement le dernier firmware. Il faut passer par l'installation de chaque firmware intermédiaire. Par conséquent la procédure est la suivante:
Le PC redémarre deux fois pour chaque mise à jour dans mon cas. A chaque redémarrage on reste sur un écran noir qui semble interminable, mais au global c'est assez rapide: moins de 30 minutes suffisent pour appliquer toutes les mises à jour.
Il est inutile de commencer à faire des réglages dans le BIOS avant les upgrades, car tout va être réinitialisé.
Gigabyte a sorti plus tard une nouvelle version du firmware pour la carte mère (FA5) qui remplace les FA5a et FA5b, sauf que ces derniers ont disparu du site avec leurs release notes, donc je ne peux même pas savoir ce que corrige la FA5 pour moi. C'est agaçant. Je choisis de ne pas mettre à jour.
En même temps je suis un peu con de ne pas avoir compris que le "b" dans "FA5b" voulait probablement dire beta, mais cette logique de release n'est pas expliquée.
En tout cas après presque 1 an de fonctionnement je n'ai eu aucun problème.
Je suis maintenant à jour dans la dernière version (FA5b du 23 juin 2025), maintenant je fais les réglages désirés dans le BIOS:
Au moment de la réception des composants je n'ai pas encore la carte graphique finale donc le déploiement se fait avec une vieille RTX 2070. Cela ne change rien à la procédure car elle utilise les mêmes drivers Nvidia unifiés que toutes les autres RTX. La seule chose qui changera sera l'identifiant du matériel pour configurer la virtualisation, à adapter dans les fichiers de conf selon les outputs des commandes (procédure décrite plus bas), mais le principe reste le même. J'en profite pour détailler plus bas la procédure à suivre lorsqu'on remplace le GPU dans ce type de setup.
J'utiliserai également temporairement, pendant l'installation de l'OS Ubuntu, un dongle USB vers Ethernet, car le driver du Realtek 2.5GbE intégré à la carte mère, n'est pas chargé à ce moment et le PC sera sans réseau, or on a besoin d'une connexion au moment de l'install pour faire les mises à jour et d'autres choses (bien sûr on peut installer l'OS sans réseau et apporter plus tard le driver de la NIC Realtek intégrée avec une clé USB. Mais c'est moins pratique).
Dans mon cas j'ai oublié de cocher la case donc j'ai fait après l'install:
# apt install ubuntu-drivers-common
Cette étape est facultative, elle permet de bénéficier de paquets complémentaires testés et compilés par Canonical, et de mises à jour de sécurité pendant dix ans au lieu de cinq.
Avec un compte gratuit sur le site de Canonical on peut enregistrer 5 serveurs max. pour un usage non commercial, ce qui est parfait pour un homelab.
# dpkg -l | grep linux-generic
=> linux-generic-hwe-24.04
# pro attach <clé générée sur le site d'Ubuntu>
# apt update && apt upgrade
Cette étape est spécifique à mon matériel mais en fait, on peut très souvent retrouver la problématique lorsqu'on installe une distribution Linux sur du matériel fort récent, et le principe reste le même.
Mon contrôleur réseau est un Realtek 8125 donc je télécharge le driver sur le site du fabricant:
# apt install build-essential dkms linux-headers-$(uname -r) git
# URL=https://github.com/awesometic/realtek-r8125-dkms/archive/refs/heads/master.zip
# wget -O r8125-dkms.zip $URL
# apt install unzip
# unzip r8125-dkms.zip
# cd realtek-r8125-dkms-master/
# ./dkms-install.sh
# ip a
# modprobe r8125
# ip a # vérifier
# vi /etc/netplan/*.yaml
Remplacer enx207bd2b2e60b par enp14s0 (donc en gros: remplacer le nom de l'interface USB-ETH par le nom de la NIC interne avant de swapper le câble réseau - à adapter selon son cas et le retour de la commande "ip a")
Enlever le dongle USB-Eth et rebooter
Il m'est arrivé que le système change le nom du device correspondant à la NIC d'un reboot à l'autre (apparemment si on change le matériel présent). Afin de ne pas perturber ce qu'on a défini dans des fichiers de conf, on peut fixer le nom du device de la façon suivante (dans mon cas le nom du device est enp14s0): éditer le fichier /etc/netplan/50-cloud-init.yaml puis stipuler l'adresse MAC de la carte en question pour la lier au nom défini:
# vi /etc/netplan/*.yaml
network:
version: 2
ethernets:
enp14s0:
match:
macaddress: xx:xx:xx:xx:xx:xx
dhcp4: true
Etant donné que je compte utiliser la carte RTX uniquement dans des machines virtuelles, il n'est pas nécesasire d'installer des drivers sur l'hôte. Cependant si on souhaite utiliser le GPU sur l'OS hôte, la procédure est la suivante. D'abord on tape la commande suivante:
# ubuntu-drivers devices
Ensuite la commande "ubuntu-drivers autoinstall" permet d'installer automatiquement le driver recommandé mais elle sélectionne prioritairement un driver opensource. Si on préfère utiliser les drivers non-libres fournis par Nvidia, ce qui peut être mieux pour bénéficier de toutes les fonctionnalités, dans ce cas il faut plutôt faire:
# apt install nvidia-driver-570
# reboot
Installation des outils nécessaires + de quelques utilitaires couramment utilisés:
# apt install ethtool cpufrequtils htop lsof curl wget net-tools git dkms build-essential pciutils usbutils ifstat
Vérifier si amd-pstate est déjà actif (normalement, oui):
# grep 'amd-pstate' /sys/devices/system/cpu/cpu0/cpufreq/scaling_driver
Sinon, l'ajouter au démarrage de GRUB:
# sed -i 's/GRUB_CMDLINE_LINUX="/GRUB_CMDLINE_LINUX="amd-pstate=active /' /etc/default/grub
Ajouter aussi iommu au démarrage de GRUB:
# sed -i 's/GRUB_CMDLINE_LINUX="/GRUB_CMDLINE_LINUX="iommu=on /' /etc/default/grub
Mettre à jour GRUB et redémarrer:
# update-grub
# reboot
La commande suivante permet de lister les disques NVMe et les identifiants de devices PCIe correspondants:
# for dev in /sys/class/nvme/nvme*/device; do nvme=$(basename $(dirname "$dev")) ; pci=$(basename $(readlink "$dev")) ; echo "$nvme <-> $pci" ; done
On installe l'hyperviseur et ses dépendances:
# apt install qemu-system-x86 virt-manager bridge-utils
# virt-manager
Lancer virt-manager => enabler le network default au boot puis créer les pools de stockage souhaités.
Stopper le pool default et désactiver l'autostart
Le tuto passthrough dont je me suis inspiré est le suivant: https://github.com/HarbourHeading/KVM-GPU-Passthrough
C'est parti:
Taper les commandes suivantes et vérifier qu'elles retournent les bonnes valeurs (ce qui indique que les prérequis à la virtu sont OK):
# lscpu | grep "Virtualization"
Virtualization: AMD-V
si cette commande retourne quelque chose, c'est OK.
# egrep -c '(vmx|svm)' /proc/cpuinfo
32
cette commande doit retourner le nombre de threads du CPU.
# lspci -nn |grep -E "NVIDIA|Samsung Electronics Co Ltd Device" |grep "\[....:....\]"
03:00.0 VGA compatible controller: NVIDIA Corporation Device [10de:1e84] (rev a1)
03:00.1 Audio device: NVIDIA Corporation Device [10de:10f8] (rev a1)
03:00.2 USB controller: NVIDIA Corporation Device [10de:1ad8] (rev a1)
03:00.3 Serial bus controller [0c80]: NVIDIA Corporation Device [10de:1ad9] (rev a1)
02:00.0 Non-Volatile memory controller [0108]: Samsung Electronics Co Ltd Device [144d:a810]
03:00.0 Non-Volatile memory controller [0108]: Samsung Electronics Co Ltd Device [144d:a810]
Là, on affiche les identifiants du matériel qu'on veut pouvoir passer en direct à des machiens virtuelles. Dans mon cas comme je veux utiliser en passthrough le GPU Nvidia et des devices Samsung, je recherche "NVIDIA" et "Samsung".
On a donc:
Les deux devices "VGA controller" et "Audio device" sont courants pour une carte graphique Nvidia. Les deux autres ("USB controller" et "Serial bus controller") qui sont présents sur ma vieille RTX 2070 ne correspondent pas à des ports physiques mais à un contrôleur intégré qui servait à utiliser un casque de VR, ou quelque chose comme ça, techno qui a été abandonnée par Nvidia depuis. Les devices de ce type sont absents sur les cartes récentes, comme on le verra plus bas lors de l'upgrade vers la 5090.
Pour activer les iommu pour les devices PCI souhaités, éditer /etc/default/grub et ajouter dans GRUB_CMDLINE_LINUX_DEFAULT la valeur suivante:
vfio-pci.ids=<identifiants des devices>
Par exemple, ma ligne GRUB_CMDLINE_LINUX_DEFAULT ressemble maintenant à ça:
GRUB_CMDLINE_LINUX_DEFAULT="amd_iommu=on iommu=pt vfio-pci.ids=10de:1e84,10de:10f8,10de:1ad8,10de:1ad9,144d:a810,1b21:2426 isolcpus=8-15,24-31 nohz_full=8-15,24-31 rcu_nocbs=8-15,24-31"
1. Dans mon cas j'ai aussi ajouté le device 1b21:2426 qui correspond à un contrôleur USB4 intégré à la carte mère qui dispose de deux ports USB-C et que je veux passer aux VMs.
2. Les directives isolcpus=8-15,24-31 nohz_full=8-15,24-31 rcu_nocbs=8-15,24-31 servent à isoler un groupe de threads du CPU. On demande au scheduler de Linux de ne pas les utiliser pour l'ordonnancement normal, d'activer le mode tickless sur ceux-ci pour qu'ils reçoivent le moins d'interruptions possibles, et de déporter les traitements différés du sous-système RCU sur d'autres threads. Bref, on demande au système d'utiliser ces threads le moins possible, pour les réserver aux machines virtuelles. Le choix des threads 8 à 15 et 24 à 31 permet de réserver un groupe de cores cohérent. C'est une configuration courante sur un processeur avec 16 cœurs physiques et 32 threads, où les paires 8<=>24, 9<=>25, etc. correspondent aux deux threads d'un même coeur physique. Détails ici.
Mettre à jour GRUB et redémarrer:
# update-grub
# reboot
Pour préparer le passthrough, vérifier les IOMMU avec le script suivant:
# vi iommu.sh
#!/bin/bash
for d in /sys/kernel/iommu_groups/*/devices/*; do
n=${d#*/iommu_groups/*}; n=${n%%/*}
printf 'IOMMU Group %s ' "$n"
lspci -nns "${d##*/}"
done
#end#
# chmod +x iommu.sh
# ./iommu.sh
==> IOMMU NVIDIA 01:00 : Group 15
==> IOMMU NVME 02:00 : Group 16
==> IOMMU NVME 03:00 : Group 17
Isoler le GPU:
# vi /etc/modprobe.d/blacklist-gpu.conf
blacklist nouveau
blacklist nvidia
blacklist nvidia_drm
blacklist nvidiafb
Isoler les devices:
# vi /etc/modprobe.d/vfio.conf
options vfio-pci ids=10de:1e84,10de:10f8,10de:1ad8,10de:1ad9,144d:a810,1b21:2426
# vi /etc/initramfs-tools/modules
vfio
vfio_iommu_type1
vfio_pci
vfio_virqfd
Updater l'initramfs et redémarrer:
# update-initramfs -c -k $(uname -r)
# reboot
Vérifier tout:
# lspci -k |grep -E "vfio-pci|NVIDIA"
# lspci -nnk -d 10de:1e84
# lspci -nnk -d 10de:10f8
# lspci -nnk -d 10de:1ad8
# lspci -nnk -d 10de:1ad9
# lspci -nnk -d 144d:a810
==> Vérifier qu'on a pour ces devices: "Kernel driver in use: vfio-pci"
Ceci est juste donné à titre d'exemple (dépend largement des choix techniques et du matériel).
Sur mon serveur j'ai choisi d'utiliser du ZFS pour plein de raisons donc je le configure de la sorte:
La commande suivante permet de lister les disques NVMe et les identifiants de devices PCIe correspondants:
# for dev in /sys/class/nvme/nvme*/device; do nvme=$(basename $(dirname "$dev")) ; pci=$(basename $(readlink "$dev")) ; echo "$nvme <-> $pci" ; done
On fait une partition ZFS sur le reste du disque système:
# apt install zfsutils-linux
# parted /dev/nvme2n1
(parted) mkpart primary 748789760s 100%
(parted) quit
Configuration des filesystems:
# zpool create -f system /dev/nvme2n1p4
# zpool create -f data /dev/nvme3n1
# zfs set compression=lz4 system
# zfs set compression=lz4 data
# zfs create system/public
# zfs create system/repository
# zfs create system/vm
# zfs set atime=off system/vm
# zfs set recordsize=128k system/vm
# zfs create data/vm
# zfs set atime=off data/vm
# zfs set recordsize=128k data/vm
Ces réglages servent à optimiser la performance:
- désactivation du atime,
- réglage du recordsize optimisé pour des i/o séquentiels,
- activation de la compression. LZ4 est léger, il compresse moins bien que les autres algos disponibles sous ZFS mais il est très rapide et peu consommateur de ressources CPU donc c'est le meilleur compromis (il est très efficace sur des données hautement compressibles, mais si on lui balance beaucoup de données non compressibles comme par exemple du flux vidéo, il ne va pas faire fumer le CPU pour rien).
Une grosse partition sur un disque magnétique le cas échéant:
# zpool create -f hdd /dev/sda
# zfs set compression=lz4 hdd
# zfs create hdd/public
# zfs create hdd/backup
# zfs create hdd/repository
# zfs set quota=5T hdd/backup
Et ne pas oublier de setter les ownership/permissions:
# chown thom:thom /system/ /data/ /system/vm/ /system/repository/ /data/vm/ /hdd/ /hdd/backup/ /hdd/repository/
# chown nobody:nogroup /system/public /hdd/public
# chmod 777 /system/public /hdd/public
Ceci permet d'optimiser la performance de la RAM en réduisant la pression sur le TLB cache lors des allocations de mémoire lorsqu'on travaille toujours avec de grosses allocations, typiquement en démarrant des VM avec beaucoup de RAM.
Ce n'est intéressant que lorsque les VM utilisent effectivement beaucoup de mémoire, sinon le gain est quasi nul.
Vérification que c'est bien supporté:
# grep pdpe1gb /proc/cpuinfo |grep pdpe1gb
# zgrep HUGETLB /boot/config-$(uname -r) |grep -E "CONFIG_HUGETLBFS=y|CONFIG_HUGETLB_PAGE=y|CONFIG_ARCH_SUPPORTS_HUGETLBFS=y"
=> doit retourner au moins CONFIG_HUGETLBFS=y et CONFIG_HUGETLB_PAGE=y
# test -f /proc/sys/vm/nr_hugepages
# ls /sys/kernel/mm/hugepages/
Configurer les hugepages:
# mkdir /dev/hugepages
# echo "nodev /mnt/hugepages hugetlbfs pagesize=1G 0 0" >>/etc/fstab
# vi /etc/default/grub
=> ajouter (dans GRUB_CMDLINE_LINUX_DEFAULT=, au début):
default_hugepagesz=1G hugepagesz=1G hugepages=160
(160 est le nombre de GB de ram à allouer aux hugepages, je mets le maximum de RAM qui sera donné à une VM, à adapter selon le contexte).
Exemple:
GRUB_CMDLINE_LINUX_DEFAULT="default_hugepagesz=1G hugepagesz=1G hugepages=160 amd_iommu=on iommu=pt vfio-pci.ids=10de:1e84,10de:10f8,10de:1ad8,10de:1ad9,144d:a810,1b21:2426 isolcpus=8-15,24-31 nohz_full=8-15,24-31 rcu_nocbs=8-15,24-31"
Updater GRUB et rebooter:
# update-grub
# reboot
# grep Huge /proc/meminfo
# sysctl -a |grep vm.nr_hugepages
Pour faire un export NFS sur le serveur pour le rendre accessible dans les VM:
Ceci est juste un exemple, à adapter selon les cas.
# apt install nfs-kernel-server
# zfs set quota=100G system/public
# vi /etc/exports
/system/public 192.168.122.0/24(rw,sync,no_subtree_check,no_root_squash)
# vi /etc/default/nfs-kernel-server
RPCNFSDCOUNT=8
RPCMOUNTDOPTS="--manage-gids"
# systemctl restart nfs-kernel-server
Pour faire en sorte que toutes les VM démarrées s'arrêtent proprement lors d'un shutdown/reboot, créer et activer un service spécifique:
# vi /etc/systemd/system/shutdown-libvirt-vms.service
[Unit]
Description=Shutdown all running libvirt VMs
DefaultDependencies=no
Before=shutdown.target reboot.target halt.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/libvirt-shutdown-vms.sh
RemainAfterExit=true
[Install]
WantedBy=halt.target reboot.target shutdown.target
# vi /usr/local/bin/libvirt-shutdown-vms.sh
#!/bin/bash
# Timeout (en secondes) pour attendre l'arrêt de chaque VM
TIMEOUT=60
echo "Arrêt des VMs libvirt en cours..."
# Lister les VMs en cours d'exécution
running_vms=$(virsh list --name | grep -v '^$')
for vm in $running_vms; do
echo " Arrêt de $vm"
virsh shutdown "$vm"
# Attente que la VM s'arrête, avec timeout
for i in $(seq 1 $TIMEOUT); do
if ! virsh domstate "$vm" | grep -q running; then
echo " $vm arrêtée proprement"
break
fi
sleep 1
done
# Forcer l'arrêt si la VM n'a pas répondu
if virsh domstate "$vm" | grep -q running; then
echo " $vm ne répond pas, arrêt forcé"
virsh destroy "$vm"
fi
done
echo "Toutes les VMs libvirt ont été arrêtées."
# chmod +x /usr/local/bin/libvirt-shutdown-vms.sh
# systemctl enable shutdown-libvirt-vms.service
Pour faire automatiquement du space reclaim dans les fichiers qcow2 alloués aux machines virtuelles, ajouter "discard='unmap'" dans le XML des VM (via virsh edit <vm>). Exemple:
<disk type='file' device='disk'>
<driver name='qemu' type='qcow2' discard='unmap'/>
<source file= ........
On met un swappiness très faible (10 ou moins) pour optimiser la perf du gestionnaire de mémoire virtuelle, surtout si on n'a pas l'intention de surallouer de la RAM aux machines virtuelles:
# echo "vm.swappiness = 10" >>/etc/sysctl.conf
# cat /proc/sys/vm/swappiness
# sysctl -p
# cat /proc/sys/vm/swappiness
Lors d'un apt upgrade, en particulier dans mon cas où j'ai compilé un driver pour la NIC 2.5GbE sous forme de module dans l'initramfs et surtout que cette machine est installée à 200km de chez moi, il ne faut surtout pas se chier dessus au moment de l'upgrade, il faut bien vérifier que DKMS reconstruit le module avant de rebooter.
Sur un autre serveur j'ai eu un problème avec un driver d'une carte Realtek 5GbE installé manuellement et dont le pauvre script d'installation fourni par le fabricant n'avait pas mis les fichiers au bon endroit.
Je l'ai installé sans regarder l'output et j'étais content. Résultat, en redémarrant la machine après un upgrade de kernel, plus d'accès réseau.
Récit de ces aventures ici.
Voici un exemple des messages normaux qui doivent s'afficher lors d'une "grosse" mise à jour (ici on passe du noyau 6.11 au 6.14) et que DKMS rebuild un module (ici: r8125.ko):
# apt upgrade
[...]
Do you want to continue? [Y/n] Y
[...]
Setting up linux-modules-6.14.0-24-generic (6.14.0-24.24~24.04.3) ...
Setting up linux-modules-extra-6.14.0-24-generic (6.14.0-24.24~24.04.3) ...
Setting up linux-headers-6.14.0-24-generic (6.14.0-24.24~24.04.3) ...
/etc/kernel/header_postinst.d/dkms:
* dkms: running auto installation service for kernel 6.14.0-24-generic
Sign command: /usr/bin/kmodsign
Signing key: /var/lib/shim-signed/mok/MOK.priv
Public certificate (MOK): /var/lib/shim-signed/mok/MOK.der
Building module:
Cleaning build area...
'make' -j16 KVER=6.14.0-24-generic BSRC=/lib/modules/6.14.0-24-generic modules....
Signing module /var/lib/dkms/r8125/9.015.00/build/src/r8125.ko
Cleaning build area...
r8125.ko.zst:
Running module version sanity check.
- Original module
- No original module exists within this kernel
- Installation
- Installing to /lib/modules/6.14.0-24-generic/updates/dkms/
depmod...
dkms autoinstall on 6.14.0-24-generic/x86_64 succeeded for r8125
* dkms: autoinstall for kernel 6.14.0-24-generic
...done.
[...]
Setting up linux-image-6.14.0-24-generic (6.14.0-24.24~24.04.3) ...
I: /boot/vmlinuz.old is now a symlink to vmlinuz-6.11.0-29-generic
I: /boot/initrd.img.old is now a symlink to initrd.img-6.11.0-29-generic
I: /boot/vmlinuz is now a symlink to vmlinuz-6.14.0-24-generic
I: /boot/initrd.img is now a symlink to initrd.img-6.14.0-24-generic
Setting up linux-image-generic-hwe-24.04 (6.14.0-24.24~24.04.3) ...
Setting up linux-hwe-6.14-tools-6.14.0-24 (6.14.0-24.24~24.04.3) ...
Setting up linux-headers-generic-hwe-24.04 (6.14.0-24.24~24.04.3) ...
[...]
Processing triggers for initramfs-tools (0.142ubuntu25.5) ...
update-initramfs: Generating /boot/initrd.img-6.11.0-29-generic
[...]
Processing triggers for linux-image-6.14.0-24-generic (6.14.0-24.24~24.04.3) ...
/etc/kernel/postinst.d/dkms:
* dkms: running auto installation service for kernel 6.14.0-24-generic
* dkms: autoinstall for kernel 6.14.0-24-generic
...done.
/etc/kernel/postinst.d/initramfs-tools:
update-initramfs: Generating /boot/initrd.img-6.14.0-24-generic
[...]
Found linux image: [...]
Adding boot menu entry for UEFI Firmware Settings ...
done
[...]
Running kernel version:
6.11.0-29-generic
Diagnostics:
The currently running kernel version is not the expected kernel version 6.14.0-24-generic.
Restarting the system to load the new kernel will not be handled automatically, so you should consider rebooting.
The processor microcode seems to be up-to-date.
On arrive à la partie qui nous intéresse le plus: déployer une VM utilisant le hardware GPU.
Nom de la VM: ia1.
Je déploie la VM depuis un template donc les settings pour le passthrough doivent être mis dans le XML après coup.
J'alloue beaucoup de RAM à cette VM (160 GB) et je supprime le swap (swapoff /swap.img puis éditer le fstab pour commenter la ligne).
J'ajoute 2 images disques qcow2 de 512 GB.
Avant de démarrer la nouvelle VM, lui passer les PCI host devices:
Je ne sais pas si les deux devices inutiles 01:00:2 et 01:00:3 peuvent être enlevés de la liste, le driver risque de ne pas aimer (?) en tout cas je n'ai pas essayé.
Editer le XML (virsh edit) et mettre la config pour allouer 32 vCPU:
<vcpu placement='static'>32</vcpu>
<iothreads>1</iothreads>
<cputune>
<vcpupin vcpu='0' cpuset='0'/>
<vcpupin vcpu='1' cpuset='16'/>
<vcpupin vcpu='2' cpuset='1'/>
<vcpupin vcpu='3' cpuset='17'/>
<vcpupin vcpu='4' cpuset='2'/>
<vcpupin vcpu='5' cpuset='18'/>
<vcpupin vcpu='6' cpuset='3'/>
<vcpupin vcpu='7' cpuset='19'/>
<vcpupin vcpu='8' cpuset='4'/>
<vcpupin vcpu='9' cpuset='20'/>
<vcpupin vcpu='10' cpuset='5'/>
<vcpupin vcpu='11' cpuset='21'/>
<vcpupin vcpu='12' cpuset='6'/>
<vcpupin vcpu='13' cpuset='22'/>
<vcpupin vcpu='14' cpuset='7'/>
<vcpupin vcpu='15' cpuset='23'/>
<vcpupin vcpu='16' cpuset='8'/>
<vcpupin vcpu='17' cpuset='24'/>
<vcpupin vcpu='18' cpuset='9'/>
<vcpupin vcpu='19' cpuset='25'/>
<vcpupin vcpu='20' cpuset='10'/>
<vcpupin vcpu='21' cpuset='26'/>
<vcpupin vcpu='22' cpuset='11'/>
<vcpupin vcpu='23' cpuset='27'/>
<vcpupin vcpu='24' cpuset='12'/>
<vcpupin vcpu='25' cpuset='28'/>
<vcpupin vcpu='26' cpuset='13'/>
<vcpupin vcpu='27' cpuset='29'/>
<vcpupin vcpu='28' cpuset='14'/>
<vcpupin vcpu='29' cpuset='30'/>
<vcpupin vcpu='30' cpuset='15'/>
<vcpupin vcpu='31' cpuset='31'/>
</cputune>
<cpu mode='host-passthrough' check='none' migratable='on'>
<topology sockets='1' dies='1' cores='16' threads='2'/>
<feature policy='require' name='topoext'/>
</cpu>
Ajouter dans les features:
<kvm>
<hidden state='on'/>
</kvm>
<hyperv mode='custom'>
<relaxed state='on'/>
<vapic state='on'/>
<spinlocks state='on' retries='8191'/>
<vpindex state='on'/>
<runtime state='on'/>
<synic state='on'/>
<stimer state='on'/>
<frequencies state='on'/>
<reenlightenment state='on'/>
<tlbflush state='on'/>
<ipi state='on'/>
</hyperv>
<vmport state='off'/>
<smm state='on'/>
Ajouter à la fin de la partie clock:
<clock offset='utc'>
<timer name='hypervclock' present='yes'/>
</clock>
Définir la quantité de RAM et configurer l'utilisation des hugepages:
<memory unit='GiB'>160</memory>
<currentMemory unit='GiB'>160</currentMemory>
<memoryBacking>
<hugepages>
<page size='1' unit='G'/>
</hugepages>
</memoryBacking>
Démarrer la VM et se loguer dedans via la console
Configurer son adresse IP (ne pas laisser celle du template)
Effectuer la configuration:
# apt update && apt upgrade
# apt install lvm2 ubuntu-drivers-common lm-sensors
# ubuntu-drivers devices
# apt install nvidia-driver-575
# apt install python3-pip
On configure un filesystem /data qui utilise en parallèle les deux ssd nvme 9100 en passthrough pour un max de perf:
# pvcreate /dev/nvme0n1 /dev/nvme1n1
# vgcreate vg_data /dev/nvme0n1 /dev/nvme1n1
Une partition de swap juste au cas où, pour pouvoir faire "tourner" des LLM trop gros pour cette machine:
# lvcreate -i2 -I64 -L 256G -n lv_swap vg_data
# mkswap /dev/vg_data/lv_swap
# swapon /dev/vg_data/lv_swap
# blkid /dev/vg_data/lv_swap
# echo "UUID=<UUID> none swap sw 0 0" >>/etc/fstab
(remplacer l'UUID)
# swapoff -a
# swapon -a
Et régler le swappiness:
# vi /etc/sysctl.conf
vm.swappiness = 10
Vérifier:
# reboot
# cat /proc/sys/vm/swappiness
Formatage du filesystem data performant:
# lvcreate -i2 -I64 -l100%VG -n lv_data vg_data
# mkfs.xfs -K -d su=64k,sw=2 /dev/vg_data/lv_data
# mkdir /data
# echo "/dev/vg_data/lv_data /data xfs noatime,nodiratime,allocsize=1g,inode64 0 0" >>/etc/fstab
# systemctl daemon-reload
# mount /data && chown thom:thom /data
Et voilà, la VM est utilisable pour faire tourner des ia génératives avec une perf proche du bare metal.
Update 10/08/2025: je viens de recevoir le nouveau GPU 5090. Il contient 2 devices PCI (au lieu de 4 sur la 2070) dont les identifiants (tels que listés par lspci) sont 10de:2b85 et 10de:22e8.
Il faut donc mettre à jour la configuration sur le système hôte:
# vi /etc/default/grub
vfio-pci.ids=10de:2b85,10de:22e8,144d:a810,1b21:2426
Résultat:
GRUB_CMDLINE_LINUX_DEFAULT="default_hugepagesz=1G hugepagesz=1G hugepages=160 amd_iommu=on iommu=pt vfio-pci.ids=10de:2b85,10de:22e8,144d:a810,1b21:2426 isolcpus=8-15,24-31 nohz_full=8-15,24-31 rcu_nocbs=8-15,24-31"
# update-grub
# vi /etc/modprobe.d/vfio.conf
options vfio-pci ids=10de:2b85,10de:22e8,144d:a810,1b21:2426
# update-initramfs -c -k $(uname -r)
# reboot
# lspci -k |grep -E "vfio-pci|NVIDIA"
# lspci -nnk -d 10de:2b85
# lspci -nnk -d 10de:22e8
Ensuite, éditer le XML de la VM ia1 pour mettre à jour la conf:
# virsh edit ia1
=> dans les <hostdev> on supprime les devices 01:00:2 et 01:00:3 (laisser 01:00:0 et 01:00:1)
Première tentative de démarrage: j'obtiens ce message d'erreur dans la console (dmesg):
[ 8.195276] [drm:nv_drm_load [nvidia_drm]] *ERROR* [nvidia-drm] [GPU ID 0x00000a00] Failed to allocate NvKmsKapiDevice [ 8.195504] [drm:nv_drm_register_drm_device [nvidia_drm]] *ERROR* [nvidia-drm] [GPU ID 0x00000a00] Failed to register device
Et le GPU n'est pas utilisable (pas détecté par la commande nvidia-smi):
$ nvidia-smi
No devices were found
Ce message d’erreur vient de nvidia_drm au moment où le module DRM du pilote NVIDIA essaie d’initialiser le GPU en mode KMS (Kernel Mode Setting) et échoue à obtenir un device via NvKmsKapiDevice.
Ce qui veut dire que ma configuration sous QEMU/KVM avec OVMF ne permet pas à nvidia_drm d’activer le mode KMS, car la carte n’est pas initialisée comme sur un bare-metal.
Pour corriger cela il faut extraire la ROM du bios de la carte (dans la VM démarrée, avec la carte en passthrough):
- sur le host:
# echo 0000:01:00.0 > /sys/bus/pci/drivers/vfio-pci/unbind
# echo 0000:01:00.1 > /sys/bus/pci/drivers/vfio-pci/unbind
- dans la VM:
# echo 1 > /sys/bus/pci/devices/0000:01:00.0/rom
# cat /sys/bus/pci/devices/0000:01:00.0/rom > /root/gpu.rom
# echo 0 > /sys/bus/pci/devices/0000:01:00.0/rom
- sur le host:
# echo 0000:01:00.0 > /sys/bus/pci/drivers/vfio-pci/bind
# echo 0000:01:00.1 > /sys/bus/pci/drivers/vfio-pci/bind
Déplacer le fichier au bon endroit pour Qemu et donner les droits dessus:
# mkdir -p /usr/share/qemu/vbios
# mv /root/gpu.rom /usr/share/qemu/vbios/
# chown libvirt-qemu:kvm /usr/share/qemu/vbios/gpu.rom
Editer de nouveau le XML de la VM ia1 (virsh edit ia1) et ajouter la ligne rom ici:
<hostdev mode='subsystem' type='pci' managed='yes'>
<source>
<address domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
</source>
<address type='pci' domain='0x0000' bus='0x0a' slot='0x00' function='0x0'/>
<rom file='/usr/share/qemu/vbios/gpu.rom'>
</hostdev>
<hostdev mode='subsystem' type='pci' managed='yes'>
<source>
<address domain='0x0000' bus='0x01' slot='0x00' function='0x1'/>
</source>
<address type='pci' domain='0x0000' bus='0x0b' slot='0x00' function='0x0'/>
</hostdev>
Supprimer le device <video> de type virtio
(partie à supprimer :)
<video>
<model type='virtio' heads='1' primary='yes'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0'/>
</video>
Deuxième tentative de démarrage: c'est OK mais les drivers installés ne sont plus bons ! Or j'ai écrit plus haut que c'était les mêmes drivers, eh bien je me suis trompé: pour les cartes graphiques récentes, Nvidia ne fournit plus de prise en charge dans les drivers "nvidia-driver-xxx - distro non-free" mais plutôt dans la version "nvidia-driver-xxx-open - distro non-free".
Pour rectifier le tir, faire dans la VM:
# apt remove --purge nvidia-driver-575
# apt install nvidia-driver-575-open
# apt autoremove
On redémarre le système et on vérifie en tapant: nvidia-smi.
Mise à jour du driver nvidia, quand un plus récent est disponible. Exemple:
# ubuntu-drivers devices
# apt remove nvidia-driver-570
# apt autoremove
# apt install nvidia-driver-575
# reboot
Il faut donc d'abord supprimer l'ancien driver, puis installer le nouveau. C'est ce que j'ai fait lors du passage du 570 au 575.
Par contre lors d'un "apt upgrade" ultérieur je me suis retrouvé avec un changement du 575 au 580 incomplet et le GPU ne fonctionnait plus:
$ nvidia-smi
No devices were found
Dans ce cas là, procéder comme suit:
D'abord vérifier que le GPU est bien présent:
# lspci -nn | grep -i nvidia
09:00.0 VGA compatible controller [0300]: NVIDIA Corporation Device [10de:2b85] (rev a1)
0a:00.0 Audio device [0403]: NVIDIA Corporation Device [10de:22e8] (rev a1)
# lsmod | grep nvidia
nvidia_uvm 2150400 0
nvidia_drm 135168 0
nvidia_modeset 1736704 1 nvidia_drm
nvidia 11534336 2 nvidia_uvm,nvidia_modeset
drm_ttm_helper 16384 1 nvidia_drm
video 77824 1 nvidia_modeset
# dkms status
nvidia/575.64.03, 6.14.0-29-generic, x86_64: installed
nvidia/575.64.03, 6.14.0-33-generic, x86_64: installed
Tout est OK mais le module installé est toujours le 575 ; on vérifie qu'il n'y a pas d'erreur:
# dmesg | grep -iE "nvidia|nvrm|vfio"
On vérifie les drivers/firmwares installés:
# ls -l /lib/firmware/nvidia/575.64.03/
ls: cannot access '/lib/firmware/nvidia/575.64.03/': No such file or directory
==> problème.
# find /lib/firmware -name 'gsp_ga10x.bin'
/lib/firmware/nvidia/580.95.05/gsp_ga10x.bin
# dpkg -l | grep firmware
ii amd64-microcode 3.20250311.1ubuntu0.24.04.1 amd64 Processor microcode firmware for AMD CPUs
ii firmware-sof-signed 2023.12.1-1ubuntu1.10 all Intel SOF firmware - signed
ii intel-microcode 3.20250512.0ubuntu0.24.04.1 amd64 Processor microcode firmware for Intel CPUs
ii linux-firmware 20240318.git3b128b60-0ubuntu2.19 amd64 Firmware for Linux kernel drivers
ii nvidia-firmware-580-580.95.05 580.95.05-0ubuntu0.24.04.2 amd64 Firmware files used by the kernel module
# dpkg -l | grep nvidia
ii libnvidia-cfg1-575:amd64 575.64.03-0ubuntu0.24.04.1 amd64 NVIDIA binary OpenGL/GLX configuration library
ii libnvidia-common-575 575.64.03-0ubuntu0.24.04.3 all Shared files used by the NVIDIA libraries
ii libnvidia-common-580 580.95.05-0ubuntu0.24.04.2 all Shared files used by the NVIDIA libraries
ii libnvidia-compute-575:amd64 575.64.03-0ubuntu0.24.04.1 amd64 NVIDIA libcompute package
ii libnvidia-container-tools 1.18.0-1 amd64 NVIDIA container runtime library (command-line tools)
ii libnvidia-container1:amd64 1.18.0-1 amd64 NVIDIA container runtime library
ii libnvidia-decode-575:amd64 575.64.03-0ubuntu0.24.04.1 amd64 NVIDIA Video Decoding runtime libraries
ii libnvidia-egl-wayland1:amd64 1:1.1.13-1build1 amd64 Wayland EGL External Platform library -- shared library
ii libnvidia-encode-575:amd64 575.64.03-0ubuntu0.24.04.1 amd64 NVENC Video Encoding runtime library
ii libnvidia-extra-575:amd64 575.64.03-0ubuntu0.24.04.1 amd64 Extra libraries for the NVIDIA driver
ii libnvidia-fbc1-575:amd64 575.64.03-0ubuntu0.24.04.1 amd64 NVIDIA OpenGL-based Framebuffer Capture runtime library
ii libnvidia-gl-575:amd64 575.64.03-0ubuntu0.24.04.1 amd64 NVIDIA OpenGL/GLX/EGL/GLES GLVND libraries and Vulkan ICD
ii nvidia-compute-utils-575 575.64.03-0ubuntu0.24.04.1 amd64 NVIDIA compute utilities
ii nvidia-container-toolkit 1.18.0-1 amd64 NVIDIA Container toolkit
ii nvidia-container-toolkit-base 1.18.0-1 amd64 NVIDIA Container Toolkit Base
ii nvidia-dkms-575-open 575.64.03-0ubuntu0.24.04.1 amd64 NVIDIA DKMS package (open kernel module)
ii nvidia-driver-575-open 575.64.03-0ubuntu0.24.04.1 amd64 NVIDIA driver (open kernel) metapackage
ii nvidia-firmware-580-580.95.05 580.95.05-0ubuntu0.24.04.2 amd64 Firmware files used by the kernel module
ii nvidia-kernel-common-575 575.64.03-0ubuntu0.24.04.3 amd64 Shared files used with the kernel module
ii nvidia-kernel-common-580 580.95.05-0ubuntu0.24.04.2 amd64 Shared files used with the kernel module
ii nvidia-kernel-source-575-open 575.64.03-0ubuntu0.24.04.1 amd64 NVIDIA kernel source package
ii nvidia-prime 0.8.17.2 all Tools to enable NVIDIA's Prime
ii nvidia-settings 510.47.03-0ubuntu4 amd64 Tool for configuring the NVIDIA graphics driver
ii nvidia-utils-575 575.64.03-0ubuntu0.24.04.1 amd64 NVIDIA driver support binaries
ii screen-resolution-extra 0.18.3ubuntu0.24.04.1 all Extension for the nvidia-settings control panel
ii xserver-xorg-video-nvidia-575 575.64.03-0ubuntu0.24.04.1 amd64 NVIDIA binary Xorg driver
On a un mismatch, ce qu'on peut vérifier:
# apt policy nvidia-driver-575-open
nvidia-driver-575-open:
Installed: 575.64.03-0ubuntu0.24.04.1
Candidate: 575.64.03-0ubuntu0.24.04.3
Version table:
575.64.03-0ubuntu0.24.04.3 500
500 http://archive.ubuntu.com/ubuntu noble-updates/multiverse amd64 Packages
500 http://security.ubuntu.com/ubuntu noble-security/multiverse amd64 Packages
*** 575.64.03-0ubuntu0.24.04.1 100
100 /var/lib/dpkg/status
# apt policy nvidia-driver-580-open
nvidia-driver-580-open:
Installed: (none)
Candidate: 580.159.03-0ubuntu0.24.04.1
Version table:
580.159.03-0ubuntu0.24.04.1 500
500 http://archive.ubuntu.com/ubuntu noble-updates/restricted amd64 Packages
500 http://security.ubuntu.com/ubuntu noble-security/restricted amd64 Packages
# apt policy nvidia-firmware-580-580.95.05
nvidia-firmware-580-580.95.05:
Installed: 580.95.05-0ubuntu0.24.04.2
Candidate: 580.95.05-0ubuntu0.24.04.3
Version table:
580.95.05-0ubuntu0.24.04.3 500
500 http://archive.ubuntu.com/ubuntu noble-updates/restricted amd64 Packages
*** 580.95.05-0ubuntu0.24.04.2 500
500 http://security.ubuntu.com/ubuntu noble-security/restricted amd64 Packages
100 /var/lib/dpkg/status
On peut donc installer manuellement les paquets suivants:
# apt install nvidia-driver-580-open nvidia-dkms-580-open nvidia-utils-580
Puis faire de nouveau:
# apt upgrade
Et tout rentre dans l'ordre (après un reboot):
# nvidia-smi
Failed to initialize NVML: Driver/library version mismatch
NVML library version: 580.159
# reboot
$ nvidia-smi
Mon Jun 22 08:18:30 2026
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 580.159.03 Driver Version: 580.159.03 CUDA Version: 13.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 RTX 5090 Off | 00000000:09:00.0 Off | N/A |
| 0% 37C P8 11W / 575W | 36MiB / 32607MiB | 0% Default |
| | | N/A |
+-----------------------------------------+------------------------+----------------------+
+-----------------------------------------------------------------------------------------+
| Processes: |
| GPU GI CI PID Type Process name GPU Memory |
| ID ID Usage |
|=========================================================================================|
| No running processes found |
+-----------------------------------------------------------------------------------------+
--
TG - 08/2025 update 06/2026