Hyper-V: die ideale Anzahl vCPUs

Bei der Installation eines virtuellen Servers steht man früher oder später vor der Frage nach der Anzahl virtueller Prozessoren, die diesem zugewiesen werden sollen. Da trifft man in der Praxis viel an, von absolutem Minimalismus bis zu "viel hilft viel". Dieser Beitrag soll bei der Ermittlung einer vernünftigen Anzahl vCPUs helfen.

Können es zu viele virtuelle Prozessoren sein?

Ja, leider. Auch eine vCPU, die nichts macht, benötigt Ressourcen. Die Folge einer starken Überbuchung sind Performance-Probleme. Diese äussern sich beispielsweise durch "Hänger" in Terminalsitzungen: Man tippt, aber der Text erscheint erst verzögert. Und das, obwohl weder der Task-Manager in der VM noch auf dem Host eine nennenswerte Last zeigen. Der Task-Manager lügt nicht: Die vCPU macht dann tatsächlich nichts. Sie wartet darauf, an die Reihe zu kommen, um etwas zu machen.

Es ist deshalb nicht ratsam, allzu grosszügig zu sein bei der Zuweisung von vCPUs.

Weshalb führen zu viele vCPUs zu Performance-Problemen?

Man kann sich den Scheduler des Hypervisors wie einen Kellner in einem Restaurant vorstellen. Dieses hat so viele Plätze, wie die physischen CPUs Cores haben. Der Hypervisor eines Servers mit zwei CPUs mit je 16 Cores hat also 32 Plätze zu verteilen. (Zu Hyper-Threading später mehr.)

Fordert VM A Rechenleistung an, will sie in unserem Beispiel also im Restaurant essen. Sie hat zwei vCPUs, also führt der Kellner diese an zwei Plätze. Gleichzeitig kommen noch VM B mit vier vCPUs und VM C mit acht vCPUs zum Essen. Macht total 14 Plätze. Kein Problem, so viele Cores sind vorhanden, alle können gleichzeitig essen.

Kurze Zeit später steht VM D an der Türe. Diese hat 24 vCPUs. vCPUs essen nur gemeinsam. Es müssen also gleichzeitig 24 Plätze frei sein. VM D muss also warten, bis sechs Plätze frei werden. VM A und B oder VM C müssen also gehen.

Während VM D am Essen ist, können nur VMs mit total acht vCPUs gleichzeitig bedient werden.

Dies auch, wenn auf der VM nur eine vCPU tatsächlich etwas tut!

Zusammenfassung

Eine VM bekommt immer nur dann Rechenzeit, wenn so viele Cores zur Verfügung stehen, wie sie zugewiesen hat. Jede vCPU belegt einen physischen Core, auch wenn sie gerade nichts zu tun hat.

Präzisierung

Die Aussage im letzten Abschnitt ist nicht mehr ganz korrekt. Hyper-V kann "Co-Scheduling", also den vCPUs nacheinander Rechenzeit geben. Das geht aber nur begrenzt, denn der Scheduler in den VMs verteilt die Aufgaben auf alle vCPUs, sodass doch viel gewechselt werden muss.

Fazit

Jeder Kontextwechsel benötigt Zeit. Die vCPU muss auf den Core geladen werden, bevor sie mit der Arbeit beginnen kann. Je mehr der Host "überbucht" ist, desto mehr Kontextwechsel gibt es. Ein stark überbuchtes System verliert viel Zeit mit Kontextwechseln, anstatt sie für Rechenarbeit einzusetzen.

Wie fest darf überbucht werden?

In der Praxis haben sich Werte von 4:1 bis maximal 8:1 als vernünftig herausgestellt. Wobei das natürlich stark davon abhängig ist, wie stark die VMs ausgelastet sind. Ein SQL Server, der seine Cores auslastet, sollte sich diese nicht mit anderen VMs teilen müssen. In einer Testumgebung mit wenig Betrieb kann man hingegen höhere Werte erreichen.

Was ist mit Hyper-Threading?

Schaut man im Task-Manager, verdoppelt Hyper-Threading die Anzahl Cores: ein Socket * acht Cores macht 16 logische Prozessoren.

Das ist jedoch nicht ganz korrekt. Bei Hyper-Threading bekommt jeder Core eine zweite Zuleitung. Damit können zwei Threads gleichzeitig ausgeführt werden. Es wird aber nicht gleichzeitig in beiden Threads gerechnet, sondern abwechslungsweise. Wenn Thread 1 auf Daten warten muss, kann in der Zwischenzeit Thread 2 ausgeführt werden und umgekehrt.

Um beim Essens-Vergleich zu bleiben: Man kann mit beiden Händen (Threads) das Essen greifen. Während eine Hand den Mund (Core) füllt, kann die andere Hand den nächsten Bissen aufnehmen. Man kann jedoch nur eine Portion gleichzeitig kauen. Ein Mund mit zwei Händen isst schneller als ein Mund mit einer Hand, aber nicht so schnell wie zwei Münder.

In der Praxis bringt Hyper-Threading einen Performance-Gewinn von um die 20 %. Da aktuelle Betriebssysteme und Hyper-V damit umgehen können, sollte man es aktiviert lassen. Die früher kursierende Empfehlung, es zu deaktivieren, ist überholt.

Anmerkung zu Hyper-Threading

Seit Windows Server 2019 verwendet Hyper-V den Core-Scheduler. Dieser bietet einige Vorteile gegenüber dem früheren Classic-Scheduler. Unter anderem werden aus Sicherheitsgründen pro Core nur noch vCPUs einer VM ausgeführt. Wenn man Hyper-Threading aktiviert hat und zwei VMs mit je einer vCPU laufen, werden diese nicht gleichzeitig auf dem gleichen Core ausgeführt. Die Hälfte der logischen Prozessoren liegt also brach. Dies wurde eingeführt zum Schutz vor Cache-Attacken wie Meltdown und Spectre. Quelle: https://github.com/MicrosoftDocs/windowsserverdocs/blob/main/WindowsServerDocs/virtualization/hyper-v/manage/manage-hyper-v-scheduler-types.md

Damit VMs wissen, dass die CPU Hyper-Threading aktiviert hat, sollte der Parameter "HwThreadCountPerCore" gesetzt werden. Dies geht erst ab VM-Version 9, also Windows Server 2019. VMs in älteren Versionen oder solche, auf Version 9 angehoben wurden, verwenden den Wert "1", wissen also nichts von Hyper-Threading.

Es ist empfohlen, den Wert auf "0" zu setzen. Damit wird die Einstellung des Hosts übernommen.

Die aktuelle Version sieht man im Hyper-V-Manager oder kann sie mit "Get-VM | FT Name, Version" anzeigen. Aktualisiert werden kann die Version ebenfalls im Hyper-V-Manager oder mit "Update-VMVersion".

Den Wert von "HwThreadCountPerCore" kann man mit "Get-VM | Get-VMProcessor | FT VMName,HwThreadCountPerCore" abrufen und mit "Get-VM | Set-VMProcessor -HwThreadCountPerCore 0" für alle VMs auf "0" setzen.

Quelle: https://kevinholman.com/2019/07/22/windows-server-2019-hyper-v-might-only-use-half-the-available-cpus/

NUMA

Bei Mehrprozessorsystemen ist NUMA zu beachten. Das steht für "Non-Uniform Memory Access". Diese Architektur verwenden alle modernen Rechner. Jede CPU hat ihr eigenes RAM. Zugriffe auf den Arbeitsspeicher einer anderen CPU sind möglich, aber langsamer. Für Details siehe https://de.wikipedia.org/wiki/Non-Uniform_Memory_Access.

Bezüglich Virtualisierung heisst das, dass man einer VM maximal so viele vCPUs zuweisen sollte, wie eine CPU Cores hat. (Bei Hyper-Threading zählen die logischen Prozessoren.) Hat man zwei CPUs mit je 16 Cores und eine VM mit 24 Cores, macht diese zwangsläufig Speicherzugriffe über NUMA-Grenzen hinweg und die Performance leidet. Überschreitet man das Limit nicht, ist der Scheduler von Hyper-V schlau genug, um die NUMA-Grenzen zu beachten.

Wie viele vCPUs sollen es denn sein?

Möglichst wenig, das ist nun klar. Aber so viel wie nötig, das ist auch logisch.

Zur Auffrischung: Die Anzahl vCPUs definiert, wie viele Aufgaben (Threads) gleichzeitig ausgeführt werden können. Aufgaben lassen sich nur selten parallelisieren, sodass auch ein SQL Server für eine Abfrage meist nur eine vCPU auslastet.

Wenn ein Datenbank- oder Webserver mehrere Anfragen gleichzeitig verarbeiten soll, verbessern mehr vCPUs die Leistung.

Wichtig: vCPUs sind da, um verwendet zu werden. Wenn eine virtuelle Maschine zwei vCPUs hat und diese zu 40 % auslastet, ist sie nicht schneller, wenn man ihr vier vCPUs zuweist und die Auslastung dann nur noch 20 % beträgt!

Als allgemeiner Rat gilt: Sich von unten ans Optimum herantasten. Mit wenig vCPUs anfangen. Wenn die Auslastung über 80 % liegt, Anzahl vCPUs erhöhen.

Nachfolgend einige Empfehlungen für verschiedene Einsatzzwecke:

Domänencontroller

Microsoft ist sehr konservativ und nennt eine vCPU pro 1000 gleichzeitige Benutzer. Das sind Umgebungen mit ungefähr 10000 Benutzern im Active Directory.

Ein Domänencontroller mit einer vCPU läuft gut, geht aber in die Knie, wenn man Windows Updates installiert und diese die vCPU komplett auslasten. Deswegen ist es gut, zwei vCPUs zu haben. (Es kommt aber natürlich auf die Situation an. In sehr dichten Umgebungen oder mit wenig Cores im Host fährt man allenfalls besser, wenn die DCs nur eine vCPU haben.)

Dateiserver

Die Dateidienste benötigen nur wenig Rechenleistung. Wenn man keine rechenaufwendigen Features wie Deduplizierung aktiviert hat, sollten zumindest in kleineren Umgebungen zwei vCPUs reichen.

Datenbankserver

Bei Datenbankservern ist die sinnvolle Anzahl vCPUs abhängig von der Software und den Anforderungen. Der SQL Server Express verwendet beispielsweise nur eine vCPU. Da reicht es, wenn man eine vCPU für das Betriebssystem dazugibt und zwei vCPUs verwendet.

Da wie weiter oben erwähnt die meisten Abfragen nicht parallelisierbar sind, kommt es ganz auf die Anzahl gleichzeitigen Anfragen an. Wenn länger laufende Auswertungen durchgeführt werden, sind vier vCPUs möglicherweise sinnvoll.

Terminalserver

Bei dieser Serverrolle verschätzt man sich erfahrungsgemäss am meisten. Die sinnvolle Anzahl vCPUs hängt von der Anzahl gleichzeitig auf dem Server arbeitenden Benutzer ab. Microsoft unterscheidet verschiedene Benutzertypen:

TypBeispielbenutzertypische AnwendungenBenutzer pro vCPU
leichtLagermitarbeitereinfache Datenbankanwendung, Kommandozeilenanwendung6
mittelSekretariatMicrosoft Word4
schwerSoftware-EntwicklerVisual Studio2
extremCAD-ZeichnerCAD1
Quelle: https://learn.microsoft.com/en-us/windows-server/remote/remote-desktop-services/virtual-machine-recs

Je nach Komplexität der verwendeten Anwendungen und Websites liegen die meisten Benutzer wohl zwischen mittel und schwer. Als guten Anfangswert würde ich eine vCPU pro zwei Benutzer nehmen. Bei steigender Benutzerzahl dann eher weniger, da sich die Last besser verteilt. Ausnahmen sind Situationen, in denen sich viele Benutzer gleichzeitig anmelden.

Wie bei den anderen Szenarien gilt: Sich von unten an das Optimum herantasten.

Zusammenfassung

  • weniger vCPUs sind besser als zu viele
  • nicht mehr vCPUs zuweisen, als eine CPU Cores hat
  • Konfigurationsversion der VMs prüfen und ggf. aktualisieren und HwThreadCountPerCore auf 0 setzen
  • Entscheidungen aufgrund von Messungen und nicht Vermutungen treffen