Wprowadzenie
Jakiś czas temu postawiono przede mną zadanie opracowania klastra o wysokiej dostępności dla , działającego w kilku centrach danych połączonych światłowodem w obrębie jednego miasta, zdolnego przetrwać awarię (np. wyłączenie zasilania) jednego z centrów danych. Jako oprogramowanie odpowiedzialne za wysoką dostępność wybrałem , ponieważ to oficjalne rozwiązanie od RedHat do tworzenia klastrów o wysokiej dostępności. Jego zaletą jest wsparcie, jakie zapewnia RedHat, oraz to, że jest to rozwiązanie uniwersalne (modułowe). Dzięki temu można zapewnić wysoką dostępność nie tylko dla PostgreSQL, ale także dla innych usług, wykorzystując standardowe moduły lub tworząc je według specyficznych potrzeb.
Do tego rozwiązania pojawiło się zasadnicze pytanie: jak bardzo odporny będzie klaster o wysokiej dostępności? Aby to zbadać, opracowałem testowe środowisko, które imituje różne awarie w węzłach klastra, czeka na przywrócenie sprawności, naprawia uszkodzony węzeł i kontynuuje testowanie w cyklu. Początkowo projekt nosił nazwę hapgsql, ale z czasem znudziło mi się to imię z jedną samogłoską. Dlatego odpornym bazom danych (i przypisanym do nich float IP) zacząłem nadawać nazwę krogan (postać z gry komputerowej, w której wszystkie ważne organy są zduplikowane), natomiast węzły, klastry i sam projekt nazwano tuchanka (planeta, na której żyją kroganie).
Obecnie kierownictwo zatwierdziło . README wkrótce zostanie przetłumaczone na język angielski (ponieważ oczekuje się, że głównymi użytkownikami będą programiści Pacemaker i PostgreSQL), a stary rosyjski wariant README postanowiłem opracować (częściowo) w formie tego artykułu.

Klastry są uruchamiane na wirtualnych maszynach . Łącznie zostanie uruchomionych 12 wirtualnych maszyn (w sumie 36GiB), które utworzą 4 klastry o wysokiej dostępności (różne opcje). Dwa pierwsze klastry składają się z dwóch serwerów PostgreSQL, które są rozmieszczone w różnych centrach danych oraz wspólnego serwera witness c urządzenia quorum (umieszczonego na taniej wirtualnej maszynie w trzecim centrum danych), który rozstrzyga niepewność 50%/50%, oddając swój głos jednej ze stron. Trzeci klaster znajduje się w trzech centrach danych: jeden główny, dwa podrzędne, bez urządzenia quorum. Czwarty klaster składa się z czterech serwerów PostgreSQL, po dwa na centrum danych: jeden główny, pozostałe replikami, i również wykorzystuje witness c urządzenia quorum. Czwarty wytrzymuje awarię dwóch serwerów lub jednego centrum danych. To rozwiązanie może być w razie potrzeby skalowane na większą liczbę replik.
Usługa dokładnego czasu również został przystosowany do awaryjności, ale tam stosuje się metodę samego ntpd (orphan mode). Ogólny serwer witness pełni rolę centralnego serwera NTP, rozprowadzając swój czas wszystkim klastrom, tym samym synchronizując wszystkie serwery ze sobą. Jeśli witness wyjdzie z użytku lub zostanie odizolowany, wtedy swój czas zacznie rozprowadzać jeden z serwerów klastra (wewnątrz klastra). Pomocniczy pamięciowy HTTP proxy również został uruchomiony na witness, dzięki niemu pozostałe maszyny wirtualne mają dostęp do repozytoriów Yum. W rzeczywistości takie usługi, jak dokładny czas i proxy, z pewnością będą umieszczone na dedykowanych serwerach, a w teście są usytuowane na witness tylko dla oszczędności liczby maszyn wirtualnych i miejsca.
Wersje
v0. Działa z CentOS 7 i PostgreSQL 11 na VirtualBox 6.1.
Struktura klastrów
Wszystkie klastry są przeznaczone do umieszczania w kilku centrach danych, połączone w jedną płaską sieć i muszą wytrzymać awarię lub izolację sieci jednego centrum danych. Dlatego niemożliwe użycie w celu ochrony przed split-brain standardowej technologii Pacemaker, która nazywa się STONITH (Shoot The Other Node In The Head) albo fencing. Jej istota polega na tym, że jeśli węzły w klastrze zaczynają podejrzewać, że z którymś węzłem dzieje się coś złego, on nie odpowiada lub działa nieprawidłowo, to skutecznie go odłączają przez „zewnętrzne” urządzenia, takie jak kontroler IPMI lub UPS. Ale to zadziała tylko w przypadkach, gdy przy jednorazowej awarii serwera IPMI lub UPS nadal będą działać. Tutaj planowana jest ochrona przed znacznie bardziej katastrofalną awarią, gdy odmawia (na przykład zostaje odcięte zasilanie) całe centrum danych. A przy takiej awarii wszystkie stonith-urządzenia (IPMI, UPS itp.) również nie będą działać.
Zamiast tego podstawą systemu jest idea kworum. Wszystkie węzły mają głos i mogą pracować tylko te, które widzą więcej niż połowę wszystkich węzłów. Ta liczba „połowa+1” nazywa się kworum. Jeśli quorum nie zostanie osiągnięty, węzeł uznaje, że znajduje się w izolacji sieciowej i powinien wyłączyć swoje zasoby, tzn. jest to taka ochrona przed split-brain. Jeśli oprogramowanie odpowiedzialne za takie zachowanie nie działa, powinien zadziałać watchdog, na przykład oparty na IPMI.
Jeśli liczba węzłów jest parzysta (klaster w dwóch centrach danych), może wystąpić tak zwana niejednoznaczność 50%/50% (50-50), kiedy izolacja sieciowa dzieli klaster dokładnie na pół. Dlatego dla parzystej liczby węzłów dodaje się urządzenia quorum — mało wymagający demon, który może być uruchomiony na najtańszej wirtualce w trzecim centrum danych. Daje swój głos jednemu z segmentów (który widzi) i tym samym rozwiązuje niejednoznaczność 50%/50%. Serwer, na którym zostanie uruchomione urządzenie quorum, nazwałem witness (terminologia z repmgr, podoba mi się).
Zasoby mogą przemieszczać się z miejsca na miejsce, na przykład z uszkodzonych serwerów na sprawne, lub na polecenie administratorów. Aby klienci wiedzieli, gdzie znajdują się potrzebne im zasoby (gdzie się podłączyć?), używane są pływające IP (float IP). To są IP, które Pacemaker może przenosić pomiędzy węzłami (wszystko znajduje się w płaskiej sieci). Każdy z nich symbolizuje zasób (usługę) i będzie znajdować się tam, gdzie należy się podłączyć, aby uzyskać dostęp do tej usługi (w naszym przypadku do bazy danych).
Tuchanka1 (schemat ze sprężeniem)
Struktura

Pomysł polegał na tym, że mamy wiele małych baz danych o niskim obciążeniu, dla których nieopłacalne jest utrzymywanie wydzielonego serwera slave w trybie hot standby dla transakcji tylko do odczytu (brak potrzeby takiego marnotrawstwa zasobów).
W każdym centrum danych znajduje się jeden serwer. Na każdym serwerze są dwa instancje PostgreSQL (w terminologii PostgreSQL nazywane są one klastrami, ale dla uniknięcia zamieszania będę je nazywał instancjami, a klastry Pacemaker klastry). Jedna instancja działa w trybie master, i tylko ona świadczy usługi (tylko na nią prowadzi float IP). Druga instancja działa jako slave dla drugiego centrum danych i będzie świadczyć usługi tylko w przypadku awarii swojego mastera. Ponieważ większą część czasu usługi (wykonywanie zapytań) będzie świadczyć tylko jedna instancja z dwóch (master), wszystkie zasoby serwera są optymalizowane dla mastera (przydzielana jest pamięć na bufor cache shared_buffers itd.), ale tak, aby na drugą instancję także wystarczyło zasobów (nawet do pracy w trybie nieoptymalnym przez cache systemu plików) na wypadek awarii jednego z centrów danych. Slave nie świadczy usług (nie wykonuje zapytań typu read only) przy normalnej pracy klastra, aby nie doszło do walki o zasoby z masterem na tej samej maszynie.
W przypadku dwóch węzłów odporność na awarie jest możliwa tylko przy asynchronicznej replikacji, ponieważ przy synchronizowanej awaria slave'a spowoduje zatrzymanie mastera.
Awaria witness

Awaria witness (urządzenia quorum) rozważę tylko dla klastra Tuchanka1, w przypadku wszystkich pozostałych będzie ta sama historia. Przy awarii witness w strukturze klastra nic się nie zmieni, wszystko będzie działało tak, jak dotąd. Ale kworum stanie się równe 2 z 3, i dlatego każda kolejna awaria będzie fatalna dla klastra. I tak czy inaczej trzeba będzie szybko naprawić.
Awaria Tuchanka1

Awaria jednego z centrów danych dla Tuchanka1. W takim przypadku witness przekazuje swój głos drugiemu węzłowi w drugim centrum danych. Tam były slave przekształca się w mastera, w wyniku czego na jednym serwerze działają oba mastera i na nich wskazują obie ich float IP.
Tuchanka2 (klasyczny)
Struktura

Klasyczny schemat z dwóch węzłów. Na jednym działa master, na drugim slave. Oba mogą wykonywać zapytania (slave tylko read only), więc na obu wskazują float IP: krogan2 — na master, krogan2s1 — na slave. Odporność na awarie będzie zarówno dla mastera, jak i dla slave'a.
W przypadku dwóch węzłów odporność na awarie jest możliwa tylko przy asynchronicznej replikacji, ponieważ przy synchronizowanej awaria slave'a spowoduje zatrzymanie mastera.
Awaria Tuchanka2

Przy awarii jednego z centrów danych witness głosuje na drugi. Na jedynym działającym centrum danych zostanie uruchomiony master, a oba float IP: masterowy i roboczy będą do niego wskazywać. Oczywiście instancja musi być skonfigurowana w taki sposób, aby miała wystarczające zasoby (limity pod połączenia itp.), aby jednocześnie przyjmować wszystkie połączenia i żądania od masterowego i roboczego float IP. Oznacza to, że przy normalnej pracy powinna mieć wystarczający zapas limitów.
Tuchanka4 (wiele robotów)
Struktura

To już inny ekstremum. Są bazy danych, które otrzymują bardzo dużo żądań tylko do odczytu (typowy przypadek wysoko obciążonej strony internetowej). Tuchanka4 to sytuacja, w której może być trzech lub więcej robotów do obsługi takich żądań, ale nie za dużo. Przy bardzo dużej liczbie robotów będzie trzeba wymyślić hierarchiczny system replikacji. W minimalnym przypadku (na obrazku) w każdym z dwóch centrów danych znajdują się po dwa serwery, z których każdy ma jedną instancję PostgreSQL.
Kolejną cechą tego schematu jest to, że można już zorganizować jedną synchronizowaną replikację. Jest ona skonfigurowana tak, aby replikować, w miarę możliwości, do innego centrum danych, a nie do repliki w tym samym centrum danych, gdzie znajduje się master. Na mastera i na każdego robota wskazuje float IP. W zasadzie między robotami należałoby zrealizować równoważenie żądań. sql proxy, na przykład, po stronie klienta. Różnym typom klientów może być potrzebny różny typ sql proxy, i tylko programiści klientów wiedzą, kto czego potrzebuje. Ta funkcjonalność może być zrealizowana zarówno jako zewnętrzny demon, jak i biblioteka klienta (connection pool) itd. Wszystko to wykracza poza temat systemu baz danych odpornego na awarie (odporność SQL proxy może być realizowana niezależnie, razem z odpornością klienta).
Awaria Tuchanka4

W przypadku awarii jednego centrum danych (tzn. dwóch serwerów) witness głosuje na drugi. W rezultacie w drugim centrum danych działają dwa serwery: na jednym działa master, a na niego wskazuje masterowy float IP (do przyjmowania żądań read-write); a na drugim serwerze działa robot z synchronizowaną replikacją, a na niego wskazuje jeden z roboczych float IP (do żądań tylko do odczytu).
Pierwszą rzeczą, którą należy odnotować: roboczym float IP będzie tylko jeden, a aby poprawnie z nim działać, będzie trzeba, aby sql proxy przekierowywał wszystkie zapytania na jedyny pozostały adres float IP; a jeśli sql proxy nie, to można wymienić wszystkie adresy float IP niewolników oddzielając je przecinkiem w URL do połączenia. W takim przypadku z libpq połączenie będzie na pierwszym działającym IP, tak jest zrobione w systemie automatycznego testowania. Może się okazać, że w innych bibliotekach, na przykład JDBC, może to nie zadziałać i będzie potrzebne sql proxy. Zostało to zrobione, ponieważ dla float IP niewolników obowiązuje zakaz jednoczesnego uruchamiania na jednym serwerze, aby były równomiernie rozdzielane między serwery niewolników, jeśli działa ich kilka.
Drugie: nawet w przypadku awarii centrum danych synchronizowana replikacja będzie zachowana. A nawet jeśli nastąpi wtórna awaria, czyli w pozostałym centrum danych jeden z dwóch serwerów ulegnie awarii, klaster, mimo że przestanie świadczyć usługi, nadal zachowa informacje o wszystkich zakommitowanych transakcjach, dla których wydał potwierdzenie o commicie (nie będzie utraty informacji w przypadku wtórnej awarii).
Tuchanka3 (3 centra danych)
Struktura

To klaster na sytuację, gdy są trzy w pełni działające centra danych, w każdym z nich działa w pełni działający serwer bazy danych. W takim przypadku urządzenia quorum nie jest potrzebny. W jednym centrum danych działa master, w dwóch innych — niewolnicy. Replikacja jest synchronizowana, typu ANY (slave1, slave2), co oznacza, że klient otrzyma potwierdzenie commitu, gdy którykolwiek z niewolników jako pierwszy odpowie, że przyjął commit. Na zasoby wskazuje jeden float IP dla mastera i dwa dla niewolników. W przeciwieństwie do Tuchanka4 wszystkie trzy float IP są odporne na awarie. Do balansowania zapytań SQL w trybie tylko do odczytu można użyć sql proxy (z osobną odpornością na awarie), lub przydzielić jednej połowie klientów jeden niewolniczy float IP, a drugiej połowie — drugi.
Awaria Tuchanka3

W przypadku awarii jednego z centrów danych pozostają dwa. W jednym działa master i float IP od mastera, w drugim — niewolnik i oba niewolnicze float IP (na instancji powinien być podwójny zapas zasobów, aby przyjąć wszystkie połączenia z obu niewolniczych float IP). Między masterem a niewolnikiem jest synchronizowana replikacja. Ponadto klaster zachowa informacje o zakommitowanych i potwierdzonych transakcjach (nie będzie utraty informacji) w przypadku zniszczenia dwóch centrów danych (jeśli nie zostaną zniszczone jednocześnie).
Nie zamieszczałem szczegółowego opisu struktury plików i wdrażania. Kto chce się pobawić, może to wszystko przeczytać w README. Przedstawiam tylko opis automatycznego testowania.
System automatycznego testowania
Aby sprawdzić odporność klastrów przy symulacji różnych usterek, stworzono system automatycznego testowania. Uruchamiany jest za pomocą skryptu test/failure. Skrypt może przyjmować jako parametry numery klastrów, które chce się przetestować. Na przykład, to polecenie:
test/failure 2 3będzie testować tylko drugi i trzeci klaster. Jeśli parametry nie są podane, będą testowane wszystkie klastry. Wszystkie klastry są testowane równolegle, a wyniki wyświetlane są w panelu tmux. Tmux używa dedykowanego serwera tmux, więc skrypt można uruchamiać z domyślnego tmux, co daje zagnieżdżony tmux. Rekomenduję użycie terminala w dużym oknie i z małą czcionką. Przed rozpoczęciem testowania wszystkie wirtualne maszyny są przywracane do migawki w momencie zakończenia skryptu. setup.

Terminal jest podzielony na kolumny według liczby testowanych klastrów, domyślnie (na zrzucie ekranu) jest ich cztery. Zawartość kolumn opiszę na przykładzie Tuchanka2. Panel na zrzucie ekranu jest ponumerowany:
- Tutaj wyświetlane są statystyki testów. Kolumny:
- failure — nazwa testu (funkcji w skrypcie), który emuluje awarię.
- reaction — średni czas w sekundach, w jakim klaster przywrócił swoją funkcjonalność. Mierzony jest od początku działania skryptu, emulującego awarię, do momentu, gdy klaster przywraca swoją funkcjonalność i jest w stanie nadal świadczyć usługi. Jeśli czas jest bardzo krótki, na przykład sześć sekund (co zdarza się w klastrach z wieloma pracownikami (Tuchanka3 i Tuchanka4)), oznacza to, że awaria wystąpiła na asynchronicznym pracowniku i w żaden sposób nie wpłynęła na działanie, a zmiany stanu klastra nie miały miejsca.
- deviation — pokazuje rozrzut (dokładność) wartości reaction metodą „odchylenie standardowe”.
- count — ile razy test został wykonany.
- Krótki dziennik pozwala ocenić, czym zajmuje się klaster w danym momencie. Wyświetlany jest numer iteracji (testu), znacznik czasu oraz nazwa operacji. Zbyt długie wykonanie (> 5 minut) sygnalizuje jakiś problem.
- heart (serce) — bieżący czas. Dla wizualnej oceny wydajności mistrzowie do jego tabeli stale wpisywane jest bieżące czas za pomocą float IP mistrza. W przypadku sukcesu wynik zostaje wyświetlony w tym panelu.
- beat (puls) — „bieżący czas”, który wcześniej był zapisany przez skrypt heart do mistrza, teraz odczytywany jest z niewolnika za pomocą jego float IP. Pozwala wizualnie ocenić wydajność niewolnika i replikacji. W Tuchanka1 nie ma niewolników z float IP (brak niewolników świadczących usługi), ale są dwa instancje (Baza Danych), dlatego tutaj wyświetlane będzie nie beat, a heart drugiej instancji.
- Monitorowanie stanu klastra za pomocą narzędzia
pcs mon. Pokazuje strukturę, rozkład zasobów po węzłach oraz inne przydatne informacje. - Tutaj wyświetlane jest monitorowanie systemowe z każdej wirtualki klastra. Takich paneli może być więcej – tyle, ile wirtualek w klastrze. Dwa wykresy Obciążenie CPU (w wirtualkach po dwa procesory), nazwa wirtualki, Obciążenie systemu (nazywane jako Load Average, ponieważ jest uśrednione za 5, 10 i 15 minut), dane o procesach oraz rozkład pamięci.
- Śledzenie skryptu, wykonującego testy. W przypadku usterki — nagłego przerwania pracy lub nieskończonej pętli oczekiwania — można tutaj zobaczyć przyczynę takiego zachowania.
Testowanie odbywa się w dwóch etapach. Najpierw skrypt przechodzi przez wszystkie rodzaje testów, losowo wybierając wirtualkę, do której ten test zostanie zastosowany. Następnie wykonywana jest nieskończona pętla testowania, wirtualki i usterka są za każdym razem wybierane losowo. Nagle zakończenie skryptu testowania (dolny panel) lub nieskończona pętla oczekiwania na coś (> 5 minut czasu wykonania jednej operacji, co widać w śledzeniu) sugeruje, że któryś z testów w tym klastrze nie powiódł się.
Każdy test składa się z następujących operacji:
- Uruchomienie funkcji, emulującej usterkę.
- Gotowe? — oczekiwanie na przywrócenie funkcjonalności klastra (gdy świadczone są wszystkie usługi).
- Wyświetlane jest czas oczekiwania na przywrócenie klastra (reaction).
- Napraw — klaster „naprawia się”. Po czym powinien wrócić do pełnej funkcjonalności i gotowości na następną usterkę.
Oto lista testów z opisem, co one robią:
- ForkBomb: tworzy "Out of memory" za pomocą fork-bomby.
- OutOfSpace: zapełnia dysk twardy. Jednak test ma charakter raczej symboliczny przy tej znikomym obciążeniu, które powstaje podczas testowania, przy zapełnieniu dysku twardego, zwykle nie następuje awaria PostgreSQL.
- Postgres-KILL: zabija PostgreSQL poleceniem
killall -KILL postgres. - Postgres-STOP: zawiesza PostgreSQL poleceniem
killall -STOP postgres. - PowerOff: „wyłącza” wirtualną maszynę poleceniem
VBoxManage controlvm "wirtualna_maszyna" poweroff. - Reset: restartuje wirtualną maszynę poleceniem
VBoxManage controlvm "wirtualna_maszyna" reset. - SBD-STOP: zawiesza demon SBD poleceniem
killall -STOP sbd. - ShutDown: wysyła na wirtualną maszynę polecenie przez SSH
systemctl poweroff, system działa poprawnie. - UnLink: izolacja sieciowa, polecenie
VBoxManage controlvm "wirtualna_maszyna" setlinkstate1 off.
Zakończenie testu czy to za pomocą standardowego polecenia tmux "kill-window" Ctrl-b &, czy polecenia "detach-client" Ctrl-b d: w tym czasie testy są kończone, tmux się zamyka, wirtualne maszyny są wyłączane.
Zidentyfikowane problemy podczas testów
Na chwilę obecną demon watchdog sbd wykonuje zatrzymanie monitorowanych demonów, ale nie ich zawieszenie. I w konsekwencji, niepoprawnie są traktowane awarie prowadzące do zawieszenia tylko Corosync i Pacemaker, ale przy tym nie zawieszające sbd. Aby sprawdzić Corosync więc z pomocą tej kolekcji certyfikatów można zbudować łańcuch i uwierzytelnić witrynę internetową. , zaakceptowane w gałęzi master. Obiecali (w PR#83), że dla Pacemaker będzie coś podobnego, mam nadzieję, że do RedHat 8 to zrealizują. Ale takie „usterki” są teoretyczne, łatwo je odtworzyć sztucznie przy pomocy na przykład
killall -STOP corosync, ale nigdy nie występują w rzeczywistości.U Pacemaker w wersji dla CentOS 7 niepoprawnie ustawiony jest sync_timeout u urządzenia quorum, w rezultacie , na który powinien przejść master. To zostało naprawione przez zwiększenie sync_timeout u urządzenia quorum podczas wdrażania (w skrypcie
setup/setup1). Ta poprawka nie została zaakceptowana przez deweloperów Pacemaker, zamiast tego obiecali przekształcić infrastrukturę w taki sposób (w nieokreślonej przyszłości), aby ten timeout był obliczany automatycznie.Jeśli przy konfigurowaniu bazy danych wskazano, że w
LC_MESSAGES(wiadomości tekstowe) może być używany Unicode, na przykład,ru_RU.UTF-8, to przy uruchamianiu postgres w środowisku, gdzie locale nie jest UTF-8, na przykład, w pustym środowisku (tutaj pacemaker+pgsqlms(paf) uruchamia postgres), to . Deweloperzy PostgreSQL nadal nie doszli do zgody, co w takim przypadku zrobić. Można to obejść, należy ustawićLC_MESSAGES=en_US.UTF-8podczas konfigurowania (tworzenia) instancji bazy danych.Jeśli ustawiono wal_receiver_timeout (domyślnie 60s), to podczas testu PostgreSQL-STOP na głównym serwerze w klastrach tuchanka3 i tuchanka4 . Replikacja jest tam synchronizowana, więc zatrzymuje się nie tylko pracownik, ale także nowy główny serwer. Można to obejść, ustawiając wal_receiver_timeout=0 podczas konfiguracji PostgreSQL.
Rzadko zaobserwowano zawieszanie replikacji w PostgreSQL podczas testu ForkBomb (przepełnienie pamięci). . Spotkałem się z tym tylko w klastrach tuchanka3 i tuchanka4, gdzie z powodu synchronizowanej replikacji zawiesił się główny serwer. Problem ustępował sam, po jakimś dłuższym czasie (około dwóch godzin). Wymaga to dodatkowego badania, aby to naprawić. Objawy przypominają poprzedni błąd, który jest spowodowany inną przyczyną, ale ma identyczne konsekwencje.
Obraz krogan został wzięty z za zgodą autora:

Źródło: habr.com
