Ubuntu 24.04 : Steal CPU et surcharge de virtualisation — comment les repérer et que faire (Cas n°44)

Cet article vous a aidé ?

Votre service est lent. La charge moyenne a l’air explosive. Les utilisateurs se plaignent. Mais à l’intérieur de la VM, l’« utilisation » CPU n’est même pas si élevée. Vous scalez le déploiement, vous attribuez une instance plus grosse, et pourtant l’exécution ressemble toujours à de la course dans de la boue.

C’est là que le CPU steal et la surcharge de virtualisation apparaissent — discrètement, comme une taxe que vous n’avez pas votée. Si vous exécutez Ubuntu 24.04 sur KVM, VMware, Xen ou « cloud », vous devez pouvoir prouver (pas deviner) quand l’hyperviseur est le goulot d’étranglement, et quel levier vous avez réellement.

Ce qu’est réellement le CPU steal (et ce que ce n’est pas)

Le CPU steal est la portion de temps pendant laquelle votre système d’exploitation invité souhaitait s’exécuter sur un CPU physique, mais l’hyperviseur a dit « pas maintenant ». Il est mesuré par le noyau invité et exposé comme %steal (outils comme mpstat, top, pidstat) et comme temps steal dans les compteurs d’ordonnancement Linux.

En clair : vous avez payé pour des vCPU. L’hôte avait d’autres idées. Ce seau « d’autres idées » est le steal. Vous ne le voyez pas comme une forte utilisation en user CPU ou system CPU dans la VM, parce que votre VM ne tournait pas. Elle attendait en coulisses pendant que d’autres invités avaient le micro.

Le steal n’est pas la même chose que le throttling

Le throttling est un comportement de limitation appliquée : la VM est empêchée d’utiliser plus qu’une allocation (souvent via des quotas cgroups, des modèles de crédits CPU ou des politiques du fournisseur). Le steal est un comportement de contention : vous êtes exécutable, mais pas programmé sur le cœur physique parce que quelqu’un d’autre l’utilise.

Le steal n’est pas l’attente d’I/O

%iowait est le temps passé à attendre la complétion d’E/S pendant que le CPU est inactif. %steal est le temps pendant lequel vous auriez pu exécuter, mais l’hyperviseur n’a pas programmé votre vCPU. Les deux ralentissent l’application. Une seule implique l’ordonnancement du CPU de l’hôte.

Que signifie la surcharge de virtualisation en pratique

Même avec zéro steal, les machines virtuelles paient un coût : VM exits, émulation pour certains périphériques, virtualisation des interruptions, coût des flush TLB, tables de pages imbriquées et toute la gymnastique d’ordonnancement qui accompagne la multiplexation de nombreux vCPU sur moins de physiques.

Sur les KVM/VMware modernes, cette surcharge est généralement faible si la configuration est sensée. Lorsqu’elle n’est pas faible, c’est généralement parce que vous faites l’une de ces choses :

  • Exécuter trop de vCPU par VM par rapport à votre parallélisme réel (pénalité SMP, délais d’ordonnancement).
  • Utiliser des hôtes surabonnés (fort steal/ready).
  • Générer des taux d’interruption élevés (réseau, stockage) via des périphériques virtuels ou réglages d’offload sous-optimaux.
  • Mélanger des charges sensibles à la latence avec des jobs batch gourmands en CPU sur le même hôte.

Idée paraphrasée de Werner Vogels (fiabilité/ops) : « Tout échoue, tout le temps ; concevez et opérez avec cette hypothèse. » Cela s’applique aussi ici : supposez que l’hyperviseur est occupé sauf si vous pouvez prouver le contraire.

Blague #1 : Si vous n’avez jamais été gaslighté par un hyperviseur, vous n’avez pas vécu. La VM jure qu’elle est au repos pendant que vos utilisateurs jurent qu’elle est en feu.

Faits et contexte intéressants (à connaître)

  • Le steal est apparu à l’époque de la paravirtualisation Linux : les premiers invités Xen pouvaient observer explicitement quand ils étaient désordonnancés, ce qui est devenu plus tard un concept standard de comptabilité.
  • Le « CPU Ready » de VMware précède la plupart des tableaux de bord Linux : de nombreuses entreprises ont découvert ce problème via les graphes vCenter avant que les admins Linux ne voient %steal dans leurs outils.
  • Le « vCPU » cloud n’a jamais garanti « un cœur physique » : la surallocation est normale ; ce que vous achetez est une part d’ordonnancement, pas un siège avec votre nom dessus.
  • Un steal élevé est généralement un symptôme d’hôte, pas d’invité : dans la VM vous ne pouvez pas voir directement qui est le voisin bruyant, seulement que vous perdez du temps d’ordonnancement.
  • Le steal peut être quasi nul et vous pouvez quand même être lent : la surcharge de virtualisation peut se manifester par un coût augmenté des changements de contexte, une surcharge d’interruptions et une latence des queues supérieure sans pics évidents de steal.
  • Le CPU steal est souvent en rafales : le cron d’un voisin, une sauvegarde ou un ETL peut créer des pics périodiques qui ruinent la latence p99 alors que les métriques moyennes semblent correctes.
  • La tenue du temps et la virtualisation ont été un chaos : les anciens noyaux et hyperviseurs avaient des dérives d’horloge et des problèmes TSC ; Ubuntu 24.04 masque cela en grande partie, mais quand le temps devient bizarre, les métriques d’ordonnancement deviennent rapidement trompeuses.
  • Le NUMA compte plus qu’on le reconnaît : le placement des vCPU sur plusieurs sockets et la localité mémoire peuvent causer des latences qui ressemblent à du « CPU » mais qui sont en réalité du trafic mémoire inter-nœuds.
  • La virtualisation imbriquée amplifie la surcharge : exécuter un hyperviseur dans une VM ajoute des VM exits et des couches d’ordonnancement ; l’invité voit plus de variabilité même si le steal semble modéré.

Symptômes : comment le steal CPU se déguise

Le CPU steal est un maître du déguisement parce que l’OS invité n’est pas « occupé » de la manière habituelle. Votre thread est exécutable, mais le vCPU n’obtient pas de temps sur le silicium. Cela crée un schéma de douleur spécifique :

Modèles de symptômes à reconnaître

  • Charge moyenne élevée avec une utilisation CPU modérée : des tâches exécutables s’entassent, mais le user/system CPU dans la VM n’augmente pas proportionnellement.
  • Pics de latence sur des endpoints non reliés : tout ralentit, pas seulement un sous-système. Le p99 s’effondre.
  • Timeouts « aléatoires » : handshakes TLS, requêtes base de données et consommateurs de queues expirent sous un trafic modéré.
  • Baisse de débit après un « scaling réussi » : ajouter des vCPU ou des pods augmente les threads exécutables, ce qui peut accroître la contention d’ordonnancement et aggraver la situation.
  • Soft lockups et heartbeats manqués : des démons système ne s’exécutent pas à temps ; le monitoring observe du flapping.
  • Les pauses GC semblent plus longues : pas parce que le GC a changé, mais parce que le processus est désordonnancé au milieu d’une collection.

Pensez au steal comme une file d’attente cachée devant votre CPU. L’invité voit la file mais pas le caissier.

Mode opératoire de diagnostic rapide (premier / second / troisième)

Ceci est l’ordre de triage qui fait gagner du temps. Ne faites pas au feeling. Vous essayez de répondre à une question : Sommes-nous à court de CPU parce que l’hôte ne nous programme pas, ou parce que nous sommes réellement CPU-bound dans la VM ?

Premier : prouver ou éliminer le steal

  1. Vérifiez %steal dans le temps (mpstat), pas un seul instantané.
  2. Corrélez avec les pics de latence (métriques applicatives) ou la profondeur des queues (métriques système).
  3. Si le steal est constamment >2–5% pendant les incidents, considérez-le comme une contention réelle. Si ça pique à 20–50%, c’est un incident même si vos graphes CPU semblent « corrects ».

Second : séparer « CPU occupé » de « CPU bloqué »

  1. Vérifiez la file d’exécution et les motifs de changements de contexte (vmstat, pidstat).
  2. Vérifiez iowait et la latence disque (iostat) pour ne pas accuser le steal pour des blocages stockage.
  3. Vérifiez la pression mémoire (free, psi) pour ne pas confondre des stalls de reclaim avec une contention CPU.

Troisième : identifiez le levier que vous avez réellement

  1. Si vous êtes sur une instance cloud partagée : migrez de type/taille d’instance ou passez à des hôtes dédiés/coeurs isolés.
  2. Si vous gérez KVM/VMware : corrigez la surallocation, le dimensionnement vCPU, l’alignement NUMA et le placement des voisins bruyants.
  3. Si vous êtes en Kubernetes : décidez s’il faut réparer le nœud (niveau hôte), limiter le pod (cgroups) ou déplacer les workloads.

Tâches pratiques : commandes, interprétation, décisions

Vous vouliez du concret, pas de la théorie. Voici des tâches concrètes à lancer sur des invités Ubuntu 24.04 pour détecter le steal et le distinguer d’autres goulots. Chaque tâche inclut (1) la commande, (2) ce que signifie la sortie, et (3) la décision à prendre.

Task 1: Confirm you’re virtualized (and how)

cr0x@server:~$ systemd-detect-virt
kvm

Signification : Vous êtes dans une VM, donc le steal est un candidat. Si ça affiche none, le steal n’est pas votre problème (regardez CPU-bound, E/S, verrous ou throttling).

Décision : Si virtualisé, continuez les vérifications de steal. Notez aussi la famille d’hyperviseur ; elle influence quelles métriques existent côté hôte.

Task 2: Snapshot CPU accounting including steal

cr0x@server:~$ top -b -n 1 | head -n 5
top - 10:42:18 up 12 days,  3:21,  2 users,  load average: 8.21, 7.90, 6.44
Tasks: 312 total,   3 running, 309 sleeping,   0 stopped,   0 zombie
%Cpu(s):  9.2 us,  3.1 sy,  0.0 ni, 79.8 id,  0.4 wa,  0.0 hi,  0.3 si,  7.2 st
MiB Mem :  32085.4 total,   1954.7 free,  11892.3 used,  18238.4 buff/cache
MiB Swap:   4096.0 total,   4096.0 free,      0.0 used.  20193.1 avail Mem

Signification : st est le steal. Ici il est à 7.2%—pas discret. La charge moyenne est élevée alors que l’idle aussi : signature classique de contention.

Décision : Si st est non négligeable, arrêtez de blâmer « l’appli » tant que vous n’avez pas mesuré plus. Passez à l’échantillonnage en séries temporelles ensuite.

Task 3: Time-series CPU steal per core

cr0x@server:~$ mpstat -P ALL 1 10
Linux 6.8.0-41-generic (server)  12/30/2025  _x86_64_  (8 CPU)

10:42:30 AM  CPU   %usr  %nice   %sys %iowait  %irq  %soft  %steal  %idle
10:42:31 AM  all   12.5   0.0    4.0    0.3    0.0   0.5     9.8    72.9
10:42:31 AM    0   10.0   0.0    3.0    0.0    0.0   0.0    18.0    69.0
10:42:31 AM    1    9.0   0.0    2.0    0.0    0.0   0.0     1.0    88.0
...

Signification : Le steal est inégal entre vCPU. Cela indique souvent une contention côté ordonnanceur et des effets de placement vCPU. Si %steal est constamment >2–5% pendant les périodes lentes, c’est réel.

Décision : Si le steal est en rafales, capturez-le pendant les incidents et corrélez-le avec la latence. S’il est constant, envisagez de migrer ou redimensionner ; le steal constant est un problème de capacité.

Task 4: Compare load average to run queue and blocked tasks

cr0x@server:~$ vmstat 1 10
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 6  0      0 201312  98244 932112    0    0     1     8  520 1220 10  3 76  1 10
 8  0      0 200984  98244 932120    0    0     0     0  610 1405 12  4 70  0 14

Signification : r (exécutables) est élevé ; b (bloqués) est faible. Le CPU a un st significatif. Cela pointe vers une contention d’ordonnancement CPU plutôt qu’un blocage d’E/S.

Décision : Si b est élevé, enquêtez sur le stockage et le réseau. Si r est élevé avec du steal, concentrez-vous sur la contention d’hôte ou le dimensionnement vCPU.

Task 5: Check if you’re being CPU-throttled by cgroups (containers/systemd)

cr0x@server:~$ systemctl show -p CPUQuota -p CPUQuotaPerSecUSec docker
CPUQuota=
CPUQuotaPerSecUSec=infinity

Signification : Pas de quota systemd au niveau du service. Dans Kubernetes ou runtimes de conteneurs, le throttling peut encore se produire au niveau du cgroup du pod/conteneur.

Décision : Si des quotas existent, corrigez le throttling en premier ; le throttling peut ressembler à un « CPU lent » même avec zéro steal.

Task 6: Look for container CPU throttling evidence (cgroup v2)

cr0x@server:~$ cat /sys/fs/cgroup/cpu.stat
usage_usec 389432187
user_usec 312903110
system_usec 76529077
nr_periods 21903
nr_throttled 0
throttled_usec 0

Signification : nr_throttled et throttled_usec montrent le throttling CPU cgroup. Ici c’est zéro, donc l’invité n’est pas auto-throttlé.

Décision : Si le throttling est non nul et augmente, augmentez les limites/requests, réduisez la concurrence, ou passez à des pools CPU dédiés ; ne blâmez pas encore le steal.

Task 7: Check pressure stall information (PSI) for CPU saturation signals

cr0x@server:~$ cat /proc/pressure/cpu
some avg10=2.14 avg60=1.05 avg300=0.34 total=9823412
full avg10=0.00 avg60=0.00 avg300=0.00 total=0

Signification : PSI CPU some indique le temps avec au moins une tâche en attente de CPU. Si cela augmente avec %steal, vous avez une contention. Si PSI est élevé avec peu de steal, vous pouvez être CPU-bound dans la VM.

Décision : PSI élevé + steal élevé : migrez d’hôte/type d’instance ou réduisez l’overcommit. PSI élevé + steal faible : optimisez l’usage CPU applicatif ou ajoutez du vrai calcul.

Task 8: Separate I/O wait from steal with iostat

cr0x@server:~$ iostat -xz 1 5
Linux 6.8.0-41-generic (server)  12/30/2025  _x86_64_  (8 CPU)

avg-cpu:  %user %nice %system %iowait  %steal  %idle
          11.9  0.00    3.8    0.4     9.7    74.2

Device            r/s     w/s   rKB/s   wKB/s  await  svctm  %util
nvme0n1          2.1     3.4    45.2    88.3   1.20   0.35   0.3

Signification : await disque est faible, %util est négligeable ; le stockage n’est pas le goulet. Pendant ce temps %steal est ~10%.

Décision : Ne touchez pas aux disques. Escaladez vers une contention d’ordonnancement de l’hyperviseur ou vers le dimensionnement/placement de la VM.

Task 9: Check network softirq load (virtual NIC overhead can look like CPU problems)

cr0x@server:~$ cat /proc/softirqs | head
                    CPU0       CPU1       CPU2       CPU3       CPU4       CPU5       CPU6       CPU7
HI:                 12          8          4          7          6          5          4          6
TIMER:        1234567    1122334    1099887    1044556    1001222     998877     912345     887766
NET_TX:         23456      19876      21011      18765      16543      17002      16001      15888
NET_RX:        345678     312345     321002     300111     280987     275444     260999     255888

Signification : Des comptes massifs NET_RX/NET_TX peuvent impliquer des taux de paquets élevés, ce qui augmente la charge CPU et la gigue d’ordonnancement—surtout avec de mauvais réglages d’offload ou des petits paquets.

Décision : Si le réseau est chaud, vérifiez le type de NIC (virtio vs émulé), les offloads et la distribution des interruptions. Mais ne confondez pas cela avec le steal ; ils peuvent coexister.

Task 10: Check vCPU topology exposed to the guest

cr0x@server:~$ lscpu | egrep 'CPU\\(s\\)|Thread|Core|Socket|NUMA'
CPU(s):                               8
Thread(s) per core:                   1
Core(s) per socket:                   8
Socket(s):                            1
NUMA node(s):                         1

Signification : Cet invité voit une topologie simple 1-socket. Si vous voyez plusieurs sockets/NUMA nodes dans une VM et que la charge n’est pas NUMA-aware, vous pouvez obtenir des outliers de latence sans gros steal.

Décision : Pour les applis sensibles à la latence, préférez moins de sockets et des comptes de vCPU sensés ; évitez les grosses VMS SMP sauf si vous avez vraiment besoin de parallélisme.

Task 11: Look for clocksource/timekeeping weirdness (it can amplify scheduling pain)

cr0x@server:~$ cat /sys/devices/system/clocksource/clocksource0/current_clocksource
kvm-clock

Signification : kvm-clock est attendu sous KVM. Des clocksources étranges ou des sauts fréquents de temps peuvent rendre l’analyse de latence trompeuse et casser la logique de timeout.

Décision : Si des sauts temporels sont observés (logs montrant « time went backwards »), enquêtez sur la configuration du temps de l’hyperviseur et NTP/chrony. Ne pourchassez pas des bugs de performance fantômes.

Task 12: Capture scheduling delay evidence with perf sched (short, controlled)

cr0x@server:~$ sudo perf sched record -a -- sleep 10
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 6.214 MB perf.data (12698 samples) ]
cr0x@server:~$ sudo perf sched latency --sort max | head
Task                  |   Runtime ms  | Switches | Avg delay ms | Max delay ms
postgres:checkpointer |      120.11   |    1321  |       0.18   |      22.34
nginx:worker          |       98.45   |    1887  |       0.12   |      18.02

Signification : De grands délais max d’ordonnancement suggèrent que l’invité n’obtient pas le CPU en temps utile. Cela peut être dû au steal (contention de l’hyperviseur) ou à une saturation CPU dans l’invité.

Décision : Si %steal est élevé en même temps, vous avez une contention d’hôte. Si %steal est faible, vous êtes probablement surchargé dans la VM (ou victime de contentions de locks).

Task 13: Check kernel messages for soft lockups and scheduling stalls

cr0x@server:~$ journalctl -k --since "1 hour ago" | egrep -i "soft lockup|rcu_sched|stall|watchdog" | tail -n 5
Dec 30 10:11:02 server kernel: watchdog: BUG: soft lockup - CPU#3 stuck for 22s! [java:28199]
Dec 30 10:11:02 server kernel: rcu: INFO: rcu_sched detected stalls on CPUs/tasks: { 3-... } (detected by 2, t=5250 jiffies)

Signification : Des stalls peuvent se produire avec un steal extrême parce que l’invité n’est tout simplement pas programmé assez souvent. Cela peut aussi arriver avec de véritables lockups CPU, mais dans les VM la contention est un déclencheur fréquent.

Décision : Si cela corrèle avec des pics de steal, escaladez vers la capacité plateforme/hôte. Si cela corrèle avec un CPU saturé et zéro steal, enquêtez sur des threads hors de contrôle ou des bugs noyau.

Task 14: Confirm hypervisor hints (DMI) for escalation breadcrumbs

cr0x@server:~$ sudo dmidecode -s system-product-name
Standard PC (Q35 + ICH9, 2009)

Signification : Cela peut vous aider à identifier la pile de virtualisation (type de machine QEMU ici). Utile lorsque vous devez ouvrir un ticket ou matcher des problèmes connus.

Décision : Enregistrez cela avec les graphes de steal et les timestamps d’incident. Quand vous escaladez, apportez des preuves, pas des impressions.

Task 15: If you suspect host overcommit, check steal from Prometheus node_exporter style counters (guest-side)

cr0x@server:~$ awk '/^cpu /{print "steal_jiffies=" $9}' /proc/stat
steal_jiffies=981223

Signification : Le 9ème champ CPU agrégé dans /proc/stat est le steal en jiffies. C’est ainsi que les exporters calculent le taux de steal. Utile pour vérifier ce que votre monitoring devrait voir.

Décision : Si vos dashboards montrent zéro steal mais que /proc/stat évolue, votre monitoring est incorrect. Corrigez le monitoring avant de « réparer » la production.

Task 16: Check if virtualization features are available (paravirt hints)

cr0x@server:~$ dmesg | egrep -i "kvm-clock|pvclock|Hypervisor detected|Booting paravirtualized kernel" | tail -n 5
[    0.000000] Hypervisor detected: KVM
[    0.000000] kvm-clock: Using msrs 4b564d01 and 4b564d00

Signification : L’horloge paravirtualisée et la détection hyperviseur sont normales et généralement bonnes. Si vous ne voyez pas les fonctionnalités virt que vous attendez, vous pourriez utiliser des périphériques émulés ou manquer des optimisations, ce qui augmente la surcharge.

Décision : Si les fonctionnalités virtuelles manquent, révisez la configuration VM (drivers virtio, passthrough du modèle CPU, type de machine correct). Corriger cela peut réduire la surcharge même si le steal est faible.

Ce que vous contrôlez vs ce que le fournisseur contrôle

Le steal est politiquement délicat : la cause racine est souvent « quelqu’un d’autre sur l’hôte ». Dans un cloud public, ce quelqu’un n’ira pas rejoindre votre call d’incident. Votre travail est d’identifier quels leviers existent dans votre environnement et de tirer ceux qui paient.

Si vous êtes sur un cloud public en locataire partagé

  • Vous pouvez : changer de famille d’instances, adapter la taille, changer de région/zone, utiliser des hôtes/instances dédiés, utiliser le CPU pinning quand disponible, ajuster l’autoscaling et la concurrence, réduire le nombre de vCPU pour coller au parallélisme réel.
  • Vous ne pouvez pas : corriger l’overcommit de l’hôte directement, expulser des voisins bruyants, contrôler les versions du noyau hôte ou changer la politique d’ordonnancement de l’hyperviseur.

Conseil pratique : si les pics de steal sont réguliers et corrélés aux heures ouvrables, traitez cela comme un mauvais match de capacité. Passez à un niveau plus prévisible (hôtes dédiés, performance réservée, offres de cœurs isolés). Payer un peu plus coûte souvent moins que d’expliquer pourquoi le p99 est hanté.

Si vous gérez votre propre KVM/Proxmox/OpenStack

  • Vous pouvez : définir les ratios vCPU:pCPU, pinner des vCPU, isoler des cœurs hôtes, tuner l’ordonnanceur hôte, appliquer le placement des workloads, monitorer les files d’exécution hôte et éviter de mélanger workloads latence avec calcul batch.
  • Vous ne pouvez pas : contourner les lois de la physique ; si vous surallouez et que tout le monde devient occupé, le steal est le résultat correct.

Si vous utilisez VMware

VMware appelle le cousin de cette métrique « CPU Ready ». Le %steal de l’invité va souvent corréler, mais pas parfaitement. Le sens opérationnel est le même : la VM est exécutable mais pas programmée. Utilisez les métriques côté hôte pour le verdict final, mais ne rejetez pas le steal côté invité — c’est souvent le signal le plus précoce.

Si vous exécutez Kubernetes sur des VMs

Vous avez deux couches de contention d’ordonnancement :

  • Les cgroups Linux (limites de pod) peuvent throttler le CPU, ce qui n’est pas du steal mais ressemble à une application lente.
  • L’ordonnancement de l’hyperviseur peut voler du CPU au nœud, ce que le kubelet ne peut pas réparer.

Opérationnellement : si le steal au niveau nœud augmente, rescheduler des pods dans le même pool de nœuds ne fait souvent rien. Il faut remplacer le nœud, changer la classe d’instance ou fournir une capacité dédiée.

Trois mini-histoires d’entreprise (anonymisées, plausibles, douloureuses)

Mini-histoire 1 : L’incident causé par une mauvaise hypothèse

Ils faisaient tourner une API de paiements sur des VM Ubuntu. L’incident a commencé par « la base de données est lente ». Les équipes applicatives voyaient des timeouts, les SRE voyaient la charge moyenne grimper, et tout le monde regardait les graphes de la base comme s’ils leur devaient de l’argent.

La mauvaise hypothèse : « le CPU est correct parce que l’utilisation n’est que de 25% ». Ce chiffre venait de l’intérieur de la VM. Les hôtes étaient suralloués, et un locataire d’analytics batch sur le même cluster hyperviseur a lancé un job CPU-intensive. Les VMs de paiements ont montré %steal autour de 15–30% pendant quarante minutes. Pas assez pour bloquer le CPU à 100%, mais assez pour exploser la latence tail. Les requêtes ont timeouté, les retries se sont accumulés, et la pression de queue a empiré la situation.

Le débogage a été retardé parce que les dashboards de l’équipe n’incluaient pas le steal. Leur node exporter existait, mais leur panneau Grafana ne traçait que user/system/idle. L’incident est donc devenu « l’appli est lente pour des raisons inconnues », ce qui veut poliment dire « on ne mesure pas ce qui compte ».

Une fois qu’ils ont ajouté rate(node_cpu_seconds_total{mode="steal"}) à leur panneau standard, ils ont vu clairement la signature de contention. La correction réelle a été ennuyeuse : déplacer le tier paiements sur un cluster moins suralloué et limiter le placement du locataire batch pendant les heures ouvrables. L’action postmortem n’était pas « optimiser SQL » mais « arrêter de supposer que l’utilisation CPU dans une VM est toute l’histoire ».

Mini-histoire 2 : L’optimisation qui a échoué

Une équipe plateforme a voulu « aider » un service Java en doublant ses vCPU : de 8 à 16. Leur raisonnement était propre : plus de cœurs, plus de débit. Le service a effectivement été un peu plus rapide en tests synthétiques, assez pour déclarer victoire et pousser le changement en production.

Puis le p99 s’est dégradé. Pas tout le temps. Juste pendant les pics. C’est comme ça que ces gremlins fonctionnent : ils attendent que vous soyez confiant.

Le cluster hôte était modérément suralloué. Une VM 16-vCPU est plus difficile à ordonnancer qu’une 8-vCPU quand l’hyperviseur tente de co-programmer les vCPU de façon équitable. Sous contention, la VM a passé plus de temps à attendre que l’hôte trouve des créneaux CPU. Le %steal invité a augmenté. L’application avait aussi augmenté la taille de son pool de threads parce qu’elle « voyait » plus de CPU, ce qui a augmenté les threads exécutables et la contention de locks. Ils avaient amélioré la capacité du système à se combattre lui-même.

Le rollback — retour à 8 vCPU et limitation de la concurrence — a stabilisé la latence. L’amélioration finale a été plus nuancée : passer à une famille d’instances optimisée CPU et utiliser moins de vCPU plus rapides avec de meilleures performances par cœur. Leçon : augmenter le nombre de vCPU peut accroître la latence d’ordonnancement et la surcharge. Plus grand n’est pas toujours plus rapide ; parfois c’est juste plus gros.

Mini-histoire 3 : La pratique ennuyeuse mais correcte qui a sauvé la journée

Une équipe infra avait une habitude : chaque tableau de bord de nœud production incluait le steal, le PSI, et une alerte simple « suspicion contention hôte » quand le steal dépassait un petit seuil pendant quelques minutes. Personne n’était enthousiaste. Ce n’était pas glamour. C’était juste là, comme les ceintures de sécurité.

Un mardi, un client a signalé une lenteur intermittente. Les graphes applicatifs semblaient majoritairement corrects, mais la latence p95 pour un endpoint clé montait toutes les 20–30 minutes. Le premier examen du nœud a montré des pics de %steal correspondant presque parfaitement au pattern. Pas de devinettes. Pas deux heures de débat sur le garbage collector.

Ils ont classé l’incident comme « contention plateforme », déplacé la charge vers un pool de nœuds dédié, et le problème a disparu. Plus tard, ils ont trouvé que les pics périodiques étaient causés par des tâches de maintenance sur d’autres invités partageant le même cluster hôte. L’équipe n’avait pas besoin de prouver qui était le voisin ; elle avait juste besoin de protéger le service.

La pratique qui les a sauvés n’était pas un tweak noyau astucieux. C’était d’avoir les bonnes métriques, déjà tracées, et une voie d’escalade qui ne dépendait pas de l’humeur de quelqu’un. L’ennuyeux gagne encore.

Blague #2 : Le meilleur fix de performance est parfois la relocalisation. C’est comme déménager parce que le voisin pratique la batterie — techniquement pas votre bug, mais toujours votre problème.

Erreurs courantes : symptôme → cause racine → correction

Cette section est volontairement directe. Ce sont les erreurs qui prolongent les incidents plus que nécessaire.

1) Charge moyenne élevée, CPU « idle » élevé

Symptôme : La charge moyenne grimpe, le CPU utilisateur reste modeste, la latence applicative augmente.

Cause racine : CPU steal (contention hôte) ou délai d’ordonnancement sévère.

Correction : Confirmez %steal via mpstat. Si soutenu, migrez vers une capacité moins contendue, réduisez le nombre de vCPU, ou passez à des hôtes dédiés. Si vous possédez l’hyperviseur, réduisez l’overcommit et isolez les workloads bruyants.

2) « On a scale up, c’est pire »

Symptôme : Augmenter les vCPU ou les réplicas de pods augmente la latence tail.

Cause racine : Les grosses VMs SMP sont plus difficiles à ordonnancer ; l’augmentation de la concurrence accroît les threads exécutables et la contention de locks ; l’hôte est suralloué.

Correction : Scale-out avec plus de petites VMs (si la capacité hôte le permet) ou changez de famille d’instances. Limitez la concurrence. Préférez moins de vCPU avec de meilleures performances par cœur pour les services sensibles à la latence.

3) Confondre throttling cgroup avec steal

Symptôme : Service lent, utilisation CPU plafonnée, steal proche de zéro.

Cause racine : Throttling CPU (nr_throttled augmente) ou limites Kubernetes trop basses.

Correction : Inspectez /sys/fs/cgroup/cpu.stat. Ajustez limites/requests, utilisez QoS Guaranteed pour les workloads critiques, ou passez à des nœuds CPU dédiés.

4) Blâmer le stockage parce que « iowait existe »

Symptôme : Un petit %wa apparaît, l’appli est lente, on crie « disque ».

Cause racine : CPU steal ou délai d’ordonnancement, alors que le stockage est correct.

Correction : Utilisez iostat -xz pour valider await et %util. Si le disque n’est pas occupé et que le steal est élevé, arrêtez de tuner le stockage.

5) Ne pas capturer pendant l’incident

Symptôme : « On a vérifié plus tard et tout avait l’air bien. »

Cause racine : Le steal est en rafales ; vous l’avez manqué.

Correction : Ajoutez un script de capture léger d’incident (snapshots mpstat/vmstat/iostat), activez le monitoring continu, et alertez sur le steal soutenu au-dessus d’un seuil.

6) Considérer le steal comme « bruit normal du cloud »

Symptôme : Pics de latence réguliers acceptés comme destin.

Cause racine : Mauvais choix de classe d’instance ; la locataire partagée est trop contendue pour votre SLA.

Correction : Utilisez une capacité dédiée/isolée pour les services critiques en latence, ou migrez vers un niveau où l’ordonnancement prévisible fait partie du produit.

7) Trop tuner le noyau en ignorant le placement

Symptôme : Semaines de tweaks sysctl, aucune amélioration durable.

Cause racine : Contention hôte ; le tuning ne peut pas convaincre l’ordonnanceur de vous programmer si vous n’êtes pas programmé.

Correction : Résolvez la capacité et le placement d’abord. Tuner ensuite, et seulement avec des bénéfices mesurés.

Listes de vérification / plan étape par étape

Checklist A: Quand une VM est lente et que vous suspectez la surcharge de virtualisation

  1. Confirmez la virtualisation : systemd-detect-virt.
  2. Faites un instantané CPU avec steal : top (cherchez st).
  3. Échantillonnez le steal dans le temps : mpstat -P ALL 1 60 pendant l’incident.
  4. Vérifiez la file d’exécution et le steal ensemble : vmstat 1 60.
  5. Excluez le disque : iostat -xz 1 10.
  6. Excluez le throttling cgroup : cat /sys/fs/cgroup/cpu.stat.
  7. Vérifiez le PSI : cat /proc/pressure/cpu et le PSI mémoire si nécessaire.
  8. Si le steal est confirmé, décidez : migrer/redimensionner/dédier vs tuner l’invité.

Checklist B: Si vous contrôlez l’hyperviseur (KVM/VMware) et que le steal est élevé

  1. Trouvez l’overcommit CPU de l’hôte et la pression de file d’exécution (outillage côté hôte).
  2. Réduisez les ratios vCPU:pCPU pour les clusters latence.
  3. Arrêtez de mélanger jobs batch CPU-intensive et services latence sur les mêmes hôtes/pools.
  4. Pinner ou isoler des CPUs pour les workloads critiques si votre modèle opérationnel le permet.
  5. Validez l’alignement NUMA : évitez d’étaler inutilement sur plusieurs sockets.
  6. Utilisez des périphériques virtio et des modèles CPU modernes ; évitez l’émulation quand c’est possible.
  7. Retestez avec mpstat et les p95/p99 applicatifs après chaque changement.

Checklist C: Pour des nœuds Kubernetes sur VM

  1. Vérifiez le steal au niveau nœud (mpstat) et le throttling conteneur (cpu.stat).
  2. Si le steal est élevé : remplacez le(s) nœud(s) ou déplacez vers un autre pool de nœuds/classe d’instance.
  3. Si le throttling est élevé : ajustez les limites, utilisez des politiques CPU manager dédiées ou changez la QoS.
  4. Ne « corrigez » pas en augmentant les replicas sans comprendre la contention ; vous pouvez l’amplifier.

Seuils opérationnels (opinionnés, utiles)

  • Steal < 1% la plupart du temps : acceptable pour beaucoup de workloads.
  • Steal 1–5% soutenu : à investiguer pour les systèmes sensibles à la latence ; probable contention.
  • Steal 5–10% soutenu : attendez-vous à un impact visible par les utilisateurs sous charge ; traitez comme un problème plateforme.
  • Steal > 10% pendant les incidents : escaladez et migrez ; tuner côté invité est surtout cosmétique.

FAQ

1) Quel pourcentage de CPU steal est « mauvais » ?

Pour des jobs batch, quelques pourcents peuvent être tolérables. Pour des services sensibles à la latence, un >2–5% soutenu est un vrai problème. Les pics >10% qui coïncident avec des pics p99 sont en gros une confession.

2) Le steal peut-il être zéro et je souffre quand même de surcharge de virtualisation ?

Oui. La surcharge peut provenir du coût des interruptions, des VM exits, des effets NUMA et de l’émulation des périphériques. Cela se traduit par un coût CPU par requête plus élevé et une pire latence tail sans grande signature de steal.

3) Comment le steal Linux se rapporte-t-il au CPU Ready de VMware ?

Ils sont conceptuellement similaires : « exécutable mais pas programmé ». Le CPU Ready est mesuré côté hôte et est souvent le plus autoritatif dans les environnements VMware. Le steal invité reste utile pour le diagnostic de premier niveau.

4) Pourquoi la charge moyenne augmente-t-elle quand le CPU dans la VM semble idle ?

La charge comprend les tâches exécutables. Si votre vCPU n’est pas programmé (steal), les tâches restent exécutables plus longtemps, gonflant la charge moyenne même si le guest ne consomme pas de user/system CPU.

5) Le CPU steal est-il toujours causé par un voisin bruyant ?

Souvent, oui. Mais cela peut aussi être de la maintenance hôte, des migrations live, des changements de fréquence CPU au niveau hôte pour contraintes thermiques, ou des décisions d’overcommit prises par votre équipe de virtualisation.

6) Dois-je juste passer à des instances plus grosses pour corriger le steal ?

Parfois cela aide, parfois cela aggrave la situation. Les grosses VMs peuvent être plus difficiles à ordonnancer sous contention. Préférez migrer vers une classe moins contendue (cœurs dédiés/isolés, familles optimisées compute) plutôt que d’augmenter simplement le nombre de vCPU.

7) Comment alerter correctement sur le steal ?

Alarmez sur un taux de steal soutenu, pas sur des pics isolés : par exemple steal >2% pendant 5–10 minutes sur des nœuds sensibles à la latence. Corrélez avec p95/p99 pour éviter de sonner l’alarme pour des tiers batch.

8) Puis-je corriger le steal depuis l’invité en tunant le noyau ?

Pas vraiment. Vous pouvez réduire la demande CPU (moins de threads, moins de busy polling), ce qui diminue la fréquence à laquelle vous voulez du CPU, mais vous ne pouvez pas forcer l’hyperviseur à vous programmer plus. Le steal est un problème de capacité/placement.

9) Qu’en est-il du scaling de fréquence CPU et des symptômes « semblables au steal » ?

La scaling de fréquence affecte la quantité de travail que vous obtenez par tranche de temps programmée, mais cela n’apparaît pas comme du steal. Si vous voyez peu de steal mais un débit réduit, vérifiez les performances par cœur, le comportement turbo et les différences de modèle CPU entre hôtes.

10) Le pinning vCPU résout-il tout ?

Le pinning peut réduire la gigue pour des workloads spécifiques, mais il réduit la flexibilité et peut diminuer l’utilisation globale. C’est un outil pour services critiques avec des besoins CPU connus, pas une solution universelle pour une planification de capacité désordonnée.

Conclusion : prochaines étapes qui font réellement avancer

Quand Ubuntu 24.04 tourne dans une VM, l’« utilisation » CPU n’est que la moitié de l’histoire. Le steal est le chapitre manquant : le temps pendant lequel votre workload voulait s’exécuter mais n’était pas autorisé. Si vous ne le mesurez pas, vous allez mal diagnostiquer les incidents, perdre du temps à tuner le mauvais sous-système, et aggraver la performance en voulant bien faire en scalant.

Faites ceci ensuite, dans cet ordre :

  1. Ajoutez le steal à vos dashboards standards (et vérifiez qu’il correspond à /proc/stat).
  2. Définissez une alerte pour le steal soutenu sur les nœuds critiques en latence.
  3. Pendant le prochain incident, capturez mpstat, vmstat, iostat et cpu.stat cgroup pour la fenêtre exacte.
  4. Si le steal est confirmé, arrêtez de tuner dans la VM et changez le placement/la capacité : migrez, réduisez l’overcommit, ou achetez de l’ordonnancement dédié.
  5. Right-sizez le nombre de vCPU : moins de cœurs plus rapides battent souvent plus de cœurs lents et contendus pour la latence tail.

Le meilleur : une fois que vous pouvez prouver le steal, vous avez l’argument juste avec la bonne équipe. Et vous pouvez arrêter d’interroger la base de données pour des crimes commis par l’hyperviseur.

← Précédent
HDR PC : pourquoi c’est incroyable… et pourquoi c’est parfois affreux
Suivant →
ZFS : Utiliser NVMe comme SLOG — Quand c’est parfait et quand c’est excessif

Laisser un commentaire