ADMIN

2024

06

2024-05-30T12:00:00

Datenbanken

SCHWERPUNKT

074

Virtualisierung

Virtuelle Maschinen

Virtuelle Maschinen richtig dimensionieren

Maßgeschneidert

von Wolfgang Weith

Veröffentlicht in Ausgabe 06/2024 - SCHWERPUNKT

IT-Architekten stehen oft vor der Herausforderung, dynamische Anforderungen zu erfüllen. Während ein genereller Trend hin zu cloudnativen Applikationen und Microservices geht und sich das primär in vergleichsweise kleinen, dafür aber vielen virtuellen Maschinen – sogenannten Container-Hosts – manifestiert, gibt es andererseits immer noch Anwendungen wie SAP HANA, die extrem große und leistungshungrige VMs erfordern. Diese werden häufig als Monster-VMs bezeichnet und erfordern den richtigen Umgang.

Große, ressourcenhungrige und monolithische Applikationen wie Oracle, Microsoft SQL und insbesondere SAP HANA erfolgreich auf VMware vSphere zu betreiben, ist anspruchsvoll, da es viel mehr Faktoren zu bedenken gibt als bei kleinen VMs. Viele der in diesem Artikel genannten Konfigurations-Best-Practices stammen aus dem SAP-HANA-Umfeld, sind aber auf andere Monster-VMs und entsprechende Applikationen ebenso anwendbar. Eine vollständige Betrachtung würde den Rahmen dieses Artikels sprengen, daher liegt der Fokus auf den wichtigsten Regeln. Diese kommen in allen Phasen des Lebenszyklus einer großen VM zum Tragen. Monster-VMs sind schwierig beim Erstellen, Verwalten und Verschieben. Einige der Herausforderungen dabei sind:
- die saubere Konfiguration des physischen Servers (BIOS, CPU, Memory, Netzwerkkarten),
- das richtige initiale Sizing (vCPUs, NUMA Alignment, Reserven für den Hypervisor) und
Große, ressourcenhungrige und monolithische Applikationen wie Oracle, Microsoft SQL und insbesondere SAP HANA erfolgreich auf VMware vSphere zu betreiben, ist anspruchsvoll, da es viel mehr Faktoren zu bedenken gibt als bei kleinen VMs. Viele der in diesem Artikel genannten Konfigurations-Best-Practices stammen aus dem SAP-HANA-Umfeld, sind aber auf andere Monster-VMs und entsprechende Applikationen ebenso anwendbar. Eine vollständige Betrachtung würde den Rahmen dieses Artikels sprengen, daher liegt der Fokus auf den wichtigsten Regeln. Diese kommen in allen Phasen des Lebenszyklus einer großen VM zum Tragen. Monster-VMs sind schwierig beim Erstellen, Verwalten und Verschieben. Einige der Herausforderungen dabei sind:
- die saubere Konfiguration des physischen Servers (BIOS, CPU, Memory, Netzwerkkarten),
- das richtige initiale Sizing (vCPUs, NUMA Alignment, Reserven für den Hypervisor) und
- vMotion (Zeitpunkt, Dauer, Performance, negative Auswirkungen).
CPU richtig einsetzen
Die virtuelle CPU-Konfiguration spielt eine zentrale Rolle für die Leistung der VMs. Eine adäquate CPU-Konfiguration gewährleistet, dass jede VM über die notwendige Rechenleistung verfügt, um ihre Aufgaben effizient auszuführen, ohne die Ressourcen des Hosts unnötig zu belasten.
vSphere unterscheidet zwischen physischen CPUs (pCPUs) und virtuellen CPUs (vCPUs). pCPUs befinden sich im physischen Server und sind die tatsächlichen Hardwarekomponenten, die Rechenoperationen durchführen. Ein physischer CPU-Socket kann mehrere CPU-Cores enthalten und jeder Core kann mittels Hyperthreading zwei Threads gleichzeitig verarbeiten. Hier lauert bereits eine erste Falle im Hinblick auf Performance: Hyperthreading stellt zwei logische CPUs (die Hyperthreads) bereit. Es gibt also bei aktiviertem Hyperthreading doppelt so viele Möglichkeiten, einen Thread auszuführen, wie physische Cores verbaut sind.
Dem Hypervisor gibt dies die Möglichkeit, vCPUs besser zu schedulen und somit flüssiger zu arbeiten. Hyperthreading verdoppelt jedoch nicht die Performance. Das Sizing einer CPU-hungrigen Applikation sollte also immer die physische Core-Anzahl als Grundlage nehmen und nicht die Anzahl der Hyperthreads, die dann im Folgenden üblicherweise mit der Anzahl der für die VM verfügbaren vCPUs korreliert. Anders ausgedrückt: Die Auswirkungen von Hyperthreading auf die Performance beziehungsweise Peformancesteigerung sind kritisch zu bewerten. In einigen Fällen, insbesondere bei Anwendungen, die stark vom CPU-Cache abhängig sind, kann Hyperthreading sogar zu Leistungseinbußen führen, da sich mehrere Threads den gleichen Cache teilen. Gründliche Tests vor Inbetriebnahme und durchgehende Überwachung in der Produktion sind unerlässlich, um sicherzustellen, dass Hyperthreading tatsächlich einen Vorteil bietet.
vCPUs hingegen sind eine Abstraktion, die es einer VM ermöglicht, Rechenressourcen so zu nutzen, als ob sie direkt auf die physische Hardware zugreifen würde. Die Anzahl der einer VM zugewiesenen vCPUs sollte sich nicht am maximal Möglichen orientieren, sondern an der tatsächlich geforderten Performance oder Skalierung (zum Beispiel Anzahl der parallelen Datenbanknutzer). Die Applikationshersteller haben hierfür in der Regel auch gute Sizing-Tools parat.
Non-Uniform Memory Access
NUMA (Non-Uniform Memory Access) ist ein entscheidender Aspekt in der Architektur moderner Multi-Core-/Multi-Socket-Systeme, der den effizienten Zugriff auf den Arbeitsspeicher optimiert. In NUMA-Systemen (also vereinfacht gesagt Multiprozessor-Systemen) wird der RAM in verschiedene Bereiche aufgeteilt, die jeweils einem bestimmten CPU-Socket zugeordnet sind.
Die Kombination aus dem Prozessor (Socket) und dem direkt angebundenen Arbeitsspeicher ist die NUMA-Node. Liegt eine VM mit ihren vCPUs also auf einem Socket, kann sie auf das direkt angeschlossene RAM mit der höchsten Bandbreite und der geringsten Latenz zugreifen. Die Rede ist hier auch von NUMA-Lokalität. Erfolgt der Zugriff von einer CPU auf das RAM hinter dem Memory-Controller einer anderen CPU, erhöht sich die Latenz des RAM Zugriffs. Bild 1 illustriert dies für ein Zwei-Socket-System.
Bild 1: Ein Zwei-Socket-System (Zwei-NUMA-Node) und Illustration des lokalen und Remote-RAM-Zugriffs von CPU0.
Je nach Servertyp (Zwei-Socket-, Vier-Socket- oder Acht-Socket-System) sind verschiedene Memory-Bus-Architekturen im Einsatz, die zu unterschiedlich langen Wegen von einer CPU zum Arbeitsspeicher einer anderen CPU führen. Die Auswahl des Servers hat also eventuell schon direkte Auswirkungen auf die zu erwartende Performance der virtualisierten "Monster-Applikation".
vSphere unterstützt NUMA in virtuellen Umgebungen, indem es sicherstellt, dass die Speicher- und CPU-Ressourcen effizient genutzt werden, um die Leistung zu maximieren. Die korrekte Konfiguration von NUMA kann insbesondere bei großen VMs mit hohen Speicher- und Rechenanforderungen signifikante Leistungsverbesserungen bringen. Unabhängig von Servertyp und -größe sind diverse BIOS/ UEFI-Einstellungen eine Grundvoraussetzung für hoch performante Monster-VMs:
- Aktivieren von Turbo Boost (sofern konfigurierbar), was zu einer ausgewogenen Arbeitslast auf ungenutzten Kernen führt.
- Nutzen von Hyperthreading für besseres CPU-Scheduling der VMs.
- In Systemen wie HPE-Servern wird NUMA eingeschaltet, indem Admins das Node-Interleaving deaktivieren.
- Aktivieren von erweiterten CPU-Funktionen wie VT-x/AMD-V, EPT, NX/XD und RVI.
- Deaktivieren aller nicht verwendeten Geräte und BIOS-Funktionen (zum Beispiel Video-RAM-Cacheable, On-board-Audio, serielle Ports, CD-ROM oder USB).
- Ausschalten des "C1E Halt State".
- Deaktivieren der "Enhanced C-States".
- Konfiguration des Power-Managements auf "High Performance” und somit Unterbinden von Power-Saving und damit verbundener Performancereduzierung.
Mit diesen Hostkonfigurationen sind die Grundlagen für das Erstellen der Monster-VMs gelegt.
Korrekte VM-Größe
Der Grundsatz "größer ist besser" stimmt im Kontext von virtuellen Maschinen nur bedingt. Natürlich ist es das Ziel, den Host und seine CPU- und RAM-Ressourcen maximal auszunutzen, und auch der SAP-HANA- oder Oracle-Admin will möglichst alle CPUs/Cores/Hyperthreads der Physik auch für die VM einsetzen. Aber genau hier passiert der zweite große Fehler.
Der Hypervisor selbst benötigt auch Ressourcen. Der VMkernel des ESXi-Servers muss sich um viele Dinge kümmern, stellt er doch die Grundfunktionen des Hypervisors wie CPU-Scheduling, RAM-Verwaltung, virtuelles Netzwerk – also Standard-vSwitch (VSS) oder verteilten virtuellen Switch (VDS) – bereit, kümmert sich um den Storage-Stack (NFS, VMFS, VVOL, vSAN) inklusive Multipathing für Block-Storage, um nur einige der zentralen Komponenten zu nennen.
Dazu kommen eventuell noch weitere Integrationen wie etwa mit VMware NSX für Netzwerkvirtualisierung und verteiltes Firewalling (DFW). Und zu guter Letzt sollen ja auch Funktionen wie DRS- und HA-Clustering und vMotion nutzbar sein.
Zusätzlich benötigt jede VM zur Laufzeit auch VMkernel-Prozesse, die CPU und RAM überwachen sowie virtuelle Netzwerkkarten, virtuelle SCSI-Adapter, virtuelle Grafikkarte et cetera abbilden. Übergreifend ist hier vom Virtualisierungs-Overhead die Rede. Für die Kernel-Prozesse und den Virtualisierungs-Overhead der VMs (groß oder klein) sollten entsprechend CPU und RAM Ressourcen reserviert, also nicht von VMs belegt, werden.
Genügend Puffer einplanen
Gerade für Monster-VMs ist Verfügbarkeit und Performance entscheidend. Indem Sie diese VMs etwas kleiner dimensionieren als die verfügbaren NUMA-Node- oder Host-Ressourcen verhindern Sie Konkurrenz zwischen VM und Hypervisor. Die zuvor aufgezählten Kernel-Funktionen sind Funktionen des Hypervisors und damit gegenüber der VM priorisiert.
Würden Sie nun VMs so dimensionieren, dass alles an RAM und CPU in Beschlag genommen wird und der VMkernel muss nun eine weitere Aufgabe ausführen (etwa eine vMotion-Migration), würden die VMs im Zugang zum CPU-Scheduling depriorisiert und im Gastbetriebssystem und der Applikation würde das im besten Falle zu einer Performancebeeinträchtigung, im schlimmsten Fall zu einem temporären Stillstand führen. Dies gilt es, durch sauberes VM-Sizing zu verhindern.
Wie sieht denn nun ein sauberes Sizing aus, oder anders gefragt, wie viel CPU und RAM sollte dem Hypervisor zur Verfügung stehen? Die Antwort lautet: es kommt darauf an, welche Funktionalitäten auf dem ESXi genutzt werden. Nutzen Sie den Hypervisor out of the box inklusive Clustering (DRS/HA), können Sie mit fünf bis sieben Prozent der RAM- und CPU-Ressourcen rechnen. Kommt vSAN hinzu, wird natürlich mehr RAM und auch mehr CPU benötigt und wir sind sehr schnell bei zehn Prozent oder mehr.
Machen wir den CPU-Overhead anhand eines Beispiels fest:
- Der Server ist mit vier 28-Kern-CPUs (also 56 Hyperthreads pro CPU) ausgestattet.
- Fünf Prozent von 28 Cores entsprechen rechnerisch 1,4 Kernen.
- Sieben Prozent von 28 Cores entsprechen dann 1,96 Kernen.
Wenn wir also zwei Kerne pro Socket für Kernel und Virtualisierungs-Overhead reservieren, sind wir auf der sicheren Seite. Uns stehen also pro NUMA Node 26 Cores oder 52 Hyperthreads für die VM zur Verfügung. Bild 2 zeigt einen Vier-Socket-Server mit jeweils 28 Kernen pro Socket und 6 TByte RAM.
VMs richtig dimensionieren
Wie also werden Monster-VMs gemäß der genannten Regeln sauber dimensioniert? Blicken wir dazu auf Bild 3. Wir sehen den Server aus Bild 2 mit 6 TByte RAM, 4 Sockets zu jeweils 28 Kernen beziehungsweise 56 Hyperthreads. Wenn wir nun eine VM erstellen, können wir das natürlich völlig falsch machen und dies mit 56 vCPUs und 1,5 TByte vRAM tun (im Bild repräsentiert durch die rote Monster-VM2). Oder wir machen es korrekt: Also 26 Kerne/52 Hypertreads pro NUMA-Node. Für den RAM-Overhead rechnen wir hier schon großzügig mit zehn Prozent. Die resultierende saubere VM-Größe ist also 1,35 TByte RAM und 52 vCPUs (also die Anzahl der korrelierenden Hyperthreads für 26 Cores) – im Bild repräsentiert durch Monster-VM1 in grün.
Bild 2: Ein Vier-Socket-System (Vier-NUMA-Node) mit 6 TByte RAM. Das entspricht vier CPU-Sockets mit jeweils 28 Kernen und jeweils 1,5 TByte RAM pro NUMA-Node.
Wir sprechen von Monster-VMs und daher werden häufig deutlich größere VMs gebaut, also VMs, die größer sind als ein NUMA-Node. In Bild 3 findet sich zusätzlich eine Dual-NUMA-Node-VM, die sich über die NUMA-Nodes 2 und 3 spannt. Auch hier gilt die gleiche Regel, zwei Cores pro NUMA-Node als Reserve für den ESXi-Host und zehn Prozent RAM. Resultierende Größe ist hiermit 104 vCPUs und 2,7 TByte RAM. Maximieren wir die VM auf wirkliche Monstergröße, nimmt sie alle vier NUMA-Nodes ein und kommt im Sizing auf 5,4 TByte und 208 vCPUs: vier mal (28-2) Kerne mal zwei Hyperthreads entspricht 208 Hyperthreads.
Bild 3: Vier-Socket-System (Vier-NUMA-Node) mit 6 TByte RAM: Vier CPU-Sockets mit jeweils 28 Kernen und 1,5 TByte RAM pro NUMA-Node. Zwei Single-NUMA-Node-VMs und eine Dual-NUMA- Node-VM.
So weit, so gut. Wir erinnern uns: Unser Ziel für SAP HANA, Oracle und andere Business-kritische Monster-VMs ist Incident-freier Betrieb, maximale Verfügbarkeit und maximale Performance. Treffen wir also noch einige Vorkehrungen hierfür. Wir reservieren sämtlichen RAM und die gesamte konfigurierte CPU-Leistung der VM, da uns dies später Vorteile für VMotion bringt.
Zu beachten ist hier, dass die Reservierung nicht in Anzahl an CPUs erfolgt, sondern in der korrelierenden MHz/GHz-Zahl. Kommen wir zurück zum NUMA-Alignment. Wenn die VM größer wird als ein NUMA-Node, ist es empfehlenswert, der VM nicht einfach nur vCPUs zu geben, sondern die virtuelle CPU-Topologie auch entsprechend anzupassen, also mit virtuellen Kernen und Sockets zu arbeiten. Erstellen Sie eine neue VM, ist der Default für die CPU-Topologie "Assigned at power on". Wir könnten auch sagen, der ESXi wählt die passende Topologie.
Für Multi-NUMA-Node-VMs wollen wir aber explizit die Topologie bestimmen. Das geschieht im Reiter "VM Options", wo wir in das Feld "Cores per Socket" die passende Anzahl eintragen. Sind der VM beispielsweise 16 vCPU zugeordnet und handelt es sich um eine Dual-NUMA-Node-VM, tragen Sie hier also acht Cores pro Socket ein. Automatisch erscheint nun "Sockets: 2" und eine Performance-Warnung. Wir haben unsere Einstellungen jedoch korrekt vorgenommen und können daher die Warnung guten Gewissens ignorieren.
NUMA-Alignment steuern
Wenden wir dies nun auf die Monster-VM 4 aus dem Beispiel weiter oben an, würde die VM mit 208 CPUs sowie 52 Cores per Socket konfiguriert, was in vier virtuellen Sockets resultiert. Die VM hat damit ein NUMA-Layout, das perfekt zu dem des physischen Servers passt.
Kommen wir zurück zur kleineren Monster-VM (Single- oder Dual-NUMA-Node): Wir haben also schon die perfekte Größe konfiguriert. Aber wie stellen wir nun sicher, dass die VM wirklich auf dem physischen NUMA-Node bleibt und nicht ungünstig über mehrere verteilt läuft? Arbeitet eine VM perfekt auf einem NUMA-Node, sprechen wir von "NUMA aligned", im anderen Fall von "NUMA unaligned". Den zweiten Fall wollen wir vermeiden, denn dann greift das Thema Remote-Memory-Zugriff und die resultierende ungewollte Latenz, die wir weiter oben bereits betrachtet hatten.
Monster-VM A in Bild 4 ist also perfekt aligned, während VM B unaligned ist. Doch wie korrigieren wir diesen Zustand? Zunächst wollen wir, dass die vCPUs die Hyperthreads auf dem lokalen Socket nutzen und nicht auf andere Cores auf anderen Sockets ausweichen. Dies erreichen wir durch ein VM-Advanced-Setting im "vmx"-File der VM:
numa.vcpu.preferHT=TRUE
Bild 4: Zum Vergleich einmal NUMA-aligned versus NUMA-unaligned.
Damit die VM A in unserem Beispiel auch wirklich auf dem NUMA-Node 0 bleibt, stellen wir dies über einen weiteren Parameter sicher:
numa.nodeAffinity = 0
Möchten wir also auch die Monster-VM B sauber in ein NUMA-Alignment bringen und auf den NUMA-Node 1 setzen, wären folgende Konfigurationen im Fall von VM A nötig:
numa.vcpu.preferHT=TRUE
numa.nodeAffinity = 0
sowie im Fall von VM B:
numa.vcpu.preferHT=TRUE
numa.nodeAffinity = 1
In Bild 5 haben wir auf die restlichen beiden NUMA-Nodes noch eine Monster-VM C gelegt, die sich über zwei NUMA-Nodes (Node 2 und 3) spannt. Diese VM muss entsprechend folgende Konfigurationsparameter tragen:
numa.vcpu.preferHT=TRUE
numa.nodeAffinity = 2,3
Bild 5: Die NUMA-aligned VMs mit der Monster-VM C.
VMs zügig verschieben
Wir haben also eine VM, die sauber gesized sowie NUMA-aligned ist und das auf einem UEFI-seitig perfekt konfigurierten Host. Oracle oder HANA sind installiert und laufen problemlos. Früher oder später aber muss sicherlich die Server-Firmware aktualisiert oder der ESXi-Server auf eine neue Version oder Patch-Level gehoben werden. Und das führt zu einem Neustart. Dann greift eines der besten und beliebtesten Features der VMware-Virtualisierung: vMotion, also die Livemigration von einem ESXi-Host zum anderen.
Die gute Nachricht ist, dass vMotion über die diversen vSphere-Versionen immer wieder optimiert wurde und die letzten wirklich großen Verbesserungen mit vSphere 7.0U2 eingeführt wurden. Die meisten VMs, egal welcher Größe, lassen sich per VMotion ohne Einschränkung verschieben.
Ist eine VM zu migrieren, soll dies in der Regel so schnell wie möglich geschehen, vor allem wenn der ESXi-Host über moderne 25-, 40- oder gar 100-GBit-Netzwerkadapter verfügt und die VM extrem groß ist, also beispielsweise 5,4 TByte RAM den Besitzer wechseln müssen. Und hier folgt die schlechte Nachricht: Auch wenn ein 100-GBit-Adapter im Server werkelt, müssen Sie noch ein wenig VMotion-Tuning betreiben, damit Sie die Bandbreite auch nutzen können. Wir ersparen uns hier die Grundlagen zur Funktionsweise von vMotion und steigen direkt ins Tuning ein. Der Einfachheit halber nehmen wir das Beispiel eines 100-GBit-Adapters und eines einzigen vMotion-VMkernel-Ports, was der Realität in vielen Umgebungen entspricht oder nahekommt.
Möchten Sie eine VM verschieben, starten ein VMotion-Prozess sowie diverse Helferprozesse. Zu Letzteren gehören der Completion-Helper, der Crypto-Helper und der Stream-Helper. Fokussieren wir uns hier auf den Stream-Helper, denn dieser Prozess ist derjenige, der für die Übertragung des RAM-Inhalts vom Quell- zum Ziel-ESXi-Server verantwortlich ist; analog gibt es diese Prozesse auch auf dem Zielserver.
Nun zum eigentlichen Problem. Ein einzelner vMotion-Stream (und damit Stream Helper) ist auf eine durchschnittliche Bandbreite von 15 GBit limitiert. Das heißt, wir würden trotz 100-GBit-NIC nur maximal 15 GBit vMotion-Bandbreite realisieren und der vMotion-Prozess würde relativ langsam laufen. Wenn wir aber multiple Streams erzeugen, steigt die Bandbreite und vMotion wird beschleunigt:
- ein Stream = etwa 15 GBit
- zwei Streams = etwa 30 GBit
- drei Streams = etwa 45 GBit
- sechs Streams = etwa 90 GBit
- sieben Streams = etwa 105 GBit
Wollen wir unser 100-GBit-Interface also voll ausnutzen, dann müssen wir sieben Streams konfigurieren, denn das geschieht in der Regel nicht automatisch und für unsere Monster-VMs wollen wir auf Nummer sicher gehen. Dazu ist es notwendig, den Parameter "Migrate.VMotionStreamHelpers" in den "Advanced Systems Settings" aller ESXi-Server auf den entsprechenden Wert zu setzen, also in unserem Fall "7". Der Default-Wert ist "0", was einem automatischen Erstellen entspricht, das aber in der Regel nicht mehr als vier Stream-Helper startet. Daher setzen wir die Werte hier manuell.
Zwischen dem VMkernel-Port und der physischen NIC gibt es aber auch noch eine beziehungsweise mehrere Hardware-Queues. Um die maximale vMotion-Performance zu erreichen, müssen wir noch die Anzahl der Hardwarequeues an die Anzahl der Softwarequeues (die mit der Anzahl der Stream-Helper übereinstimmt) anpassen. In unserem Fall also auch auf "7". Dies geschieht ebenfalls in den erweiterten Systemeinstellungen des ESXi-Hosts über den Parameter "Net.TcpipRXDispatchQueue". Der Default hier ist "2". Da es sich um eine hardwarenahe Änderung handelt, benötigt der Server anschließend einen Reboot.
Fazit
Wir haben für unsere Monster-VMs nun die wichtigsten Schritte vollzogen, um sie sauber zu dimensionieren, auf NUMA-Nodes zu verteilen und die Voraussetzungen für ein problemloses vMotion zu schaffen. Mit diesen Regeln sollten 85 bis 90 Prozent aller Monster-VMs, egal ob HANA, Oracle oder MS SQL, verfügbar und performant laufen und sich verschieben lassen. Die restlichen zehn bis 15 Prozent sind mit weiteren Hürden versehen wie einer hohen Latenzempfindlichkeit im Bereich Memory, Anforderungen an niedrigste Netzwerklatenz aufgrund von Systemreplikation oder Echtzeitanwendungen.
(dr)