Steuerung eines Hyper-V Clusters per USV
Technorati-Tags: Microsoft
Wie steuere ich einen Hyper-V Cluster per USV? Ein Leser meines Blogs schickte mir diese Frage – Vielen Dank für die Anregung!
Die Situation: Ein Hyper-V Cluster, Windows Server 2008 R2, jeder Knoten gesichert mit einer unterbrechungsfreien Stromversorgung (USV). Fällt der Strom aus, kann die USV eine gewisse Zeit überbrücken, ist jedoch ein alternatives Aggregat (z.B. ein Diesel-Generator) nicht vorhanden oder fehlerhaft, soll der Cluster mit allen virtuellen Maschinen (VM) geordnet heruntergefahren werden.
Erreicht werden soll das mit Bordmitteln. Leider schließt das eine der einfachsten und vielseitigsten Optionen aus, nämlich die Integration mit System Center. Mit dem Operations Manager und dem Virtual Machine Manager wäre eine umfassende Überwachung und vollautomatisierte Umsetzung ohne weiteres möglich. Aber auch die Bordmittel des Windows Server 2008 R2 reichen dafür aus.
Was passiert, wenn man einfach die USV-Agenten auf allen Cluster-Knoten so konfiguriert, daß sie die Knoten geordnet herunterfahren? Immer vorausgesetzt, man hat für die virtuellen Maschinen die Eigenschaften der Cluster-Ressourcen nicht verändert, versucht der Cluster, vor dem Herunterfahren eines Knotens die Cluster-Ressourcen auf andere Knoten zu verschieben. Bei nur wenigen VMs geht das relativ schnell, und wenn man geordnet Knoten für Knoten herunterfährt, werden die VMs jeweils auf die verbleibenden Knoten verschoben und mit dem letzten Knoten dann ebenfalls beendet. Das setzt natürlich voraus, daß ein Knoten allein die nötigen Kapazität besitzt, um alle VMs gleichzeitig betreiben zu können. Ist das nicht der Fall, bleiben während des Prozesses bereits einige VMs ‘auf der Strecke’ – was aber nicht gravierend ist, da ohnehin alles heruntergefahren werden sollte.
Funktioniert doch – warum ist das kein wirklich gutes Szenario? Es dauert einfach unnötig lang und ist unnötig komplex. Fällt der Strom aus, dann ist die Hoffnung ja durchaus berechtigt, daß er rechtzeitig wiederkommt und man keine Server herunterfahren muß. Man möchte daher so lange wie möglich abwarten. USV-Kapazität ist sehr teuer und daher zeitlich begrenzt. Das Herunterfahren der Systeme muß also so schnell wie möglich gehen, und die soeben beschriebene Vorgehensweise ist da hochgradig ineffizient.
Natürlich könnte man jetzt in jeder virtuellen Maschine einen USV-Agenten installieren. Das ist aber sehr aufwändig, und nicht jede VM hat notwendigerweise Netzwerk-Zugriff. Die naheliegende Option ist, das Herunterfahren der VMs über Hyper-V zu steuern.
Unser USV-Agent hat üblicherweise eine Funktion, die vor dem Herunterfahren eines Knotens beliebige Skripte ausführen kann. PowerShell ist dafür sehr gut geeignet. Für Hyper-V gibt es auf Codeplex die nötigen CMDlets: die “PowerShell Management Library for Hyper-V". Diese Bibliothek enthält die nötigen Kommandos. Mit ‘get-vm’ bekommen wir eine Liste aller VMs auf einem Knoten und deren Status, mit ‘stop-vm’ , ‘suspend-vm’ oder ‘shutdown-vm’ können wir sie gezielt herungerfahren. Ein Möglichkeit ist z.B.
get-vm | where-object {$_.EnabledState –eq $vmState.running} | shutdown-vm -Reason "Power Outage – USV Script"
Klingt gut, hat aber eine Schwachstelle: das Kommando ‘shutdown-vm’ funktioniert nur, wenn die Integration Components installiert sind. Das kann man mit z.B. mit ‘test-vmheartbeat’ herausfinden, indem man mit ‘get-vm’ alle VMs als Parameter einschleift. Daraus ergibt sich dann etwa:
get-vm | where-object {$_.EnabledState –eq $vmState.running} | test-VMheartbeat | where-object {$_.Status –eq “OK”} | shutdown-vm -Reason "Power Outage – USV Script"
Jetzt haben wir zumindest eine Chance, daß die VMs auch wirklich herunterfahren. Zurückmelden tun sie sich leider nicht – ist auch schwierig, wenn man sich soeben selbst abgeschaltet hat. Das könnten wir nach einer vernünftigen Wartezeit testen, z.B. indem wir ‘waitfor’ benutzen und auf ein Ereignis warten, daß nicht eintritt – wir brauchen hier ja nur den Countdown. Also für 1 Minute Wartezeit z.B.:
waitfor /T 60 NonExistentEvent | get-vm | where-object {$_.EnabledState –eq $vmState.running} | test-VMheartbeat | where-object {$_.Status –eq “OK”}
Andererseits ist die Wahrscheinlichkeit recht hoch, daß wir die VM auch beim zweiten Anlauf nicht herunterfahren können, wenn sie beim ersten Mal nicht reagiert hat. Daher sollten wir als nächstes auf ‘suspend-vm’ zurückgreifen. Damit erwischen wir außerdem auch alle VMs ohne Integration Components, wobei die Option ‘-wait’ dafür sorgt, daß das nächste Kommando erst gestartet wird, wenn ‘suspend-vm’ für alle noch aktiven VMs durchgelaufen ist:
get-vm -running | suspend-vm -wait
Wenn das auch nicht hilft, wäre dann noch ‘stop-vm’ möglich. Das entspricht ‘Turn Off’, also dem harten Ausschalten in Hyper-V. Schreiben Sie dann unbedingt noch ein Skript, daß sich hinterher bei den betroffenen VMs entschuldigt !!!
Wenn wir das jetzt so durchführen, fahren alle VMs herunter – und danach gleich wieder hoch … Der ein oder andere, der mit Failover Clustering Erfahrungen hat, wird schon seit einigen Minuten innerlich geschmunzelt haben. Die obigen Kommandos sind durchaus richtig und nützlich für einzelne Hyper-V Hosts, aber in einem Failover Cluster ist natürlich der Ansatz falsch, das Herunterfahren der VMs über Hyper-V zu steuern. Das müssen wir über den Cluster selbst erledigen. Vorteil: wir können das jetzt einmal für den kompletten Cluster machen und müssen das nicht einzeln pro Knoten erledigen. (Für die PowerShell v2-Profis: ja, das geht auch mit den obigen CMDlets, und zwar mit Hilfe von PowerShell Sessions.)
Auch hierfür gibt es PowerShell-Kommandos. Die werden mit Windows Server 2008 R2 mitgeliefert. Entweder benutzt man das ‘Failover Cluster PowerShell Management’ aus der Systemverwaltung, oder man fügt ganz einfach das Modul in einem normalen PowerShell v2-Fenster hinzu. Das funktioniert mit
‘import-module FailoverClusters’
Mit ‘get-command –module FailoverClusters’ bekommen wir dann alle verfügbaren CMDlets angezeigt.
Erst mal lassen wir uns alle aktiven VMs im Cluster anzeigen:
Get-ClusterResource | where-object {$_.ResourceType –like “Virtual Machine”} | where-object {$_.State –like “Online”}
Bevor wir nun die VMs alle herunterfahren, sollten wir sicherstellen, daß dabei das richtige Verfahren angewendet wird: ‘Turn Off’, ‘Save’, ‘Shut Down’ oder ‘Shut Down (Forced)’. Dieser Parameter heißt ‘OfflineAction’ , und man kann ihn mit ‘Get-ClusterParameter‘ auslesen und mit ‘Set-ClusterParameter’ verändern. Dabei ist der richtige Wert:
- Turn Off: 0
- Save: 1
- Shut Down: 2
- Shut Down (Forced): 3
Um z.B. alle VMs mit ‘Shut Down (Forced)’ zu beenden, nimmt man folgende Kommandozeilen:
Get-ClusterResource | where-object {$_.ResourceType –like “Virtual Machine”} | where-object {$_.State –like “Online”} | Set-ClusterParameter OfflineAction 3
Get-ClusterResource | where-object {$_.ResourceType –like “Virtual Machine”} | where-object {$_.State –like “Online”} | Stop-ClusterResource
So, jetzt bleiben die VMs heruntergefahren. Endlich! Sogar, nachdem der Strom wieder da ist und die Cluster-Knoten wieder gestartet sind … !!! Bitte dran denken: Die VMs müssen Sie nach dem Neustart des Clusters wieder starten. Sie sind ja jetzt nicht im Status ‘Failed’ – nur dann wird der Cluster versuchen, die VMs selbst zu starten. Um zu verhindern, daß die VMs nach dem Neustart plötzlich alle auf einem einzigen Knoten laufen, können Sie z.B. die Option ‘Allow Immediate Failback’ für die Cluster-Gruppen setzen, oder Sie verschieben Sie automatisiert mit ‘Move-ClusterGroup’ .
Mit freundlichen Grüßen!
Ralf M. Schnell