Mythes sur l’affectation CPU dans Proxmox — Le réglage qui aggrave la latence

Cet article vous a aidé ?

Vous affectez des vCPU parce que vous voulez de la « déterminisme ». Puis la VM de base de données commence à hoqueter toutes les quelques minutes, votre latence p95 double,
et quelqu’un finit par dire : « Mais on l’a pinée, donc ce ne peut pas être le CPU. »

L’affectation CPU dans Proxmox peut être un outil de performance. Elle peut aussi être une attaque par déni de service auto-infligée sur votre propre ordonnanceur.
L’astuce est de savoir quel type de « pinning » vous avez réellement fait, ce qu’il a volé au reste du système, et quelle latence vous venez d’empirer.

Le mythe : affecter = latence plus faible

L’affectation est vendue comme « empêcher la VM de sauter d’un cœur à l’autre ». Ce n’est pas faux. Ce n’est pas non plus le plus important.
Sur Linux moderne avec KVM, l’ordonnanceur est déjà plutôt bon pour garder les threads chauds sur les mêmes cœurs.
Il est encore meilleur pour emprunter du CPU disponible n’importe où lorsqu’un pic arrive.

La plupart des gens affectent parce qu’ils ont un problème qu’ils ne voient pas : voisins bruyants, contention sur l’hôte, tempêtes d’interruptions,
pénalités de mémoire NUMA ou sursouscription. L’affectation donne l’impression de contrôle. Et parfois elle en donne.
Mais si vous affectez sans comprendre où vont les cycles, vous transformez un système flexible et adaptatif en une série
de petites cages. La latence s’aggrave alors pour deux raisons :

  • Vous supprimez les échappatoires de l’ordonnanceur. Quand un CPU est occupé (ou préempté par une interruption), la VM ne peut pas s’exécuter ailleurs.
  • Vous concentrez les collisions. Les threads vCPU de la VM, l’émulation QEMU, vhost-net et la maintenance de l’hôte peuvent se disputer les mêmes cœurs.

L’affectation n’est pas un « mode performance ». C’est un contrat. Vous promettez ces cœurs à la VM. En retour, vous devez garder ces cœurs propres :
peu d’interruptions, fréquence stable, localité mémoire prévisible et suffisamment de marge. La plupart des environnements signent le contrat puis
oublient de payer.

Ce qu’est vraiment l’affectation CPU (et ce que Proxmox modifie réellement)

Trois choses différentes que l’on appelle « affectation »

Dans l’univers Proxmox, « affectation » sert à décrire au moins trois mécanismes distincts. Les confondre est le point de départ du folklore.

  1. Affinité des threads QEMU/KVM (à la taskset). Vous liez les threads vCPU de la VM aux CPUs de l’hôte. C’est le classique « pinning vCPU ».
  2. cpuset cgroups (partitionnement strict). Vous contraignez l’arbre de processus de la VM à un ensemble de CPUs. Plus fort que l’affinité ; vous l’enfermez.
  3. Isolation CPU pour l’hôte (paramètres de démarrage du noyau). Vous réservez des CPUs pour des charges spécifiques en repoussant le travail général de l’hôte
    (callbacks RCU, kworkers, nombreuses interruptions). C’est la partie que les gens zappent.

Ce que Proxmox expose vs ce que Linux planifie réellement

Proxmox vous donne des réglages comme CPU units, CPU limit, NUMA, et parfois « affinity » via args ou hooks. En réalité,
votre VM est un processus QEMU avec plusieurs threads : un par vCPU, plus des threads I/O, plus l’émulation, plus des threads vhost selon le modèle de périphérique.
Linux planifie des threads, pas des « VMs ».

Quand vous affectez « la VM », vous affectez en fait un sous-ensemble de ces threads.
Si vous n’affectez que les threads vCPU et oubliez le thread I/O, vous pouvez toujours être bouché sur le thread non affecté qui tourne sur un cœur chargé.
Si vous mettez tout sur le même petit ensemble, vous créez un autocuiseur de pression de latence.

L’affectation change l’équité, pas la physique

L’affectation ne rend pas les CPUs plus rapides. Elle change qui est autorisé à tourner où.
Cela compte parce que le rôle de l’ordonnanceur Linux est de répartir le travail exécutable sur les CPUs disponibles tout en maintenant l’équité
et la localité de cache. Si vous le restreignez, vous devez vous assurer que votre ensemble restreint a :

  • Assez de cycles CPU en charge maximale
  • Fréquence stable (pas de forte baisse pendant un pic)
  • Comportement d’interruptions acceptable
  • Localité NUMA acceptable

Le réglage qui aggrave la latence : contraindre l’ordonnanceur sans isoler l’hôte

Voici le schéma de réglage qui ruine discrètement la latence : affecter des vCPU (ou utiliser des contraintes cpuset) sur un hôte Proxmox
qui continue de planifier la maintenance de l’hôte et les interruptions sur ces mêmes cœurs
.

Vous vouliez des « cœurs dédiés ». Vous avez obtenu des « cœurs partagés avec moins d’options ». La VM ne peut pas échapper à un cœur occupé, mais le cœur est occupé
parce que l’hôte y exécute encore des tâches : gestion des interruptions, kworkers du noyau, maintenance ZFS, softirqs du réseau,
et tout autre VM qui n’était pas aussi « spéciale » que la VM pinée.

Le résultat est la latence de queue classique. La médiane semble correcte. p95/p99 devient moche. Et la laideur corrèle souvent avec :

  • Pics réseau (sauts de temps softirq)
  • Pics de stockage (kworker, sync txg ZFS, complétions IO)
  • Activités périodiques du noyau (RCU, ticks si le noyau n’est pas tickless)
  • Transitions de mise à l’échelle de fréquence (comportement boost modifié par chaleur/alim)

Pourquoi ce schéma est pire que de ne rien faire

Sans affectation, les threads vCPU de la VM peuvent migrer hors d’un cœur temporairement mauvais. L’ordonnanceur peut répartir le travail.
Quand vous affectez, vous convertissez une interférence hôte transitoire en un arrêt dur : le vCPU est exécutable, mais ne peut pas être planifié sur un CPU non bloqué.
Ce n’est pas du « déterminisme ». C’est « la file la plus courte au supermarché, mais c’est la seule que vous avez le droit d’utiliser. »

Blague n°1 : L’affectation CPU, c’est comme attribuer à chacun dans le bureau un seul ascenseur. C’est très ordonné jusqu’à ce que quelqu’un arrive avec un chariot.

Que faire à la place (la plupart du temps)

Si votre objectif est de réduire la latence, ne commencez pas par l’affectation. Commencez par :

  • Capacité et marge : arrêter de sursouscrire les CPUs pour les VMs sensibles à la latence
  • Correction NUMA : garder vCPUs et mémoire locaux, ou accepter la pénalité en connaissance de cause
  • Hygiène des interruptions : s’assurer que les « cœurs dédiés » de la VM ne reçoivent pas les pires IRQs de l’hôte
  • Politique de fréquence CPU : choisir un governor adapté à votre charge (et vérifier)

L’affectation devient raisonnable quand elle fait partie d’un ensemble : isolation CPU + affinité IRQ + alignement NUMA + dimensionnement vCPU sensé.
L’affectation seule, c’est comme acheter un pneu de course et le mettre sur un caddie.

Faits intéressants et courte histoire (pourquoi ça revient)

  • Fait 1 : L’affinité CPU existe dans Linux depuis des décennies, mais elle est devenue courante avec les systèmes SMP puis explosive avec la virtualisation.
  • Fait 2 : KVM n’est pas un ordonnanceur d’hyperviseur séparé ; c’est un module noyau. Votre « ordonnancement hyperviseur » dépend largement de l’ordonnanceur Linux.
  • Fait 3 : NUMA est une mine de problèmes depuis que les serveurs multi-socket sont devenus courants ; l’accès mémoire distant peut ressembler à des pics de latence aléatoires.
  • Fait 4 : Les noyaux « tickless » (NO_HZ) ont réduit les interruptions périodiques de timer, ce qui compte quand vous chassez des micro-arrêts sur des CPUs isolés.
  • Fait 5 : irqbalance a été créé pour répartir les interruptions entre CPUs pour le débit, pas pour protéger vos cœurs basse-latence des IRQ bruyantes.
  • Fait 6 : En virtualisation, le vCPU est un thread hôte. S’il est préempté par un long softirq, votre invité le ressent comme un « CPU steal » ou simplement comme un ralentissement.
  • Fait 7 : L’essor du NVMe a réduit la latence stockage suffisamment pour que la planification CPU et les interruptions deviennent le nouveau goulot d’étranglement dans de nombreuses piles.
  • Fait 8 : SMT/Hyper-Threading complique l’affectation parce que deux « CPUs » partagent des ressources d’exécution ; pinner sur des siblings peut augmenter le jitter sous contention.
  • Fait 9 : Les cgroups ont évolué des CPU shares vers des contrôleurs plus précis ; cpuset est puissant et facile à mal utiliser comme un marteau-pilon dans un atelier d’horlogerie.

Comment l’affectation casse : principaux modes de défaillance de latence

1) Vous avez affecté les vCPU sur des CPUs qui sont des hotspots d’interruptions

Une VM sensible à la latence, affectée sur un cœur qui reçoit les interruptions de la carte réseau, est une forme particulière d’automutilation.
En charge, le traitement softirq peut dominer. Votre thread vCPU est exécutable, mais le CPU est occupé à faire le travail réseau pour l’hôte.
L’invité subit des pauses et du jitter aléatoires.

2) Vous avez affecté à travers des nœuds NUMA sans contrôler la localité mémoire

Vous pouvez affecter des vCPU sur des CPUs de deux sockets pendant que la mémoire invitée est majoritairement allouée sur un seul nœud. Maintenant la moitié de vos vCPU
effectue des accès mémoire distants. La mémoire distante n’est pas toujours catastrophique, mais elle est rarement stable. Vous pouvez obtenir des pics de queue quand la bande passante distante sature.

3) Vous avez affecté, puis sursouscrit quand même

L’affectation ne corrige pas la sursouscription ; elle la rend plus rigide. Si vous avez affecté plusieurs VMs chargées sur des ensembles CPU qui se chevauchent,
vous avez créé de la contention que vous ne pouvez pas planifier. C’est comme ça que vous obtenez « tout va bien jusqu’à 10:02, puis tout part en fumée. »

4) Vous avez oublié les « autres threads » (thread I/O, threads vhost, émulateur)

Une VM effectuant beaucoup de stockage ou de réseau peut être bloquée par un thread I/O QEMU ou par des threads vhost.
Si vous affectez les vCPU mais pas le chemin I/O, vous pouvez finir avec des vCPU qui attendent un thread non affecté et surchargé.
Ce n’est pas un problème CPU dans l’invité. C’est un problème d’architecture sur l’hôte.

5) Collisions entre siblings SMT

Affecter une VM à « deux cœurs » qui sont en réalité deux threads du même cœur physique peut réduire le débit et augmenter le jitter.
Pour les charges sensibles à la latence, vous voulez typiquement des cœurs complets, pas des siblings, sauf si vous avez une raison mesurée et des preuves.

6) Mise à l’échelle de fréquence et limites d’alimentation

L’affectation réduit la mobilité de l’ordonnanceur, ce qui peut interagir négativement avec le comportement de boost.
Si vos CPUs affectés sont ceux qui baissent de fréquence en premier sous contrainte thermique ou d’alimentation,
vous verrez des ralentissements qui semblent être des « latences aléatoires ». Ce ne sont pas aléatoires ; ce sont des politiques.

Citation (idée paraphrasée), attribuée : Werner Vogels répète souvent un principe de fiabilité : « Tout échoue ; concevez pour que ce soit sûr et récupérable quand ça arrive. »
L’affectation est aussi une décision de fiabilité — rendez-la récupérable, pas fragile.

Mode d’emploi pour un diagnostic rapide

Quand la latence monte sur un hôte Proxmox avec des VMs « pinées », vous voulez des réponses en minutes, pas après une semaine d’art performance interprétatif.
Voici l’ordre qui trouve généralement le coupable le plus vite.

Première étape : prouver que c’est la planification/les interruptions CPU, pas le stockage

  1. Vérifiez la saturation CPU de l’hôte et les symptômes de type steal : cherchez l’arriéré exécutable et l’utilisation CPU par CPU.
  2. Vérifiez le temps softirq/irq : si softirq est élevé sur les CPUs affectés, c’est votre coupable.
  3. Vérifiez la latence stockage : si les disques vont bien et que le CPU ne l’est pas, arrêtez de blâmer ZFS par habitude.

Deuxième étape : vérifier que l’affectation est réelle et complète

  1. Quels threads sont affectés (vCPU seulement ? tous les threads QEMU ?)
  2. Les CPUs affectés sont-ils partagés avec d’autres VMs ou du travail hôte ?
  3. Affichez-vous des affectations sur des siblings SMT par accident ?

Troisième étape : vérifier la localité NUMA

  1. Les vCPUs sont-ils répartis entre plusieurs nœuds ?
  2. La mémoire est-elle allouée sur les mêmes nœuds ?
  3. La VM est-elle suffisamment grande pour que les pénalités de mémoire distante soient inévitables ?

Quatrième étape : confirmer le comportement puissance/fréquence

  1. Governor et fréquences actuelles sous charge
  2. Limites d’alimentation/événements de throttling thermique

Cinquième étape : n’ajustez l’affectation qu’après

L’affectation est l’étape finale parce qu’elle est facile à changer et difficile à raisonner après coup.
Diagnostiquez d’abord ; puis resserrez les contraintes par petites étapes réversibles.

Tâches pratiques : commandes, sorties et décisions (12+)

Voici les vérifications que j’exécute sur un hôte Proxmox quand quelqu’un me dit : « Nous avons piné des CPUs et la latence a empiré. »
Chaque tâche inclut : une commande, ce que la sortie signifie, et la décision à prendre.

Task 1: Identify the VM’s QEMU process and threads

cr0x@server:~$ pgrep -a qemu-system
21433 /usr/bin/kvm -id 101 -name vm-db01 -m 32768 -smp 8,sockets=1,cores=8,threads=1 ...

Signification : Vous avez le PID de la VM. L’affectation impacte ce processus et ses threads.
Décision : Utilisez le PID 21433 pour les vérifications d’affinité et d’ordonnancement suivantes.

Task 2: See per-thread CPU usage (find vCPU threads, iothread, vhost)

cr0x@server:~$ top -H -p 21433 -b -n 1 | head -n 20
top - 10:12:41 up 32 days,  2:10,  2 users,  load average: 6.12, 5.88, 5.77
Threads:  36 total,   6 running,  30 sleeping,   0 stopped,   0 zombie
%Cpu(s): 62.3 us,  7.2 sy,  0.0 ni, 28.9 id,  1.1 wa,  0.0 hi,  0.5 si,  0.0 st
  PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
21433 root      20   0 12.3g  1.1g  19772 S  10.0   3.6   2:41.33 kvm
21445 root      20   0 12.3g  1.1g  19772 R  98.7   3.6  22:18.91 CPU 0/KVM
21446 root      20   0 12.3g  1.1g  19772 R  95.4   3.6  21:55.07 CPU 1/KVM
21447 root      20   0 12.3g  1.1g  19772 S  12.3   3.6   1:10.10 IOThread

Signification : Les threads vCPU (CPU 0/KVM, CPU 1/KVM, etc.) et un IOThread sont visibles. IOThread travaille aussi.
Décision : Si vous avez piné seulement les vCPUs mais que IOThread est chargé, vous devez envisager d’affecter IOThread (ou lui donner un CPU propre).

Task 3: Check current CPU affinity for the QEMU process

cr0x@server:~$ taskset -cp 21433
pid 21433's current affinity list: 2-5

Signification : Le processus QEMU est contraint aux CPUs 2-5. Ce n’est que quatre CPUs d’espace de planification.
Décision : Confirmez que cela correspond au nombre de vCPU prévu et que les CPUs 2-5 ne sont pas chargés d’interruptions ou partagés avec d’autres tâches critiques.

Task 4: Check affinity per thread (this is where “we pinned it” often falls apart)

cr0x@server:~$ for t in $(ls /proc/21433/task); do echo -n "$t "; taskset -cp $t | sed 's/^.*: //'; done | head
21433 2-5
21445 2-5
21446 2-5
21447 0-31
21448 0-31

Signification : Les threads vCPU sont pinés sur 2-5, mais IOThread (21447) et un autre thread d’aide ne le sont pas.
Décision : Soit pinzer intentionnellement tous les threads QEMU pertinents, soit accepter que les threads non pinés puissent créer un goulot et du jitter.

Task 5: Find which CPUs are SMT siblings (avoid accidental half-core pinning)

cr0x@server:~$ lscpu -e=CPU,CORE,SOCKET,NODE | head
CPU CORE SOCKET NODE
0   0    0      0
1   0    0      0
2   1    0      0
3   1    0      0
4   2    0      0
5   2    0      0

Signification : Les CPUs 2 et 3 partagent CORE 1, les CPUs 4 et 5 partagent CORE 2. Si vous pinz sur 2-5, vous utilisez deux cœurs physiques (avec SMT).
Décision : Pour les VMs sensibles à la latence, préférez pinzer sur des cœurs complets (par ex. 2,4,6,8…) plutôt que des paires siblings, sauf mesure contraire.

Task 6: Check interrupts per CPU (catch IRQ hotspots)

cr0x@server:~$ egrep -i 'CPU|eth0|nvme|mlx|ixgbe|virtio' /proc/interrupts | head -n 15
           CPU0       CPU1       CPU2       CPU3       CPU4       CPU5
  45:          0          0    9123456          0          0          0  IR-PCI-MSI 524288-edge  eth0-TxRx-0
  46:          0          0          0    8876543          0          0  IR-PCI-MSI 524289-edge  eth0-TxRx-1
  97:      11234      11876      10987      11022      11301      11450  IR-PCI-MSI 0000:01:00.0  nvme0q0

Signification : CPU2 et CPU3 reçoivent la majorité des interruptions NIC. Si votre VM est pinée sur 2-5, la moitié de sa capacité « dédiée » gère des paquets.
Décision : Déplacez l’affectation de la VM loin des CPUs chargés d’IRQ, ou déplacez l’affinité IRQ loin des CPUs de la VM. Ne partagez pas par accident.

Task 7: Check softirq pressure per CPU (network bursts show up here)

cr0x@server:~$ awk 'NR==1||/NET_RX|NET_TX|SCHED|RCU/ {print}' /proc/softirqs
                    CPU0       CPU1       CPU2       CPU3       CPU4       CPU5
NET_RX            123456     234567    9988776    8877665     345678     456789
NET_TX             98765      87654    5432109    4321098      76543      65432
SCHED            456789     567890     678901     789012     890123     901234
RCU               34567      45678      56789      67890      78901      89012

Signification : NET_RX/NET_TX sont beaucoup plus élevés sur CPU2/CPU3. C’est cohérent avec le mapping d’IRQ.
Décision : Si vos pics de latence corrèlent avec l’activité réseau, traitez le placement IRQ/softirq comme un élément de tuning prioritaire avant de toucher l’affectation.

Task 8: Check load vs runnable threads (are you CPU-starved on pinned CPUs?)

cr0x@server:~$ vmstat 1 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 7  0      0 812344  90212 4231120    0    0     2    31 8123 19022 64  9 26  1  0
 9  0      0 811920  90212 4231400    0    0     0     0 9100 22011 71 11 17  1  0

Signification : La colonne « r » (runnable) est élevée. Si la VM est pinée à un petit ensemble CPU, l’arriéré exécutable peut y être concentré.
Décision : Si l’arriéré excède les CPUs disponibles de façon persistante, désaffectez ou étendez l’ensemble CPU, ou réduisez le nombre de vCPU pour correspondre à la capacité réelle.

Task 9: See per-CPU utilization and steal-like stalls

cr0x@server:~$ mpstat -P ALL 1 3
Linux 6.2.16-xx (pve01)  02/04/2026  _x86_64_  (32 CPU)

12:14:01 PM  CPU   %usr %nice %sys %iowait %irq %soft %steal %idle
12:14:02 PM    2   68.0  0.0  9.0    0.0   2.0  21.0    0.0   0.0
12:14:02 PM    3   61.0  0.0  8.0    0.0   1.0  29.0    0.0   1.0
12:14:02 PM    4   55.0  0.0  6.0    0.0   0.0   4.0    0.0  35.0

Signification : CPU2/CPU3 ont un %soft énorme. Ces CPUs exécutent des softirqs réseau au lieu d’exécuter vos vCPUs pinés.
Décision : Rééquilibrez les IRQs ou déplacez la VM. Affecter sur des CPUs chargés de softirq est la façon dont vous manufacturez du jitter.

Task 10: Confirm NUMA topology and whether the VM spans nodes

cr0x@server:~$ numactl -H | head -n 20
available: 2 nodes (0-1)
node 0 cpus: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
node 0 size: 128755 MB
node 0 free:  42110 MB
node 1 cpus: 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
node 1 size: 128705 MB
node 1 free:  39876 MB

Signification : Deux nœuds NUMA. Si vous affectez une VM aux CPUs répartis sur les deux nœuds sans garantir la localité mémoire, des accès distants auront lieu.
Décision : Gardez les VMs sensibles à la latence dans un seul nœud NUMA quand possible, ou activez/vérifiez la configuration aware-NUMA et mesurez.

Task 11: Inspect the QEMU process NUMA memory policy (is memory local?)

cr0x@server:~$ grep -E 'Mems_allowed_list|Cpus_allowed_list' /proc/21433/status
Cpus_allowed_list:	2-5
Mems_allowed_list:	0-1

Signification : L’ensemble CPU est 2-5 (nœud 0), mais la mémoire est autorisée sur les deux nœuds. Cela peut provoquer des allocations mémoire croisées selon la pression.
Décision : Si vous pinz serré, contrôlez aussi la localité mémoire. Sinon vous obtenez la pire combinaison : CPUs fixes, mémoire errante.

Task 12: Check CPU governor and current frequency behavior

cr0x@server:~$ cat /sys/devices/system/cpu/cpu2/cpufreq/scaling_governor
powersave

Signification : Le governor est « powersave ». Cela peut aller sur certaines plateformes, mais c’est souvent mauvais pour les charges sensibles à la latence et en rafales.
Décision : Envisagez de passer en « performance » (ou une politique ajustée) pour les hôtes qui exécutent des VMs critiques pour la latence, puis vérifiez la fréquence sous charge.

Task 13: Check for throttling / power limit hints (when available)

cr0x@server:~$ dmesg -T | egrep -i 'thrott|powercap|thermal' | tail -n 5
[Mon Feb  3 22:11:52 2026] thermal thermal_zone0: critical temperature reached (97 C), shutting down
[Mon Feb  3 22:11:05 2026] CPU0: Package temperature above threshold, cpu clock throttled

Signification : Si vous voyez du throttling, votre « tuning d’affectation » réarrange des chaises longues. Le CPU ralentit littéralement.
Décision : Réglez le refroidissement/les limites d’alimentation avant d’optimiser finement le comportement de l’ordonnanceur.

Task 14: Check ZFS latency and txg behavior (so storage doesn’t get blamed incorrectly)

cr0x@server:~$ zpool iostat -v 1 3
              capacity     operations     bandwidth
pool        alloc   free   read  write   read  write
rpool       1.12T  2.38T    120    310   8.2M  21.1M
  nvme0n1   1.12T  2.38T    120    310   8.2M  21.1M

Signification : Aucun signe évident de saturation stockage dans cet instantané. Si la latence VM pique pendant que ZFS est calme, revenez au CPU/interrupts.
Décision : Ne « réparez pas ZFS » simplement parce que c’est ZFS. Réparez le goulot que vous pouvez mesurer.

Task 15: Confirm Proxmox VM CPU configuration (vCPU count, sockets/cores)

cr0x@server:~$ qm config 101 | egrep 'name|cores|sockets|cpu|numa|balloon'
name: vm-db01
cores: 8
sockets: 1
cpu: x86-64-v2-AES
numa: 1
balloon: 0

Signification : NUMA est activé, 8 cœurs configurés. Si vous avez piné le processus sur 4 CPUs hôtes (Task 3), vous avez créé un décalage évident.
Décision : Alignez le dimensionnement vCPU et l’affectation. Si vous voulez 8 vCPU, donnez-lui de la place pour 8, ou réduisez le nombre de vCPU pour correspondre à la réalité.

Trois mini-récits d’entreprise depuis les tranchées du pinning

Mini-récit 1 : L’incident causé par une fausse hypothèse (« Piné = dédié »)

Une entreprise SaaS de taille moyenne utilisait Proxmox pour des services internes : runners CI, stockage d’artefacts, monitoring, quelques clusters de bases de données.
Un jour, la VM base de données principale a commencé à montrer des blocages périodiques de requêtes. Pas des requêtes lentes — des blocages. Les connexions restaient figées une ou deux secondes,
puis reprenaient. Les graphiques ressemblaient à une dent de scie : calme, pic, calme, pic.

L’équipe avait récemment « durci les performances » en pinant les vCPU de la VM base de données sur quatre CPUs hôtes. L’hypothèse était simple :
piné = dédié, dédié = stable. Ils supposaient aussi que si la VM avait quatre CPUs pinés, elle ne pouvait pas être impactée par d’autres VMs.
Cette hypothèse est la raison pour laquelle on passe l’après-midi à lire /proc comme un roman policier.

La cause racine n’était ni la base de données, ni la configuration de la VM. C’était le placement des interruptions.
Les queues RX/TX les plus occupées de la NIC tombaient sur les mêmes CPUs sur lesquels la VM était pinée. Sous trafic de sauvegarde et uploads d’artefacts CI,
le temps softirq montait sur ces cœurs. Les threads vCPU étaient exécutables, mais « leurs » CPUs étaient occupés à gérer des paquets.
L’invité le ressentait comme des pauses aléatoires.

La correction fut ennuyeuse : déplacer l’affinité IRQ loin des CPUs de la VM, et arrêter de prétendre que le pinning est de l’isolation.
Ils ont aussi étendu l’ensemble CPU autorisé de la VM pour inclure quelques cœurs propres supplémentaires, sacrifiant un peu de localité de cache pour la capacité d’échapper.
Les blocages ont disparu. Le post-mortem : pinning est un engagement envers l’hôte, pas une protection contre lui.

Mini-récit 2 : L’optimisation qui a mal tourné (« On a piné pour la chaleur du cache »)

Une autre organisation exécutait des brokers de messages sensibles à la latence dans des VMs. Ils voulaient réduire la latence de queue pendant les pics de trafic.
Quelqu’un a proposé de pinner chaque VM broker sur un petit ensemble de CPUs « pour la chaleur du cache ». Le plan était joli sur un tableau blanc :
deux VMs par socket, chacune avec vCPUs pinés, pas d’errance.

Ça a marché dans un test synthétique. Puis la production est arrivée. Le trafic réel n’est pas un benchmark ; c’est une série de petites catastrophes horodatées.
Pendant les rafales, une VM broker saturait ses CPUs pinés et construisait une file. L’ordonnanceur ne pouvait pas emprunter du temps idle d’autres CPUs
parce que le processus était contraint. La latence montait puis restait élevée plus longtemps que prévu, car la file avait besoin de temps pour se résorber.

Pendant ce temps, d’autres CPUs de l’hôte étaient partiellement inactifs. L’hôte avait de la capacité ; la VM n’était pas autorisée à l’utiliser.
L’optimisation a échangé « caches chauds » contre « murs durs ». Les caches chauds sont agréables. Les messages perdus et les timeouts le sont moins.

Le rollback a été instructif : ils ont supprimé le pinning strict, gardé des partages CPU sensés, et se sont concentrés sur la réduction de la contention de l’hôte :
moins de voisins bruyants par nœud, meilleure distribution IRQ, et placement aware-NUMA.
Leur latence médiane a à peine changé. Leur latence de queue s’est améliorée. Le résultat n’était pas héroïque ; il était prévisible.

Mini-récit 3 : La pratique ennuyeuse mais correcte qui a sauvé la mise (« Mesurer, puis isoler ce qui compte seulement »)

Une société proche de la finance exécutait un cluster Proxmox pour des charges mixtes : jobs batch, applis web internes, quelques services sensibles à la latence.
Ils avaient une pratique qui sonnait ennuyeuse en réunion : tout « changement de performance » nécessitait une capture avant/après du CPU, des interruptions,
et de la latence stockage au niveau hôte, plus un plan de rollback exécutable en minutes.

Pendant une période de pic, une VM critique a commencé à montrer du jitter. La réaction instinctive d’une équipe applicative a été d’exiger le pinning CPU.
L’équipe infra n’a pas refusé — poliment — mais a insisté pour faire leur capture d’abord.
En 15 minutes ils ont trouvé le problème : la VM allait bien ; l’hôte avait un pic périodique de softirq causé par un changement dans les réglages de queues NIC.

Ils ont corrigé la distribution NIC/IRQ, vérifié que le temps softirq revenait à la normale, et le jitter VM a disparu sans toucher à l’affinité vCPU.
Personne n’a pu se vanter d’un « tuning ». Le système s’est simplement remis à bien se comporter, ce qui est tout l’objet.

La pratique ennuyeuse qui les a sauvés était procédurale : mesurer la contention hôte d’abord, traiter le pinning comme un outil chirurgical,
et garder les changements réversibles. Cela a évité une semaine d’expériences superstitieuses de pinning et le genre de dérive « marche sur un hôte » qui rend les clusters difficiles à exploiter.

Blague n°2 : Pinner sans mesurer, c’est comme régler votre montre en la regardant plus fort. On se sent productif ; le temps reste impassible.

Erreurs courantes : symptômes → cause racine → correction

1) Symptom: p99 latency spikes after pinning, while average CPU looks fine

Cause racine : Vous avez piné sur des CPUs chargés d’IRQ/softirq, ou vous avez contraint la VM si bien qu’elle ne peut pas échapper à une interférence transitoire de l’hôte.

Correction : Déplacez l’affinité IRQ loin de ces CPUs et/ou élargissez l’ensemble CPU autorisé. Si vous avez vraiment besoin de « cœurs dédiés », mettez en place une isolation CPU correctement.

2) Symptom: VM feels “stuttery” during network traffic bursts

Cause racine : Le traitement softirq (NET_RX/NET_TX) vole du temps à vos vCPUs pinés.

Correction : Rééquilibrez les queues/IRQs NIC, envisagez les réglages RSS, et gardez les VMs sensibles à la latence loin de ces cœurs.

3) Symptom: You pinned an 8-vCPU VM but it behaves like a 4-vCPU VM

Cause racine : L’affinité du processus QEMU limitée à moins de CPUs hôtes que le nombre de vCPU, ou des siblings SMT déguisés en « cœurs ».

Correction : Alignez le nombre de vCPU avec les cœurs physiques (pas les threads) et l’ensemble CPU autorisé. Réduisez le nombre de vCPU si vous ne pouvez pas fournir la capacité réelle.

4) Symptom: Performance is inconsistent across hosts in the same cluster

Cause racine : Paramètres BIOS d’alimentation différents, governors CPU, microcode, ou dispositions IRQ différentes. L’affectation amplifie ces différences.

Correction : Standardisez le firmware, les paramètres du noyau et la politique de performance across nœuds. Validez avec les mêmes commandes au niveau hôte.

5) Symptom: Storage latency blamed, but disks look idle

Cause racine : Contention CPU dans le chemin de complétion I/O (kworkers, softirq, vhost), pas la latence média.

Correction : Mesurez %soft et l’utilisation par thread ; assurez-vous que les threads I/O ne sont pas coincés sur des CPUs bondés ; vérifiez le placement IRQ pour NVMe et NIC.

6) Symptom: “Pinning helped once, then got worse after a kernel update”

Cause racine : L’ordonnanceur, les valeurs par défaut d’IRQ ou le comportement des drivers ont changé ; votre pinning dépend d’hypothèses fragiles.

Correction : Re-validez les interruptions, les softirqs et la topologie CPU après les mises à jour. Traitez les configurations de pinning comme des artefacts par noyau/plateforme, pas des vérités éternelles.

7) Symptom: VM is fast until another VM on the host gets busy

Cause racine : Ensembles pin qui se chevauchent ou siblings SMT partagés. Vous avez construit des voisins bruyants dans les mêmes ressources physiques.

Correction : Assurez-vous que les ensembles CPU ne se chevauchent pas pour les VMs critiques, et évitez la contention de siblings. Si vous ne pouvez pas éviter le chevauchement, ne pinnez pas ; fiez-vous aux shares et à la marge.

Listes de contrôle / plan pas à pas

Checklist A: Decide if pinning is even warranted

  1. L’hôte est-il sursouscrit en CPU ? Si oui, corrigez cela d’abord. L’affectation n’invente pas de capacité.
  2. Avez-vous une interférence IRQ/softirq prouvée ? Si oui, corrigez le placement IRQ avant d’affecter.
  3. La localité NUMA est-elle actuellement cassée ? Si oui, corrigez le placement et la localité mémoire avant d’affecter.
  4. La charge est-elle sensible à la latence ou au débit ? Si c’est du débit, l’affectation réduit souvent le débit maximum en limitant la flexibilité d’ordonnancement.
  5. Pouvez-vous revenir en arrière rapidement ? Si non, ne le faites pas en production. Les changements d’affectation sont dangereusement trompeurs parce qu’ils « semblent fonctionner » tout en étant faux.

Checklist B: If you pin, do it as a bundle (the “make it true” plan)

  1. Sélectionnez des cœurs physiques complets (évitez les siblings SMT pour l’ensemble primaire sauf mesure contraire).
  2. Restez dans un nœud NUMA quand possible : vCPUs et mémoire doivent être ensemble.
  3. Déplacez les interruptions loin de ces cœurs (NIC et NVMe sont les coupables habituels).
  4. Comptez les threads non vCPU : IOThread, threads vhost, threads émulateur. Ne priviez pas le chemin I/O.
  5. Définissez une politique de fréquence qui correspond aux besoins de latence et vérifiez-la sous charge réelle.
  6. Laissez des issues de secours si possible : un ensemble CPU légèrement plus grand peut réduire la latence de queue dans les environnements rafales.

Checklist C: Rollout and validation (don’t trust your first result)

  1. Capturez la baseline : mpstat, /proc/interrupts, /proc/softirqs, utilisation des threads VM, iostat stockage.
  2. Changez une seule chose : pinning ou affinité IRQ ou politique NUMA — pas tout en même temps.
  3. Mesurez p95/p99 dans l’invité et %soft/%irq sur l’hôte.
  4. Gardez la commande de rollback prête et testée.
  5. Re-vérifiez après un reboot : la distribution IRQ et la topologie CPU peuvent bouger.

FAQ

1) Should I pin CPUs for every VM in Proxmox?

Non. La plupart des VMs bénéficient de la flexibilité de l’ordonnanceur. Pinez seulement quand vous avez une raison mesurée : besoins d’isolation prévisible, contraintes de licence,
ou une charge sensible à la latence où vous pouvez aussi contrôler les interruptions et la localité NUMA.

2) Why did pinning improve throughput but worsen latency?

Le débit peut s’améliorer grâce à la localité de cache et à la réduction des migrations. La latence de queue peut empirer parce que la VM ne peut pas échapper à une interférence transitoire
sur ses CPUs pinés. La nature rafaleuse met en évidence l’inconvénient.

3) Is “CPU limit” the same as pinning?

Non. CPU limit est du throttling (quota de cgroup). L’affectation est du placement (affinité/cpuset). Le throttling peut ajouter de la latence en forçant des pauses périodiques.
Le placement peut ajouter de la latence en empêchant la migration hors de la contention. Différents outils, différentes coupures.

4) Does enabling NUMA in the VM fix NUMA problems?

Ça peut aider, mais ce n’est pas magique. Vous devez toujours vous assurer que l’hôte place correctement vCPUs et mémoire. Vérifiez avec les outils NUMA de l’hôte et observez
si l’ensemble CPU de la VM se mappe proprement à un nœud.

5) What’s the simplest way to tell if interrupts are stealing my pinned CPUs?

Regardez /proc/interrupts et /proc/softirqs, puis corrélez avec mpstat %irq/%soft sur les CPUs affectés.
Si les CPUs pinés montrent un softirq élevé pendant les pics de latence, vous avez votre réponse.

6) Should I disable SMT/Hyper-Threading for low latency?

Parfois. SMT peut augmenter le débit mais ajoute aussi de la contention et du jitter pour certaines charges. Avant de désactiver globalement, essayez d’affecter sur un seul thread par cœur
(évitez les siblings) et mesurez. Désactiver SMT est un marteau plus lourd avec des conséquences larges.

7) Can pinning cause time drift or clock issues inside the guest?

L’affectation en soi ne cause pas directement de dérive d’horloge, mais elle peut augmenter les délais d’ordonnancement qui affectent les applications sensibles au temps.
Assurez-vous que la synchronisation du temps invité est correcte et évitez les politiques de throttling qui introduisent des pauses périodiques.

8) My VM is pinned and still slow. What now?

Validez que l’affectation est complète (threads), vérifiez qu’il n’y a pas de hotspots IRQ sur ces CPUs, contrôlez la localité NUMA, la mise à l’échelle de fréquence et le throttling,
puis confirmez que vous n’êtes pas simplement à cours de CPU en raison de la demande. L’affectation ne corrige pas le « besoin de plus de CPU ».

9) Is it better to pin fewer vCPUs and scale vertically, or more vCPUs and rely on scheduling?

Pour de nombreux services sensibles à la latence, moins de vCPUs bien utilisés avec de la marge bat souvent de nombreux vCPUs qui se disputent des cœurs contraints.
Dimensionnez selon l’arriéré exécutable et la concurrence applicative. Mesurez, ne devinez pas.

Étapes pratiques suivantes

Si vous ne retenez qu’une chose : l’affectation n’est pas de l’isolation. C’est une contrainte. Les contraintes augmentent le coût des erreurs.
Si vous allez contraindre l’ordonnanceur, vous lui devez un environnement propre : interruptions là où vous le souhaitez, localité NUMA cohérente,
et assez de marge CPU pour que les rafales ne deviennent pas des exercices de théorie des files d’attente.

  1. Exécutez le mode d’emploi de diagnostic rapide et identifiez si les pics corrèlent avec %soft/%irq, l’arriéré exécutable, ou la latence stockage.
  2. Vérifiez l’exhaustivité du pinning : threads vCPU, IOThread, et tous les threads helpers importants pour votre chemin I/O.
  3. Arrêtez d’affecter sur des hotspots IRQ. Si vous devez pinner, faites-le avec une hygiène d’affinité IRQ.
  4. Alignez l’affectation avec les cœurs physiques et les nœuds NUMA. Évitez le pinning accidentel sur des siblings SMT pour le travail critique de latence.
  5. Faites les changements un à la fois et gardez des rollbacks prêts. Votre vous futur sera fatigué et reconnaissant.

Si vous suivez ces étapes, vous pinnerez moins souvent — et quand vous le ferez, ce sera pour une raison que vous pourrez expliquer sans superstition.
C’est tout le métier.

← Précédent
Problèmes DNS sous WSL2 : la correction de resolv.conf qui survit aux redémarrages
Suivant →
Installation de CentOS Stream 10 : configuration ‘Next RHEL’ pour labos et CI

Laisser un commentaire