A l’occasion d’une discussion avec des collègues concernant les best-practices d’allocation et de paramétrage des VMs, nous avons abordé la question des vCPU. Vous le savez sans doute, lorsque vous créez une VM, vous avez le choix d’allouer des sockets virtuels ainsi que des cœurs d’exécution par socket. Historiquement, la réponse de VMware était simple : vous faites comme vous voulez, cela permet de gérer plus facilement les problématiques de licences logicielles. En effet, certains produits étant à une époque licenciés au “processeurs physique” et il est donc plus intéressant financièrement de ne présenter qu’un seul socket et multiplier les cœurs.
Sauf que : si, dans la première moitié des années 2000 nos architectures x86 ne proposaient qu’un seul mode SMP – le plus simple – à savoir un espace mémoire commun à l’ensemble des processeurs physiques de la machine et partagé par les processeurs, désormais, et plus spécifiquement depuis Nehalem, ce n’est plus le cas. En effet, les plateformes Intel x86 utilisent maintenant le principe NUMA, acronyme pour “Non Uniform Memory Access”, et cela a des conséquences qui peuvent être non négligeables quant il s’agit de paramétrer les vCPUs des machines virtuelles qui tourneront sur ces configuration.
Petite exploration de la théorie, des impacts et des best-practices actuels ensemble…
La théorie
Je ne vais pas paraphraser Wikipédia, mais globalement, NUMA représente la conséquence de la mise en oeuvre d’une segmentation de la mémoire centrale. Chaque processeur dispose d’une mémoire “locale” directement reliée à son bus correspondant. La mémoire globale est donc répartie (souvent équitablement) entre l’ensemble des processeurs disponibles. Sur un modèle classique bi-socket x86, comme on en trouve de nombreux dans nos datacenters, chaque Xeon dispose en général d’une banque mémoire dite locale, correspondant à la moitié de la mémoire globale disponible sur la machine.
Cela signifie qu’il existe, vu d’un process s’exécutant sur un processeur donné, une notion de “distance” ou de localité de la donnée nécessaire à son fonctionnement. Pour résumer, si la donnée se trouve sur la banque mémoire directement connectée au processeur, les accès seront plus rapides que si la donnée se trouve dans la banque connecté à l’autre.
Et si cela vous avait échappé, et afin de briller en société (toujours utile pour des diners V.I.P., n’est-il pas ^^) : le bus d’interconnexion entre les processeurs Xeon qu’Intel appelle QPI ou “Quick Path Interconnect”, qui dispose d’une bande passante limitée, est justement celui qui est utilisé pour les accès mémoire distants.
Le cas de VMware ESXi
L’hyperviseur de VMware sait parfaitement gérer ces configurations NUMA mais est également sensible à la façon dont les VM ont été paramétrées. En effet, si vous choisissez systématiquement de ne jouer que sur le nombre de sockets logiques, pas de souci, ESXi s’occupe de placer systématiquement la VM sur le plus petit ensemble NUMA. Dans la pratique, sur des machines bi-processeurs que l’on connait bien et dans la mesure du possible, la VM sera placée exclusivement sur un des deux processeurs physiques. Si d’aventure, vous avez provisionné plus de vCPU que ce dont dispose chaque processeur, à ce moment là, ESXi placera la VM a cheval sur les deux processeurs et s’en arrangera.
Par contre, si vous multipliez le nombre de cœurs dans la VM, ESXi débraye sa gestion automatique de la localisation NUMA et va respecter scrupuleusement le setup vCPU de la machine (toujours dans la mesure du possible évidemment). Cela signifie dans la pratique qu’une VM disposant de 2 sockets et 2 cœurs par socket sera toujours positionnée à cheval sur deux processeurs, avec les impacts éventuels correspondant vis à vis de la localisation des données en mémoire et pourquoi pas, un niveau de performance non optimal en cas de gros accès mémoire.
Que se passe-t-il si une VM se trouve positionnée sur un “mauvais” processeur et qu’une part non négligeable de la mémoire de la VM n’est pas localisée, mais utilisée à distance via un autre processeur ? En général, on estime (ce n’est pas documenté a priori) que si moins de 80% de la mémoire est locale, le scheduler d’ESXi va se débrouiller pour déménager la VM au meilleur endroit. Celui-ci va jusqu’à déplacer les blocs mémoire pour les optimiser, dixit la documentation VMware (voir ici)
Ok, mais dans la pratique ?
Alors, comme toujours, il faut prendre un peu de recul avec cela et vraiment juger de l’impact réel en condition normale des machines virtuelles “non optimisées” au niveau vCPU. La plupart du temps, cela ne se ressentira que très peu… Maaaaaaiiiiis, malgré tout, il faut toujours imaginer le ca’z’où et respecter les best practices quand on le peut.
Qu’est ce que cela signifie au quotidien ? Et bien, tout simplement que si vous souhaitez que ESXi gère lui-même le placement des VM au sein sur les processeurs et se faisant optimise les accès mémoire – c’est une bonne idée, sauf cas particulier -, il suffit de ne provisionner que des sockets virtuels en laissant le nombre de cœurs à 1. Vous voulez avoir une machine de 4 vCPU, provisionnez 4 sockets et un cœur par socket.
L’exception concerne donc les très grosses machines disposant de beaucoup de mémoire et beaucoup de vCPU. En effet, ESXi expose la topology NUMA à l’OS invité et celui-ci peut donc tirer parti et optimiser lui-même le placement de ses process interne si vous indiquez à l’hyperviseur de reproduire le schéma déterminé par le nombre de cœur et le nombre de socket.
De manière très pragmatique, commencez à vous poser la question de la topology NUMA quand votre machine dispose de plus de 8/10/12 processeurs et une quantité de mémoire équivalente à au moins un bon 25/30% de la capacité physique réellement disponible sur les ESXi sur lesquels elle est susceptible de tourner.
ESXtop
Afin de regarder un peu comment ça se passe au sein de nos chers ESXi, il est possible d’utiliser esxtop pour visualiser les statistiques de localisation des VM par rapport à leurs processeurs d’élection. Pour se faire, il faut jouer de la ligne de commande, bien sûr. Connectez vous sur un de vos ESXi en ssh, puis lancez la commande en question.
Une fois lancée, appuyez sur la touche “m” pour passer sur la vue des statistiques mémoire. Sur la partie haute de l’affichage sont présentes les statistiques mémoire générales, dont les infos NUMA :
Ici, nous sommes sur un serveur bi-processeur disposant donc de deux banques mémoire (une par proc). Dans ces conditions, sur la ligne “NUMA”, vous trouvez la taille de chaque banque mémoire avec entre parenthèses l’espace disponible. Ensuite, pour avoir des statistiques spécifiques à chaque VM, il vous faut customiser un peu la vue du dessous. Tout d’abord, histoire de se consentrer, appuyez sur la touche “V” (majuscule) pour ne visionner que les process “VM” dans la liste. Ensuite, appuyez sur la touche “f” puis sélectionnez, en appuyant sur “g”, les statistiques NUMA :
La colonne “N%L” correspond à la quantité de mémoire “localisée” c’est à dire la quantité de mémoire située sur la banque du processeur sur lequel se trouve la machine à l’instant T.
Donc à l’avenir, prenez quelques secondes avant de choisir l’implantation vCPU de vos machines… ou pas et n’allouez que des sockets, ESXi s’en tirera comme toujours avec les honneurs !
Mes références pour ce billet:
Mark Achtemichuk, Does corespersocket Affect Performance? : ICI
Frank Denneman, NUMA Deep Dive “series” : ICI
Duncan Epping, ESXTOP : ICI
What is NUMA, sur SourceForge : ICI
Article très intéressant :) C’est bon à savoir ! Merci
Merci !
Par contre, n’hésitez pas à aller lire la série de billets de Franck Denneman sur NUMA, c’est vraiment super et on apprend plein d’autres choses ;)
En effet, ils sont passionnants mais assez complexes :).
Et surtout Franck n’a pas encore terminé la série d’articles qu’on attend avec impatience !!