Wprowadzenie do Failover Cluster Generic Script Resource

Dzisiaj chciałbym poświęcić trochę miejsca bardzo słabo udokumentowanej funkcjonalności Klastra.
Skrypty zasobów generycznych (Generic Script Resource) umożliwiają uruchomić w środowisku klastrowym nawet bardzo skomplikowane z punktu widzenia startu i potrzebnych zasobów usługi, które nie mają wbudowanych mechanizmów zapewniających wysoką dostępność.
W systemie Windows Failover Cluster umożliwia tworzenie skryptów zarządzających stanem zasobów JEDYNIE w języku Visual Basic (VBScript).
Ponieważ jest to technologia archaiczna, to powstały różne skrypty dostępne w sieci, służące do wywoływania innych narzędzi skryptowych. Ja posłużę się wersją uruchamiającą skrypty Powershell, gdyż jest to najlepszy obecnie dostępny język skryptowy dla systemu Windows.
Jednak, zanim dojdziemy do przedstawienia właściwych rozwiązań, chciałbym omówić filozofię tworzenia takich skryptów.
W środowisku klastrowym każdy zasób ma swojego właściciela (Owner Host). Jest host, na którym w danym momencie uruchamiany jest kod odpowiadający za obsługę zasobu. Celowo piszę „kod odpowiadający za obsługę”, gdyż może być to trojaki typ kodu:

  1. Manager Usług
  2. Biblioteka DLL zarządzająca zasobem (na przykład maszyny wirtualne)
  3. Skrypt zasobu generycznego

Po prawdzie, to implementacja zarówno managera usług jak i zasobu skryptu generycznego jest wewnętrznie realizowana przez odpowiednią DLL, ale rozróżnienie to jest przydatne dla administratorów i wdrożeniowców mających zdecydować, w jaki sposób należy zapewnić wysoką dostępność.

Cykl życia zasobu można opisać pewnymi etapami, które są charakterystyczne dla każdego właściciela. Należą do nich:

  1. Inicjalizacja managera zasobu (Open)
  2. Uruchomienie zasobu (Online)
  3. „Obejrzenie stanu zasobu” (LooksAlive)
  4. Sprawdzenie stanu zasobu (IsAlive)
  5. Wyłączenie zasobu (Offline)
  6. Zniszczenie zasobu (Terminate)
  7. Zamknięcie managera zasobu (Close)

Nazwy funkcji Callback implementowane w skrypcie zasobu Podane zostały w nawiasach.
Istotne jest wyjaśnienie różnicy między LooksAlive a IsAlive, gdyż na pierwszy rzut oka wydaje się to być dość nietypowe rozwiązanie. Callback LooksAlive jest wywoływany często, żeby zmniejszyć możliwie najbardziej czas reakcji na ewentualną awarię. Czas wykonania tego callback’a jest ograniczony do 300ms.
Dopiero, gdy ta funkcja zwróci błąd wywoływana jest funkcja IsAlive.
Ponadto raz na około minutę wywoływana jest funkcja IsAlive niezależnie od opisanego powyżej mechanizmu. Wynika to z pryncypium szybkiego wykonania LooksAlive, a w konsekwencji pobieżnego sprawdzenia, które może coś (celowo, lub nie) przeoczyć.
Dlatego manager klastra i tak sprawdza pełną diagnostykę zasobu raz na minutę. Jest to na tyle nieduży czas, że jeszcze akceptowalny dla większości usług, a z drugiej strony jest on na tyle duży, że nie wprowadza znacznego obciążenia do systemu „władającego” usługą.
Pozostałe funkcje callback ze skryptu nie wymagają dłuższego omówienia.

Bardzo ważną sprawą do omówienia, gdyż z tym się mogą wiązać błędu i w konsekwencji problemy z uruchomieniem, jest kwestia zmiennych zasobu. Każdy zasób może (i powinien) przechowywać swój stan konfiguracji w przestrzeni klastra – dzięki temu unikniemy sytuacji, w której usługa będzie zachowywać się niespójnie na różnych hostach-właścicielach zasobu.
Istotnym jest, aby definiować zmienne zasobu na samym zasobie skryptu, a nie na grupie zasobów, czy też na zasobie adresu ip albo jakimkolwiek innym zasobie w grupie. Tylko zmienne zasobu skryptu (czyli zdefiniowane dla instancji typu Generic Script Resource) będą dostępne przez Api Klastra.

API klastra reprezentowane jest przez obiekt VBScript o nazwie

1
Resource

Przykład użycia tego obiektu prezentuje poniższa funkcja:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Function CheckProperties(ActionName, ErrorBaseCode)
    If Resource.PropertyExists("DebugVBA") = False Or Resource.PropertyExists("DebugPS") = False Then
        WshShell.LogEvent 1, "No Debugging Settings! Exiting."
        CheckProperties = ErrorBaseCode+1
        Exit Function
    End If
    LogEvent 4, "Enter " & ActionName & "()"
    If Resource.PropertyExists("PSScriptsPath") = False Then
        LogEvent 1, "Error " & ActionName & "(). No PSScriptsPath private property. Exiting."
        CheckProperties = ErrorBaseCode+2
        Exit Function
    End If
    If Resource.PropertyExists("QueueIndex") = False Then
        WshShell.LogEvent 1, ActionName & "(): No Cluster Queue Definition! Define QueueIndex private property. Exiting."
        CheckProperties = ErrorBaseCode+3
        Exit Function
    End If
    CheckProperties = 0
End Function