Wer ein Microsoft Failover Cluster betreibt, hat bestimmt auch eine USV im Einsatz. Diese soll verhindern, dass die Knoten bei einem kleinen Spannungsabfall nicht sofort ausgehen, dass die Server keine Überspannung abbekommen und das bei einem längeren Ausfall die Systeme kontrolliert herunterfahren.
Da ich gerade für mein eigenes Azure Stack HCI Cluster, abgesichert über eine Eaton USV, solch ein Skript schreibe, sollt ihr auch daran teilhaben und vielleicht noch das ein oder andere lernen, wie ein Cluster die Ressourcen verwaltet und steuert.
Die verschiedenen Möglichkeiten
Wenn es zu einem Stromausfall kommt, und die Failover Cluster Knoten sollen kontrolliert heruntergefahren werden, gibt es mehrere Möglichkeiten. Je nach Art und Weise gibt es gewisse Vor- bzw. Nachteile, die ich hier gerne ein wenig näher erläutern möchte.
Na dann fahr halt einfach runter…
In den Standard-Einstellungen der meisten USV-Agenten ist es so, dass ein Server nach einer gewissen Zeit (120 Sekunden bei Eaton) heruntergefahren wird.
Grundsätzlich ja eine ganz nette Sache, wäre da nicht das Failover Cluster. In den Eigenschaften des Clusters gibt es einen Wert DrainOnShutdown.
Diese Einstellung bewirkt, dass ein Cluster-Knoten bei einem Shutdown versucht, die aktuell auf ihm ausgeführten VMs mittels Livemigration bzw. Schnellmigration auf andere Knoten zu verschieben, bevor der Server dann final heruntergefahren wird.
Im schlimmsten Fall führt diese Einstellung dazu, dass sich beide Knoten (oder alle Knoten bei mehr als zwei Systemen) gegenseitig die VMs zuschieben, obwohl sie sich gerade beide jeweils im Shutdown-Prozess befinden. Das verlängert die Zeit für das Herunterfahren künstlich und führt auch zu keinem brauchbaren Ergebnis. Daher sollte diese Art von Shutdown vermieden werden durch die USV.
Shutdown aller VMs
Damit die Knoten keine Livemigration machen, wäre ein Shutdown der virtuellen Systeme sinnvoll. Jetzt sollte man bei einem Failover Cluster aber darauf achten, dass man die VMs über das Failover Cluster herunterfährt, und nicht lokal.
Der Shutdown einer VM lokal wäre über ein
Stop-VM
möglich. Da aber die Rolle im Failover Cluster vorhanden ist, und die VM häufig auf einem beliebigen Knoten laufen kann, muss an dieser Stelle angesetzt werden. Hierbei ist es wichtig, dass man zum einen den korrekten Namen ausfindig macht (OwnerGroup statt Name) und zum anderen den Shutdown-Prozess nur für die VMs ausführt, die auch auf dem jeweiligen Knoten aktiv laufen.
$info = hostname
$VMs = Get-ClusterResource | ? ResourceType -eq "Virtual Machine" | select OwnerGroup, OwnerNode
Write-Host -ForegroundColor Green "VMs auf diesem Knoten, die heruntergefahren werden:"
Write-Host -ForegroundColor Green "###################################################"
foreach ($VM in $VMs) {
if ($VM.OwnerNode -eq $info) {
Write-Host $VM.OwnerGroup
Stop-VM -Name $VM.OwnerGroup -Force
}
}
Das kleine Skript liest in Zeile 1 den aktuellen Hostnamen aus. Danach werden alle Ressourcen im Cluster gespeichert, die eine Ressource “Virtual Machine” sind. Damit wir nicht alles speichern müssen, selektiere ich die Ausgabe auf OwnerGroup (Der Name der VM) und den aktuellen OwnerNode.
Nun werden alle Ressourcen in der Variable $VMs in einer Schleife abgearbeitet, und wenn sie auf dem lokalen Knoten laufen, werden Sie heruntergefahren. Damit ein Shutdown einer angemeldeten VM auch dann passiert, wenn noch aktiv Benutzer angemeldet sind (Terminal Server, geschlossene Sitzungen usw…), wird der Parameter -force mitgeliefert.
Shutdown des Failover Cluster
Wenn nun alle VMs ausgeschaltet bzw. heruntergefahren sind, können wir nun das Failover Cluster herunterfahren. Dies geschieht über den Befehl
Stop-Cluster
An dieser Stelle haben wir nun ein kleines Problem. Welcher Knoten fährt das Cluster herunter? Macht es jeder, kann es sein dass die anderen Systeme im Cluster noch nicht fertig sind mit dem Shutdown-Vorgang der VMs und das wir diesen Prozess dadurch aktiv stören. Um diesem Szenario vorzubeugen, gibt es mehrere Möglichkeiten.
Eine dieser Möglichkeiten könnte sein, dass immer der erste Knoten im Cluster den Shutdown-Prozess durchführt. Dies wäre aber nicht möglich, wenn sich der Knoten in Wartung befindet und keine Befehle im Cluster absetzen kann. Das ist also keine gute Lösung.
Ich habe mir an dieser Stelle die folgende Lösung überlegt: Ich prüfe, welcher Server im Cluster die Cluster-Ressource (IP, Name, …) betreibt. Da dies nur möglich ist, wenn der Knoten online und aktiv ist, muss dieser Server zwangsläufig in der Lage sein, das Cluster zu beenden.
Weiterhin prüfe ich den Status der VMs. Sind noch VMs online (z.B. weil der Shutdown-Vorgang auf dem Partner-System noch dauert), wartet der Server eine kurze Zeit und prüft den Zustand wieder. Dies wird so lange gemacht, bis alle VMs ausgeschaltet sind und das Cluster gefahrlos beendet werden kann.
$info = hostname
$ClusterOwnerNode = Get-ClusterResource | ? ResourceType -eq "IP Address" | select OwnerNode
if ($info -match $ClusterOwnerNode.OwnerNode)
{
Write-Host -ForegroundColor Green "Dieser Knoten ist der aktuelle Master und leitet den Shutdown-Prozess ein"
do
{
$VMs = Get-ClusterResource | ? ResourceType -eq "Virtual Machine"
if ($VMs.State -match "Online")
{
Write-Host -ForegroundColor Green "Mindestens eine VM noch online, warten"
sleep 5
}
}
until ($VMs.State -notmatch "Online")
Write-Host -ForegroundColor Green "Alles aus, Shutdown vom Cluster kann durchgeführt werden"
Stop-Cluster
}
Die andere Möglichkeit: VMs im Cluster automatisch herunterfahren
Ein Failover Cluster speichert normalerweise alle VMs, die es betreibt, wenn es beendet wird. Dies wird über die Eigenschaften der Cluster-VM-Ressource geregelt, die man sich per PowerShell anzeigen lassen kann. Wichtig ist hierbei, dass die Eigenschaft nicht direkt in der Ressource selbst angezeigt wird, sondern in den Cluster-Parametern. Diese werden nach der Filterung auf eine spezifische VM nochmal explizit abgefragt:
Get-ClusterResource | ? OwnerGroup -eq "VMName" | Get-ClusterParameter
Hier können wir den Wert OfflineAction sehen. Dieser steht in den Standard-Einstellungen auf 1. Gesetzt werden können die Werte 0 bis 3.
Wert | Einstellung |
0 | Die VM wird hart abgeschaltet |
1 | Die VM wird gespeichert |
2 | Die VM wird über das Gast-OS heruntergefahren |
3 | Die VM wird über das Gast-OS heruntergefahren inkl. Zwang |
Setzen wir diesen Wert nun auf 2 statt auf 1, werden alle VMs bei dem Stop des Clusters automatisch heruntergefahren. Wäre auch eine Möglichkeit, diese Anforderung hier zu lösen, ich verbleibe aber an dieser Stelle bei meinem Skript und benutze dies, um meine virtuellen Systeme planmäßig herunterzufahren.
Da diese Eigenschaft immer pro VM gesetzt wird, muss bei der Erstellung einer neuen VM auch darauf geachtet werden, dass die Eigenschaft gesetzt wird. Wird dies nicht gemacht, hat die VM im Cluster wieder die Eigenschaft 1 und wird gespeichert. Hier wäre z.B. ein geplanter Task hilfreich, der in regelmäßigen Abständen für alle VMs den gewünschten Wert per PowerShell setzt.
Das komplette Shutdown-Skript
Damit nun auf jedem Cluster Knoten ein einziges Skript ausgeführt wird, welches sämtliche Aufgaben bei einem Shutdown regelt, wird auf meinem beiden S2D Knoten das folgende Skript implementiert:
### Skript zum automatischen Shutdown eines Failover
### Clusters durch eine USV (In meinem Fall Eaton)
### 11.11.2020 - Helau :)
### Letztes Update - 12.08.2024
### Jan Kappen - j.kappen@building-networks.de
#
# Shutdown der VMs, die auf diesem Knoten betrieben werden
$info = hostname
$VMs = Get-ClusterResource | ? ResourceType -eq "Virtual Machine" | select OwnerGroup, OwnerNode
Write-Host -ForegroundColor Green "VMs auf diesem Knoten, die heruntergefahren werden:"
Write-Host -ForegroundColor Green "###################################################"
foreach ($VM in $VMs) {
if ($VM.OwnerNode -eq $info) {
Write-Host $VM.OwnerGroup
Stop-VM -Name $VM.OwnerGroup -Force
}
}
# Cluster herunterfahren auf dem aktuellen Besitzer der Cluster-IP
$ClusterOwnerNode = Get-ClusterResource | ? ResourceType -eq "IP Address" | select OwnerNode
if ($info -match $ClusterOwnerNode.OwnerNode)
{
Write-Host -ForegroundColor Green "Dieser Knoten ist der aktuelle Master und leitet den Shutdown-Prozess ein"
do
{
$VMs = Get-ClusterResource | ? ResourceType -eq "Virtual Machine"
if ($VMs.State -match "Online")
{
Write-Host -ForegroundColor Green "Mindestens eine VM noch online, warten auf"
sleep 5
}
}
until (($VMs.State | where {$_ -eq "Online"} | Measure-Object | select -ExpandProperty Count) -eq 0)
Write-Host -ForegroundColor Green "Alles aus, Shutdown vom Cluster kann durchgeführt werden"
sleep 30
Stop-Cluster -Confirm:$false -Force
}
# Lokalen Server herunterfahren
sleep 60
Stop-Computer -ComputerName localhost -Force
Nun muss das ganze noch in den Agenten der USV eingebunden werden. Da ich in meinem Fall nur ein Batch-Skript hinterlegen kann, rufe ich einfach innerhalb des Batch-Skripts eine PowerShell auf, die dann als Skript meine oben aufgeführte Datei ausführt.
Einbinden in den Eaton IPP Agenten
In den Eigenschaften des Eaton IPP setze ich nun eine .bat-Datei, die folgenden Inhalt hat:
C:\windows\system32\WindowsPowerShell\v1.0\Powershell.exe -executionpolicy remotesigned -File C:\script\eaton_shutdown_powershell.ps1
Damit wird dann mein Shutdown-Skript innerhalb der PowerShell aufgerufen, in dann den schon gezeigten Inhalt hat.
Testen, testen, testen!
Die schönsten Skripte bringen nichts, wenn sich irgendwo ein Fehler eingeschlichen hat und die durchgeführte Änderung nicht greift. Daher gilt: Wenn der USV-Agent installiert und konfiguriert ist, schnappt euch eine geplante Wartung und testet die Installation. Kappt die Stromverbindung der USV, stoppt die Zeit und beobachtet, ob alles wie geplant klappt oder ob es irgendwo einen Engpass gibt oder ob im schlimmsten Fall überhaupt nichts passieren würde.
Weiterhin sollte in regelmäßigen Abständen die Laufzeit der USV geprüft werden. Häufig werden solche Einrichtungen wie hier direkt bei Anschaffung der Geräte gemacht, wo noch nicht alles per USV abgesichert ist. Im Laufe der Zeit kommen immer mehr Geräte hinzu, die Laufzeit sinkt und die initial eingestellten Werte passen nicht mehr. Solche Dinge lassen sich auch gut monitoren, hier ein Beispiel aus meiner Infrastruktur:
Hallo Jan, gibt es eine Möglichkeit eine Abfrage zu starten welches prüft ob der Partnerknoten im Wartungsmodus ist. Möchte gerne ein Script erstellen welches prüft ob am zweiten Standort die USV auch keinen Strom mehr hat. DIeses soll dann nur denn Knoten an einem Standort abschalten und dabei die VMs auf den Partnerknoten umziehen. Danke
Hallo,
ja diese Möglichkeiten bestehen. Dazu muss das Skript erweitert werden, und es muss eine Abfrage eingebaut werden, bei der z.B. die USV direkt per SNMP abgefragt wird. Hier muss man dann den passenden Wert abfragen und prüfen (z.B. Spannung am Eingang der USV), und kann dann definieren, bei welchem Zustand dann die jeweilige Aktion ausgeführt wird. Das keine Sache von fünf Minuten, sollte aber grundsätzlich möglich sein.
Schönen Gruß
Jan
Hallo Jan, hab dein Script getestet. Beim abschalten des Cluster wartet er nur bis eine VM auf dem Partner System heruntergefahren ist und schaltet dan den Cluster ab auch wenn noch nicht alle VMs abgeschalten sind. 🙁 Da bei der Schleife ein offline reicht. Gruß Marko