Agent Haproxy na przykładzie nodów Galera

Opisując loadbalancing Galery przez Haproxy wspomniałem, że jednym z filarów tego rozwiązania jest agent, który bada stan poszczególnych node klastra. Chciałbym pokazać dokładniej jak skonstruować takiego agenta dla Galery oraz jakie może być ogólne wykorzystanie tego rozwiązania w Haproxy.

Implementacja niektórych rozwiązań balansowania ruchem wymaga badania stanu zaplecza w oparciu o logikę bardziej skomplikowaną niż ocena kodu odpowiedzi czy też nagłówków pakietu odbieranego przez ladbalancer w ramach prostego healt check. Dotyczy to na przykład klastrów, gdzie powinniśmy znać stan node przed wysłaniem do niego ruchu czy też replikacji master – slave z dynamicznie wybieranym masterem, gdzie loadbalancer powinien na bieżąco odpytywać audytora o wybranego mastera.

Z pomocą w takich sytuacjach przychodzi agent-check Haproxy. Rozwiązanie to polega na sprawdzaniu stanu zaplecza nie tylko przez komunikację z samym zapleczem, ale także ze zdefiniowanym agentem, od którego loadbalancer także odbiera informację o stanie zaplecza.

Komunikacja z agentem odbywa się przez protokół TCP po wskazanym porcie. Haproxy odpytuje go i oczekuje jednego z predefiniowanych stringów ASCII. Założenie to bardzo ułatwia implementację, bowiem sposób przygotowania agenta i logia jego działania są nieistotne z punktu widzenia Haproxy, ważne aby spełniały wymogi wyjścia przekazywanego na port TCP 🙂

Bazowa komunikacja z agentem wygląda więc następująco:

[root@vps1 ~]# telnet 127.0.0.1 9988
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
100% UP 
Connection closed by foreign host.

Odpowiedź agenta jest interpretowana jako ciąg słów kluczowych oddzielonych jednym ze standardowych separatorów (spacją, znakiem tabulacji, przecinkiem). Mogą składać się na nią: słowo określającego stan zaplecza oraz wartość procentowa w przedziale liczb naturalnych, z wartością maksymalną 100%.

Do dyspozycji mamy kilka słów kluczowych reprezentujących stan maszyny stanów maszyny:

  • down, failed i stopped oznaczają dla loadbalancera to samo – niedziałające zaplecze, można ich jednak używać zamiennie dla czytelności komunikacji z człowiekiem i łatwiejszego określenia przyczyny zatrzymania serwera. Stan ten może być również opatrzony komentarzem zaczynającym się od #,
  • dysponujemy również dwoma stanami administracyjnymi serwerów: drain i maint. W obu przypadkach serwer nie będzie akceptował nowych połączeń oraz stan health check’ów nie będzie brany pod uwagę. Różnica pomiędzy wspomnianymi stanami jest taka, że serwer drain zachowa trwające połączenia do czasu ich zakończenia – jest to opcja przydatna w przypadku prowadzonych prac planowych, gdzie z wyprzedzeniem możemy odłączyć serwer od ruchu i poczekać na wygaśnięcie połączeń nie powodując jednocześnie nagłego ich zrywania, co mogłoby skutkować błędami po stronie klientów,
  • ready anluluje drain i maint, przywraca healt-checki, ale nie włącza ruchu,
  • up oznacza serwer jako gotowy to działania, zaplecze zostanie jednak włączone do ruchu dopiero w momencie, gdy inne healt-checki zostaną wykonane pozytywnie.

Na osobną wzmiankę zasługuje wartość procentowa. Kiedy się pojawi, zostanie przeliczona z z aktualną wagą serwera. Jeżeli więc mamy loadbalancing oparty o wagi możemy użyć tego mechanizmu do dynamicznej zmiany ilości połączeń do zaplecza (poprzez zmniejszenie jego wagi) np. w zależności od obciążenia CPU maszyny czy wysycenia łącza.

Jak widzimy Haproxy daje nam możliwość bardzo elastycznego budowania loadbalancigu w oparciu o agenty.

W przypadku Galery zastosowanie agenta staje się niezbędne aby określić stan node, w obrębie zaplecza. Natura klastra jest taka, że na poziomie komunikacji TCP serwer może przyjmować połączenia, jednak aplikacyjnie może nie być gotowy do przyjmowania lub serwowania danych. Stany te mogą zmieniać się dynamicznie w zależności od wielu czynników.

Agent dla Galery, którego stworzyłem napisany jest w Python 2.7. W oparciu o klasę Daemon (jej oryginalna implementacja jest już chyba niedostępna w sieci, ale przykład można znaleźć np. tu) przygotowałem skrypt, który działa samodzielnie jako daemon w środowisku Linux i możemy uruchomić go standardowo:

./xaphandeamon.py start
./xaphandeamon.py stop
./xaphandeamon.py restart

Skrypt sprawdza trzy podstawowe metryki, które pozwalają ocenić, czy nasz node jest w pełni operatywny.

Pierwszym testem jest sprawdzenie czy node ma połączenie z innymi elementami klastra:

SHOW GLOBAL STATUS LIKE 'wsrep_connected';

W przypadku otrzymania statusu ON, będącego wartością oczekiwaną sprawdzamy czy uuid node  jest takie samo jak uuid klastra:

SHOW STATUS LIKE 'wsrep_cluster_state_uuid'
SHOW STATUS LIKE 'wsrep_local_state_uuid'

Otrzymanie takich samych wartości upewnia nas, że node jest zsynchronizowany z klastrem.

Ostatnim etapem jest potwierdzenie, czy klaster jest gotowy do przyjmowania zapytań:

SHOW GLOBAL STATUS LIKE 'wsrep_ready';

Niepowodzenie któregokolwiek z powyższych testów sprawia, że agent zwraca do Haproxy informację, że aktualny stan zaplecza to DOWN.

Oczywiście logikę agenta można rozbudować o dodatkowe testy np. wersji konfiguracji klastra czy też stanu klastra jako całości:

SHOW GLOBAL STATUS LIKE 'wsrep_cluster_status';

To ostatnie pozwoli nam globalnie wyłączyć ruch do wszystkich node, jeżeli klaster przestanie być operacyjny.

Zastosowanie agenta zapewni nam wyłącznie rozsynchronizowanych node z ruchu jednocześnie dając im możliwość synchronizacji. Podnosi to bezpieczeństwo zarówno w kontekście stabilności klastra jak i aplikacji, które korzystają z mysql a nie potrafią wykonać rollback dla nieudanych transakcji.

Z pełną implementacją agenta możecie zapoznać się w serwisie GitHub – projekt nazywa się Xaphan Daemon 🙂 Zapraszam do pobierania i udoskonalania 😉

Podziel się:

Mikołaj Niedbała

I'm a Poland based IT administrator, linux administrator and IT engineer creating professional IT infrastructure solutions based on Linux and virtual environments.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *