
Une VM lente l'est rarement par manque de ressources. Elle l'est parce qu'un réglage par défaut la prive d'une capacité du matériel : un modèle de CPU qui masque la moitié des jeux d'instructions, un bus disque ancien, un mode de cache qui force chaque écriture à traverser le disque. Ce guide montre où regarder, dans quel ordre, et ce que chaque réglage coûte réellement.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Choisir entre host-model et host-passthrough, et ce que chacun interdit
- Lire la topologie que vous exposez à l'invité, sockets, cœurs et threads
- Savoir quand NUMA compte, et quand s'en occuper est une perte de temps
- Comparer les modes de cache disque, mesures à l'appui
- Mesurer avant de régler, plutôt que d'appliquer une recette
Prérequis
Section intitulée « Prérequis »- KVM/libvirt installé (guide installation)
- Une VM que vous pouvez arrêter et redéfinir, jamais une machine de production
- Les commandes
virshde base (guide virsh)
Le CPU : trois modes, trois compromis
Section intitulée « Le CPU : trois modes, trois compromis »C'est le réglage qui a le plus d'effet, et le plus mal compris. libvirt propose trois façons de décrire le processeur exposé à l'invité :
virsh domcapabilities | grep -oE "<mode name='[a-z-]+' supported='[a-z]+'"| Mode | Ce que voit l'invité | Migration |
|---|---|---|
custom | Un modèle nommé, figé, choisi par vous | Vers tout hôte qui sait l'émuler |
host-model | Le modèle connu le plus proche de l'hôte, plus des features | Vers un hôte au moins équivalent |
host-passthrough | Le processeur de l'hôte, tel quel | Vers un hôte identique seulement |
Ce que host-model fait vraiment, et qui surprend
Section intitulée « Ce que host-model fait vraiment, et qui surprend »host-model ne copie pas votre processeur : il part du modèle QEMU le plus
proche et lui ajoute les fonctions manquantes. L'écart est parfois
spectaculaire. Sur la machine qui a servi à écrire ce guide, un Intel i7-12650H
de 12e génération, libvirt déduit :
<model fallback='forbid'>Skylake-Client-IBRS</model>Un modèle Skylake, soit plusieurs générations en arrière, complété par
117 features déclarées require. Le résultat est fonctionnellement proche du
processeur réel, mais la liste est explicite, et c'est précisément ce qui
rend la migration possible : l'hôte de destination peut vérifier qu'il sait tout
fournir.
host-passthrough, lui, expose les 142 flags du processeur hôte sans
traduction. L'invité voit un vrai Alder Lake, avec avx_vnni, sha_ni,
vaes ou movdir64b, que le modèle Skylake ne déclare pas. Pour une charge qui
exploite ces instructions, chiffrement ou calcul vectoriel, l'écart est réel.
Déclarer le mode
Section intitulée « Déclarer le mode »<cpu mode='host-passthrough' check='none' migratable='on'/>En virt-install, l'option correspondante est --cpu host-passthrough. Et il
faut savoir que c'est déjà le défaut, ce qui surprend la plupart des
gens. Vérifiable sans rien créer, --print-xml se contentant d'afficher la
définition :
virt-install --name lecture --memory 512 --vcpus 1 --disk none \ --import --osinfo ubuntu24.04 --print-xml | grep '<cpu'<cpu mode="host-passthrough"/>Le résultat est identique avec --osinfo generic, ubuntu24.04 ou
rhel9.4 : le mode ne dépend pas du système invité. Toute machine créée
par virt-install sans --cpu explicite est donc en host-passthrough, et
ne pourra pas migrer vers un hôte au processeur différent. Personne ne l'a
décidé, et c'est le genre de contrainte qu'on découvre le jour où l'on veut
déplacer la machine.
Si votre parc est hétérogène, ou simplement si vous n'êtes pas certain que
ces machines resteront là où elles sont, posez --cpu host-model à la
création. Pour l'existant, la vérification tient en une commande :
virsh dumpxml ma-vm | grep -A2 '<cpu'La topologie : ce que l'invité croit avoir
Section intitulée « La topologie : ce que l'invité croit avoir »Attribuer quatre vCPU ne dit pas comment l'invité les voit. Sans topologie déclarée, il perçoit souvent quatre sockets d'un cœur chacun, ce qui est une machine qui n'existe pas. Certains logiciels licenciés au socket en tirent des conclusions coûteuses, et certains ordonnanceurs y perdent leurs repères.
<cpu mode='host-model'> <topology sockets='1' cores='4' threads='1'/></cpu>Le produit sockets × cores × threads doit valoir exactement le nombre de
vCPU déclarés, sinon le domaine refuse de démarrer. Déclarer threads='2'
n'a de sens que si vous exposez réellement des paires de threads du même
cœur physique.
NUMA : savoir quand cela ne sert à rien
Section intitulée « NUMA : savoir quand cela ne sert à rien »NUMA décrit des machines où la mémoire est plus lente selon le processeur qui y accède. Sur un serveur bi-socket, y placer une VM à cheval coûte cher. Sur un poste de travail, la question ne se pose pas.
virsh capabilities | grep -A4 '<topology>'L'hôte de ce guide rend un seul nœud, cell id='0', avec ses 16 CPU et
toute sa mémoire. Dans cette configuration, le pinning NUMA n'apporte rien :
il n'y a pas de mémoire lointaine à éviter. Vérifiez donc votre topologie
avant d'appliquer une recette trouvée pour un bi-socket, sous peine de
complexifier une configuration sans rien gagner.
La mémoire : les grandes pages travaillent peut-être déjà
Section intitulée « La mémoire : les grandes pages travaillent peut-être déjà »Les hugepages réduisent le coût de traduction d'adresses en couvrant 2 Mio par entrée au lieu de 4 Kio. Deux mécanismes coexistent, et on croit souvent n'avoir ni l'un ni l'autre :
grep -E 'HugePages_Total|Hugepagesize|AnonHugePages' /proc/meminfocat /sys/kernel/mm/transparent_hugepage/enabledSur cette machine, HugePages_Total vaut 0 : aucune page statique n'est
réservée. Pourtant AnonHugePages annonce 1,8 Gio déjà servis en grandes
pages, parce que le THP est en mode madvise et que QEMU les demande. Une
partie du gain est donc déjà acquise, sans configuration.
Réserver des hugepages statiques garde son intérêt pour une VM dont la latence doit être régulière : la mémoire est prise au démarrage de l'hôte et ne peut plus être fragmentée. Le coût est qu'elle devient indisponible pour tout le reste, réservée qu'elle soit utilisée ou non.
Le disque : le bus, puis le cache
Section intitulée « Le disque : le bus, puis le cache »C'est le poste où un mauvais réglage se voit le plus, et où deux choix indépendants se cumulent.
Choisir le bus
Section intitulée « Choisir le bus »| Bus | Quand |
|---|---|
virtio (virtio-blk) | Le défaut raisonnable : chemin court, peu de couches |
virtio-scsi | Dès qu'il faut beaucoup de disques, le discard fin ou des commandes SCSI |
sata, ide | Uniquement pour un invité sans pilote virtio, Windows sans driver |
Un disque en sata sur une VM Linux moderne est le cas classique de machine
lente sans raison : l'invité a les pilotes virtio, personne ne les lui a
donnés.
Choisir le mode de cache
Section intitulée « Choisir le mode de cache »Le mode de cache décide qui garantit qu'une écriture a atteint le disque.
Mesuré sur cet hôte avec qemu-img bench, 12 000 opérations de 64 Kio, cache
de page vidé entre chaque passe :
| Mode | Lecture | Écriture |
|---|---|---|
none | 0,017 s | 0,236 s |
writeback | 0,119 s | 0,299 s |
directsync | 0,113 s | 1,710 s |
L'écart qui compte est celui de la dernière ligne : directsync écrit sept
fois plus lentement que none. Il force chaque écriture jusqu'au support,
sans aucun cache intermédiaire, ce qui est le comportement le plus sûr et le
plus coûteux qui soit.
Ces trois modes ne sont pas les seuls. libvirt en expose six, et la documentation de Red Hat les définit par ce qu'ils font du cache de page de l'hôte et du cache d'écriture du disque :
| Mode | Ce qu'il fait | Pour qui |
|---|---|---|
none | Pas de cache côté hôte, mais l'écriture peut rester dans le cache du disque | Invités à forte activité disque, et compatible migration |
writethrough | Cache côté hôte, mais écriture poussée jusqu'au support | Peu d'invités, faible activité, ou support sans cache d'écriture |
writeback | Cache côté hôte, sans synchronisation immédiate | Le plus rapide, au prix du risque en cas de coupure |
directsync | Comme writethrough, mais contourne le cache de page | Quand la durabilité prime sur tout le reste |
unsafe | Tout est mis en cache et les demandes de synchronisation de l'invité sont ignorées | Installations jetables, jamais des données qui comptent |
default | Laisse la plateforme décider | Ce que vous avez si vous n'écrivez rien |
La ligne qui décide souvent est la première, et pas pour sa vitesse :
cache='none' est le seul mode que Red Hat présente comme compatible avec la
migration. Un choix de performance devient ici un choix d'architecture.
<driver name='qemu' type='qcow2' cache='none' io='native' discard='unmap'/>cache='none' évite en outre de garder deux fois les mêmes données en
mémoire, côté hôte et côté invité. discard='unmap' fait remonter les
suppressions de fichiers jusqu'à l'image, qui cesse ainsi de grossir
indéfiniment.
Le mode d'entrées-sorties, à ne pas confondre avec le cache
Section intitulée « Le mode d'entrées-sorties, à ne pas confondre avec le cache »L'attribut io est un réglage distinct, qui décide comment QEMU soumet
les opérations, pas ce qu'il en met en cache :
io | Mécanisme |
|---|---|
native | AIO du noyau avec entrées-sorties directes : le défaut de Red Hat Virtualization |
threads | Des threads en espace utilisateur, défaut historique de RHEL |
io_uring | L'interface récente du noyau, plus efficace mais dont la maturité dépend des versions en présence |
Les deux attributs ne se choisissent pas séparément, et ce n'est pas une
affaire de bonnes pratiques : QEMU refuse la combinaison incohérente. Un
io='native' posé sur un mode de cache qui passe par le cache de page fait
échouer le démarrage, avec un message qui ne laisse aucun doute :
aio=native was specified, but it requires cache.direct=on, which was not specified.cache.direct=on correspond, côté libvirt, aux modes none et
directsync. Retenez donc le couple : io='native' impose un cache qui
contourne la page cache. Les modes threads et io_uring, eux, s'accommodent
de n'importe quel réglage de cache.
IOThreads : sortir les entrées-sorties du thread principal
Section intitulée « IOThreads : sortir les entrées-sorties du thread principal »Par défaut, les opérations disque passent par la boucle d'événements principale de QEMU, partagée avec le reste de l'émulation. Les IOThreads sont, selon la documentation libvirt, des « threads de boucle d'événements dédiés » qui traitent les requêtes en bloc à part, « afin d'améliorer la montée en charge, en particulier sur un hôte ou un invité SMP portant de nombreux LUN ».
<domain> <iothreads>2</iothreads> <cputune> <iothreadpin iothread='1' cpuset='5,6'/> </cputune></domain><iothreadpin> épingle ensuite un IOThread donné sur des processeurs
précis, avec la même syntaxe que le pinning des vCPU. Le gain est réel sur
une machine qui porte plusieurs disques actifs ; il est négligeable sur
une VM à un seul disque peu sollicité, où ajouter des threads complique sans
rien apporter.
Mesurer avant de régler
Section intitulée « Mesurer avant de régler »L'erreur la plus fréquente est d'appliquer une liste de réglages sans avoir constaté le problème. Trois commandes suffisent à savoir où regarder :
virsh domstats ma-vm --vcpu --block --interfacevirsh cpu-stats ma-vm --totalvirsh domblkstat ma-vm vdadomstats donne le temps processeur consommé, les octets lus et écrits, les
paquets réseau. Une VM dont le temps CPU stagne pendant que les écritures
disque grimpent n'a pas un problème de processeur, quel que soit le nombre
de vCPU que vous lui ajouterez ensuite.
Contrôle de connaissances
Section intitulée « Contrôle de connaissances »Vérifiez que l'essentiel de ce guide est acquis. Les questions portent uniquement sur ce qui vient d'être expliqué ici.
Contrôle de connaissances
Validez vos connaissances avec ce quiz interactif
Informations
- Le chronomètre démarre au clic sur Démarrer
- Questions à choix multiples, vrai/faux et réponses courtes
- Vous pouvez naviguer entre les questions
- Les résultats détaillés sont affichés à la fin
Lance le quiz et démarre le chronomètre
Vérification
(0/0)Profil de compétences
Quoi faire maintenant
Ressources pour progresser
Des indices pour retenter votre chance ?
Nouveau quiz complet avec des questions aléatoires
Retravailler uniquement les questions ratées
Retour à la liste des certifications
À retenir
Section intitulée « À retenir »host-modelpart du modèle QEMU le plus proche, ici un Skylake pour un processeur de 12e génération, et lui ajoute 117 features : c'est ce qui rend la migration vérifiable.host-passthroughexpose les 142 flags réels, et interdit la migration vers un hôte au processeur différent.- Sans topologie déclarée, l'invité croit souvent avoir autant de sockets que de vCPU, une machine qui n'existe pas.
- NUMA ne sert à rien sur un hôte à un seul nœud : vérifiez avant d'appliquer une recette de bi-socket.
- Les grandes pages travaillent peut-être déjà pour vous, via le THP en
mode
madvise, sans qu'aucune page statique ne soit réservée. - Côté disque,
directsyncécrit sept fois plus lentement quenonesur ce banc : le mode de cache se choisit, il ne se subit pas. - Un disque
satasur une VM Linux moderne est une lenteur gratuite : les pilotes virtio sont déjà dans l'invité.
Pour aller plus loin
Section intitulée « Pour aller plus loin »- Accès distant à libvirt : Mesurer et régler une machine depuis votre poste, sans ouvrir de session dessus.
- KVM et Terraform : Figer ces réglages dans du code plutôt que de les reposer à la main.
- Dépannage réseau KVM : Quand la lenteur vient du réseau et non du disque.