
Głównym celem Patroni jest zapewnienie wysokiej dostępności dla PostgreSQL. Jednak Patroni to tylko szablon, a nie gotowe narzędzie (co zresztą zostało zawarte w dokumentacji). Na pierwszy rzut oka, konfigurując Patroni w laboratorium testowym, można zobaczyć, jak wspaniałym narzędziem jest i jak łatwo radzi sobie z naszymi próbami zrujnowania klastra. Jednak w praktyce w środowisku produkcyjnym nie zawsze wszystko przebiega tak pięknie i elegancko, jak w laboratorium testowym.

Opowiem trochę o sobie. Zaczynałem jako administrator systemów. Pracowałem w web development. Od 2014 roku pracuję w Data Egret. Firma zajmuje się doradztwem w dziedzinie Postgres. Obsługujemy tylko Postgres i codziennie pracujemy z Postgres, dlatego mamy różne doświadczenia związane z eksploatacją.
Pod koniec 2018 roku zaczęliśmy stopniowo używać Patroni. Zgromadziliśmy pewne doświadczenie. Jakoś go diagnozowaliśmy, dostosowywaliśmy, doszliśmy do swoich najlepszych praktyk. O tym będę mówił w tym referacie.
Oprócz Postgresa lubię Linuxa. Lubię go eksplorować i badać, lubię kompilować jądra. Interesują mnie także wirtualizacja, kontenery, Docker, Kubernetes. To wszystko mnie fascynuje, ponieważ nawiązuję do dawnych nawyków administracyjnych. Lubię zajmować się monitorowaniem. I lubię rzeczy związane z administracją Postgresa, czyli replikację, kopie zapasowe. A w wolnym czasie piszę w Go. Nie jestem inżynierem oprogramowania, piszę dla siebie w Go. Sprawia mi to przyjemność.

- Myślę, że wielu z was wie, że w Postgres nie ma HA (Wysokiej Dostępności) od razu. Aby uzyskać HA, trzeba coś zainstalować, skonfigurować, włożyć wysiłek, aby to uzyskać.
- Istnieje kilka narzędzi, a Patroni to jedno z nich, które rozwiązują HA w dość imponujący i efektywny sposób. Jednak po zainstalowaniu go w laboratorium testowym i uruchomieniu, możemy sprawdzić, że to wszystko działa, możemy odtwarzać pewne problemy, zobaczyć, jak Patroni je obsługuje. I przekonamy się, że to wszystko działa całkiem dobrze.
- Jednak w praktyce spotykaliśmy się z różnymi problemami. O nich będę mówił.
- Opowiem, jak to diagnozowaliśmy, co zmienialiśmy – czy to nam pomogło, czy nie.

- Nie będę opowiadać, jak zainstalować Patroni, ponieważ można to znaleźć w Internecie, można też spojrzeć na pliki konfiguracyjne, aby zrozumieć, jak to wszystko działa i jak jest konfigurowane. Można zgłębić schematy, architektury, znajdując o tym informacje w Internecie.
- Nie zamierzam mówić o cudzych doświadczeniach. Będę mówić tylko o tych problemach, z którymi sami się spotkaliśmy.
- I nie zamierzam omawiać problemów, które są poza Patroni i PostgreSQL. Na przykład, problemy związane z load balancingiem, kiedy nasz klaster się rozpadł, nie będę o tym mówić.

I mały disclaimer przed rozpoczęciem naszej prezentacji.
Wszystkie te problemy, z którymi się zmierzyliśmy, miały miejsce w ciągu pierwszych 6-7-8 miesięcy eksploatacji. Z czasem doszliśmy do naszych wewnętrznych najlepszych praktyk. I problemy zniknęły. Dlatego prezentacja była zgłaszana około pół roku temu, kiedy to wszystko było świeże w mojej głowie i dobrze to pamiętałem.
Podczas przygotowań do prezentacji przeglądałem stare postmortemy, sprawdzałem logi. I część szczegółów mogła mi umknąć, albo pewnych detali mogło nie być wystarczająco zbadanych podczas analizy problemów, dlatego w niektórych momentach może się wydawać, że problemy zostały omówione nie w pełni, albo że brakuje pewnych informacji. Proszę o wybaczenie za ten moment.

Czym jest Patroni?
- To szablon do budowy HA. Tak pisze dokumentacja. Z mojego punktu widzenia, to bardzo trafne określenie. Patroni to nie srebrna kula, która rozwiąże wszystkie twoje problemy, tzn. trzeba włożyć wysiłek, aby zaczęło działać i przynosić korzyści.
- To serwis agenta, który jest instalowany na każdej usłudze z bazą danych i który jest rodzajem systemu init dla twojego Postgresa. Uruchamia Postgresa, zatrzymuje go, restartuje, zmienia konfigurację i topologię twojego klastra.
- W związku z tym, aby przechowywać stan klastra, jego bieżące przedstawienie, jak wygląda, potrzebne jest jakieś repozytorium. I w tym kontekście Patroni poszedł drogą przetrzymywania stanu w zewnętrznym systemie. To system rozproszonego repozytorium konfiguracji. Mogą to być Etcd, Consul, ZooKeeper, lub Etcd w Kubernetesie, tj. jedna z tych opcji.
- Jedną z cech Patroni jest to, że auto-failover otrzymujemy od razu, wystarczy go skonfigurować. Jeśli weźmiemy Repmgr jako porównanie, to failover jest w zestawie. Z Repmgr mamy switchover, ale jeśli chcemy auto-failover, musimy go dodatkowo skonfigurować. W Patroni auto-failover jest dostępne od razu.
- Jest wiele innych rzeczy. Na przykład zarządzanie konfiguracjami, dodawanie nowych replik, kopie zapasowe itp. Ale to wykracza poza prezentację, o tym nie będę mówić.

Mały podsumowanie – głównym zadaniem Patroni jest dobrze i niezawodnie realizować auto-failover, aby nasz klaster pozostawał sprawny, a aplikacja nie zauważała zmian w topologii klastra.

Ale kiedy zaczynamy używać Patroni, nasz system staje się nieco bardziej skomplikowany. Jeśli wcześniej mieliśmy Postgres, to przy użyciu Patroni otrzymujemy samego Patroni, otrzymujemy DCS, gdzie przechowywany jest stan. I wszystko to musi jakoś działać. Dlatego, co może się zepsuć?
Może się zepsuć:
- Może się zepsuć Postgres. Może to być master lub replika, jedno z nich może przestać działać.
- Może się zepsuć sam Patroni.
- Może się zepsuć DCS, gdzie przechowywany jest stan.
- I może się zepsuć sieć.
Wszystkie te kwestie omówię w prezentacji.

Będę rozważał przypadki w miarę ich skomplikowania, nie z punktu widzenia, że przypadek obejmuje wiele komponentów, ale z punktu widzenia subiektywnych odczuć, że ten przypadek był dla mnie trudny, trudno było go analizować… a odwrotnie, jakiś przypadek był łatwy i łatwo go było rozwiązać.

Pierwszy przypadek jest najprostszy. To ten przypadek, gdy wzięliśmy klaster baz danych i na tym samym klastrze uruchomiliśmy nasze przechowywanie DCS. To najczęstszy błąd. To błąd projektowania architektur, tzn. łączenie różnych komponentów w jednym miejscu.
I tak, wystąpił failover, idziemy sprawdzić, co się stało.

Interesuje nas moment, w którym nastąpił failover. Tzn. interesuje nas ten moment w czasie, kiedy nastąpiła zmiana stanu klastra.
Jednak failover nie zawsze jest jednoczesny, tzn. nie zajmuje jednej jednostki czasu, może trwać. Może to być proces długotrwały.
Dlatego ma on czas rozpoczęcia i czas zakończenia, to znaczy, że jest to zdarzenie długoterminowe. Dzielimy wszystkie zdarzenia na trzy interwały: mamy czas przed awarią, w trakcie awarii i po awarii. To znaczy, że rozważamy wszystkie zdarzenia w tej osi czasu.

I pierwszą rzeczą, gdy nastąpiła awaria, jest to, że szukamy przyczyny, co się stało, co było powodem, który doprowadził do awarii.
Jeśli spojrzymy na logi, to będą to klasyczne logi Patroni. Informują nas one, że serwer stał się mistrzem, a rola mistrza przeszła na ten węzeł. Tutaj jest to podświetlone.

Następnie musimy zrozumieć, dlaczego wystąpiła awaria, tzn. jakie wydarzenia doprowadziły do przeniesienia roli mistrza z jednego węzła na inny. W tym przypadku wszystko jest proste. Mamy błąd w interakcji z systemem przechowywania. Mistrz zrozumiał, że nie może pracować z DCS, tzn. wystąpił jakiś problem z interakcją. I mówi, że nie może być już dłużej mistrzem i zrzuca z siebie uprawnienia. Ta linia „demoted self” mówi o tym.

Jeśli spojrzymy na zdarzenia, które poprzedziły awarię, to możemy dostrzec te same przyczyny, które były problemem dla dalszej pracy mistrza.
Jeśli przejrzymy logi Patroni, to zobaczymy, że mamy masę różnych błędów, czasów oczekiwania, tzn. agent Patroni nie może pracować z DCS. W tym przypadku to agent Consul, z którym komunikacja odbywa się przez port 8500.
I problem polega na tym, że Patroni i baza danych są uruchomione na jednym hoście. Na tym samym węźle były uruchomione serwery Consul. Tworząc obciążenie na serwerze, stworzyliśmy problemy także dla serwerów Consul. Nie mogły one normalnie komunikować się.

Po pewnym czasie, gdy obciążenie spadło, nasz Patroni mógł ponownie komunikować się z agentami. Normalna praca została wznowiona. A ten sam serwer Pgdb-2 znowu stał się mistrzem. To znaczy, że nastąpił mały flip, przez który węzeł zrzucił z siebie uprawnienia mistrza, a potem znowu je przejął, to znaczy, wszystko wróciło do normy.

I to można uznać za fałszywe wyzwolenie, lub można ocenić, że Patroni postąpił właściwie. To znaczy, że zrozumiał, iż nie może utrzymać stanu klastra i zrzucił z siebie uprawnienia.
Problem pojawił się, ponieważ serwery Consul znajdują się na tym samym sprzęcie, co bazy danych. W związku z tym obciążenie, czy to obciążenie dysków, czy procesorów, wpływa również na interakcję z klastrem Consul.

Postanowiliśmy, że to nie powinno wszystko działać razem, więc wydzieliliśmy osobny klaster dla Consul. Patroni pracował już z osobnym Consul, tj. był osobny klaster PostgreSQL, osobny klaster Consul. To podstawowa instrukcja, jak należy rozdzielać te elementy i utrzymywać je, aby nie działały razem.
Alternatywnie można dostosować parametry ttl, loop_wait, retry_timeout, tj. spróbować przetrwać te krótkotrwałe szczyty obciążenia poprzez ich zwiększenie. Ale to nie jest najbardziej odpowiednia opcja, ponieważ obciążenie może być długotrwałe. Wówczas po prostu przekroczymy limity tych parametrów. To może nie do końca pomóc.

Pierwszy problem, jak się domyśliliście, jest prosty. Wzięliśmy i umieściliśmy DCS razem z bazą, co spowodowało problem.

Drugi problem jest podobny do pierwszego. Podobnie jak w pierwszym przypadku mamy znowu problemy z interakcją z systemem DCS.

Jeśli spojrzymy na logi, zobaczymy, że znów wystąpił błąd komunikacji. Patroni informuje, że nie może współpracować z DCS, więc aktualny mistrz przechodzi w tryb repliki.
Stary mistrz staje się repliką, tutaj Patroni działa jak powinien. Uruchamia pg_rewind, aby przeprowadzić rewizję dziennika transakcji, a potem podłącza się do nowego mistrza i goni nowego mistrza. W tym przypadku Patroni działa jak należy.

Musimy tutaj znaleźć miejsce, które poprzedzało awarię, tj. błędy, które były przyczyną wystąpienia awarii. W tym kontekście logi Patroni są dość wygodne w pracy. Z określonymi interwałami zapisuje te same komunikaty. Jeśli zaczniemy szybko przewijać te logi, to zobaczymy, że logi się zmieniły, co oznacza, że pojawiły się jakieś problemy. Szybko wracamy do tego miejsca i sprawdzamy, co się dzieje.
W normalnej sytuacji logi wyglądają mniej więcej tak. Sprawdzany jest właściciel blokady. I jeśli właściciel, na przykład, się zmienił, mogą wystąpić jakieś zdarzenia, na które Patroni powinien zareagować. Ale w tym przypadku wszystko jest w porządku. Szukamy miejsca, w którym zaczęły się błędy.

Przewijając do momentu, w którym zaczęły się pojawiać błędy, widzimy, że wystąpił autofailover. Biorąc pod uwagę, że nasze błędy były związane z interakcją z DCS, a w naszym przypadku używaliśmy Consul, przyglądamy się także logom Consul, aby zobaczyć, co się tam działo.
Około porównując czas failovera z czasem w logach Consul, zauważamy, że sąsiedzi w klastrze Consul zaczęli wątpić w istnienie innych uczestników klastra Consul.

Jeśli spojrzymy również na logi innych agentów Consul, to tam także widać, że występuje jakiś problem z siecią. Wszyscy uczestnicy klastra Consul wątpią w istnienie siebie nawzajem. To zadziałało jako impuls do failovera.
Patrząc na to, co działo się przed tymi błędami, można zauważyć różne problemy, takie jak deadline, RPC failed, co oznacza, że wyraźnie występuje jakiś problem w interakcji uczestników klastra Consul.

Najprostszą odpowiedzią jest naprawa sieci. Ale mi, stojąc na mównicy, łatwo to mówić. Jednak okoliczności są takie, że nie zawsze klient może sobie pozwolić na naprawę sieci. Może mieszkać w DC i nie mieć możliwości naprawy sieci ani wpływu na sprzęt. Dlatego potrzebne są inne opcje.

Opcje są:
- Najprostszą opcją, która moim zdaniem jest opisana nawet w dokumentacji, jest wyłączenie sprawdzania przez Consul, tzn. przekazanie pustej tablicy. Mówimy agentowi Consul, aby nie korzystał z żadnych sprawdzeń. Dzięki tym sprawdzeniom możemy zignorować te burze sieciowe i nie inicjować failovera.
- Inna opcja to ponowne sprawdzenie raft_multiplier. To parametr samego serwera Consul. Domyślnie ustawiony jest na wartość 5. Ta wartość jest zalecana w dokumentacji dla środowisk staging. Zasadniczo wpływa to na częstotliwość wymiany komunikatów między uczestnikami sieci Consul. W praktyce, ten parametr wpływa na szybkość komunikacji między uczestnikami klastra Consul. Dla środowiska produkcyjnego zaleca się jego zmniejszenie, aby węzły częściej wymieniały komunikaty.
- Innym rozwiązaniem, które zaczęliśmy stosować, jest zwiększenie priorytetu procesów Consul wśród innych procesów dla planisty procesów systemu operacyjnego. Istnieje taki parametr „nice”, który właśnie definiuje priorytet procesów uwzględniany przez planistę OS podczas planowania. Zmniejszyliśmy wartość nice dla agentów Consul, czyli podnieśliśmy priorytet, aby system operacyjny przydzielał procesom Consul więcej czasu na pracę i wykonanie swojego kodu. W naszym przypadku to rozwiązało nasz problem.
- Inną opcją jest rezygnacja z używania Consul. Mam znajomego, który jest zwolennikiem Etcd. Regularnie sprzeczamy się, co jest lepsze – Etcd czy Consul. W kwestii tego, co jest lepsze, zazwyczaj zgadzamy się, że Consul ma agenta, który musi być uruchomiony na każdym węźle z bazą danych. To znaczy, interakcja Patroni z klastrem Consul odbywa się poprzez ten agent. I ten agent staje się wąskim gardłem. Jeśli coś się dzieje z agentem, t wtedy Patroni nie może już komunikować się z klastrem Consul. I to stanowi problem. W przypadku Etcd nie ma żadnego agenta. Patroni może bezpośrednio współpracować z listą serwerów Etcd i komunikować się z nimi. W tym kontekście, jeśli w twojej firmie używasz Etcd, to prawdopodobnie będzie to lepszy wybór niż Consul. Jednak my zawsze jesteśmy ograniczeni tym, co klient wybrał i czego używa. A w przypadku naszych klientów, w większości przypadków, wszyscy korzystają z Consul.
- Ostatni punkt to przemyślenie wartości parametrów. Możemy zwiększyć te parametry w górę, mając nadzieję, że nasze krótkoterminowe problemy z siecią będą krótkie i nie przekroczą przedziału tych parametrów. W ten sposób możemy zmniejszyć agresywność Patroni w realizacji auto-failover, jeśli wystąpią jakieś problemy z siecią.

Myślę, że wielu, którzy używają Patroni, zna tę komendę.

Ta komenda pokazuje bieżący stan klastra. Na pierwszy rzut oka ten obraz może wydawać się normalny. Mamy główny węzeł, mamy replikę, brak opóźnienia replikacji. Ale ten obraz jest normalny tylko do momentu, gdy nie wiemy, że w tym klastrze powinny być trzy węzły, a nie dwa.

Zdarza się, że występuje auto-failover. Po tym auto-failoverze nasza replika zniknęła. Musimy ustalić, dlaczego zniknęła i przywrócić ją z powrotem. Ponownie przeszukujemy logi i sprawdzamy, dlaczego wystąpił auto-failover.

W tym przypadku druga replika stała się masterem. Wszystko jest w porządku.

Musimy przeanalizować replikę, która odpadła i nie jest w klastrze. Otwieramy logi Patroni i sprawdzamy, że podczas próby połączenia z klastrem wystąpił problem na etapie pg_rewind. Aby połączyć się z klastrem, należy cofnąć dziennik transakcji, zażądać odpowiedniego dziennika transakcji od mastera i nadrobić go do mastera.
W tym przypadku nie mamy dziennika transakcji, więc replika nie może się uruchomić. W związku z tym zatrzymujemy Postgresa z błędem. Dlatego nie ma jej w klastrze.

Musimy zrozumieć, dlaczego nie ma jej w klastrze i dlaczego nie ma logów. Idziemy do nowego mastera i sprawdzamy jego logi. Okazuje się, że podczas pg_rewind wystąpił checkpoint. Niektóre stare dzienniki transakcji zostały po prostu przemianowane. Gdy stary master próbował połączyć się z nowym masterem i zażądać tych logów, one już były przemianowane i po prostu ich nie było.

Porównywałem znaczniki czasu, kiedy miały miejsce te zdarzenia. Różnica wynosiła zaledwie 150 milisekund, a więc checkpoint zakończył się w 369 milisekund. WAL-segmenty zostały przemianowane, a następnie po 517 milisekundach uruchomiono rewind na starej replikacji. Czyli 150 milisekund wystarczyło, aby replika nie mogła się połączyć i pracować.

Jakie są opcje?
Początkowo korzystaliśmy ze slotów replikacji. Uważaliśmy, że to dobre rozwiązanie. Chociaż na początku użytkowania wyłączyliśmy sloty. Myśleliśmy, że jeśli sloty będą gromadzić zbyt wiele WAL-segmentów, możemy zawalić mastera. W końcu zmusiliśmy się do przetrwania jakiś czas bez slotów, ale zrozumieliśmy, że sloty są nam potrzebne, więc je przywróciliśmy.
Ale jest problem, że gdy master przechodzi w replikę, usuwa sloty i razem ze slotami usuwa segmenty WAL. Aby wyeliminować ten problem, postanowiliśmy zwiększyć parametr wal_keep_segments. Domyślnie wynosi 8 segmentów. Przenieśliśmy go na 1000 i zobaczyliśmy, ile mamy wolnego miejsca. I przekazaliśmy 16 gigabajtów na wal_keep_segments. To znaczy, że podczas przełączania zawsze mamy na wszystkich węzłach zapas 16 gigabajtów dzienników transakcji.
I dodatkowo – ma to znaczenie dla długotrwałych zadań konserwacyjnych. Załóżmy, że musimy zaktualizować jeden z replik. Chcemy go wyłączyć. Musimy zaktualizować oprogramowanie, być może system operacyjny, coś jeszcze. I gdy wyłączamy replikę, ten slot również jest usuwany. A jeśli używamy małego wal_keep_segments, to w przypadku długotrwałej nieobecności repliki, dzienniki transakcji zostaną odtworzone. Wzniesiemy replikę, ona zażąda tych dzienników transakcji, na których się zatrzymała, ale na masterze może ich nie być. I replikacja również nie będzie mogła się podłączyć. Dlatego trzymamy duży zapas dzienników.


Mamy bazę produkcyjną. Tam już działają projekty.
Wystąpił failover. Sprawdziliśmy i wszystko jest w porządku, replikacje są na miejscu, nie ma opóźnienia replikacji. Również nie ma błędów w dziennikach, wszystko w porządku.
Zespół produktowy mówi, że podobno powinny być jakieś dane, ale widzimy je w jednym źródle, a w bazie ich nie widzimy. I trzeba zrozumieć, co się z nimi stało.

Jasne, że pg_rewind je nadpisał. Od razu to zrozumieliśmy, ale poszliśmy sprawdzić, co się działo.

W logach zawsze możemy znaleźć, kiedy wystąpił failover, kto został masterem i możemy określić, kto był starym masterem i kiedy chciał stać się repliką, to znaczy potrzebujemy tych logów, aby ustalić, jaki był utracony wolumen dzienników transakcji.
Nasz stary master się zrestartował. I w autostart został zapisany Patroni. Patroni się uruchomił. Następnie uruchomił Postgresa. Dokładniej przed uruchomieniem Postgresa i zanim stał się repliką, Patroni uruchomił proces pg_rewind. Odpowiednio nadpisał część dzienników transakcji, pobrał nowe i połączył się. Tutaj Patroni zadziałał doskonale, to znaczy, jak powinien. Nasz klaster został przywrócony. Mieliśmy 3 węzły, po failoverze 3 węzły – wszystko jest świetnie.

Straciliśmy część danych i musimy zrozumieć, ile ich utraciliśmy. Szukamy dokładnego momentu, w którym mieliśmy rewind. Możemy to znaleźć w zapisach w dzienniku. Rewind został uruchomiony, coś tam zrobił i zakończył.

Musimy znaleźć pozycję w dzienniku transakcji, w której zatrzymał się stary master. W tym przypadku jest to ta oto zaznaczenie. Potrzebujemy drugiego zaznaczenia, czyli odległości, na jaką różni się stary master od nowego.
Bierzemy zwykły pg_wal_lsn_diff i porównujemy te dwa zaznaczenia. W tym przypadku otrzymujemy 17 megabajtów. Czy to dużo, czy mało, każdy decyduje sam. Ponieważ dla kogoś 17 megabajtów to niewiele, dla kogoś innego to dużo i niedopuszczalne. To już każdy indywidualnie określa w zależności od potrzeb biznesowych.

Ale co ustaliliśmy dla siebie?
Po pierwsze, musimy zdecydować, czy autostart Patroni po restarcie systemu jest nam zawsze potrzebny. Często zdarza się, że musimy wejść na stary master, zobaczyć, jak daleko odszedł. Możliwe, że sprawdzić segmenty dziennika transakcji, zobaczyć, co tam jest. I zrozumieć, czy możemy te dane utracić, czy musimy uruchomić stary master w trybie standalone, aby wyciągnąć te dane.
Dopiero po tym powinniśmy podjąć decyzję, czy możemy te dane odrzucić, czy możemy je odzyskać, podłączyć ten węzeł jako replika do naszego klastra.
Oprócz tego istnieje parametr „maximum_lag_on_failover”. Domyślnie, jeśli dobrze pamiętam, ten parametr ma wartość 1 megabajt.
Jak to działa? Jeśli nasza replika spóźnia się o 1 megabajt danych w lagach replikacyjnych, to ta replika nie bierze udziału w wyborach. A jeśli nagle dojdzie do failoveru, Patroni sprawdza, które repliki się spóźniają. Jeśli spóźniają się o dużą ilość dzienników transakcji, nie mogą stać się masterem. To bardzo dobra funkcja ochronna, która pozwala nie utracić wielu danych.
Istnieje jednak problem, ponieważ lag replikacji w klastrze Patroni i DCS jest aktualizowany w określonych odstępach czasu. Według mnie domyślna wartość ttl wynosi 30 sekund.
Może zdarzyć się sytuacja, w której opóźnienie replikacji dla replik w DCS jest jedno, a w rzeczywistości może być zupełnie inne opóźnienie lub wręcz nie ma żadnego opóźnienia, tzn. to rozwiązanie nie jest w czasie rzeczywistym. I nie zawsze odzwierciedla rzeczywistą sytuację. Nie warto na tym opierać skomplikowanej logiki.
Ryzyko utraty danych zawsze istnieje. W najgorszym przypadku jedna formuła, a w przeciętnym przypadku inna formuła. Tzn. kiedy planujemy wdrożenie Patroni i oceniamy, ile danych możemy stracić, musimy się opierać na tych formułach i mniej więcej przewidywać, ile danych możemy stracić.
Dobra wiadomość jest taka, że kiedy stary master odszedł do przodu, mógł się do przodu przesunąć dzięki pewnym procesom w tle. Tzn. uruchomił się jakiś auto-wakum, zapisał dane, zachował je w dzienniku transakcji. I te dane możemy swobodnie ignorować i stracić. Nie ma w tym żadnego problemu.

Tak wyglądają logi w przypadku, gdy ustawiono maximum_lag_on_failover i nastąpiło przełączenie, i trzeba wybrać nowego mastera. Replika ocenia siebie jako niezdolną do udziału w wyborach. Odrzuca udział w wyścigu o lidera. Czeka, aż zostanie wybrany nowy master, aby później do niego dołączyć. To dodatkowy środek zapobiegający utracie danych.


Nasza zespół produktowy zgłosił, że ich produkt ma problemy z działaniem na Postgresie. Przy tym dostęp do samego mastera jest niemożliwy, ponieważ jest niedostępny przez SSH. Auto-przełączenie też się nie odbywa.
Ten host został wymuszenie zrestartowany. W wyniku restartu doszło do auto-przełączenia, chociaż można było zrobić to również ręcznie, jak teraz rozumiem. Po restarcie idziemy sprawdzić, co działo się z naszym aktualnym masterem.

Jednocześnie z góry wiedzieliśmy, że mamy problemy z dyskami, tzn. już po monitoringu wiedzieliśmy, gdzie szukać i co sprawdzać.

Zagłębialiśmy się w logi Postgresa, zaczęliśmy obserwować, co się tam dzieje. Zobaczyliśmy commity, które trwały tam po jednej, dwie, trzy sekundy, co zdecydowanie nie jest normalne. Zauważyliśmy, że auto-wakum uruchamia się bardzo długo i dziwnie. Zobaczyliśmy też pliki tymczasowe na dysku. Tzn. to wszystko są oznaki problemów z dyskami.

Przyjrzeliśmy się systemowemu dmesg (logom komunikatów jądra). Zobaczyliśmy, że mamy problemy z jednym z dysków. Podsystem dyskowy stanowił oprogramowanie RAID. Spodziewaliśmy się w /proc/mdstat, że brakuje nam jednego dysku. To znaczy, że w tej konfiguracji RAID z 8 dysków brakuje nam jednego. Przy bliższym przyjrzeniu się slajdowi, w wyjściu można zauważyć, że brakuje tam sde. Można powiedzieć, że dysk nam 'wypadł'. To wyzwoliło problemy z dyskiem, a aplikacje również miały trudności w pracy z klastrem Postgresa.

W tej sytuacji Patroni nie mógłby nam pomóc, ponieważ nie ma zadania śledzenia stanu serwera ani stanu dysku. Musimy takie sytuacje monitorować zewnętrznie. Szybko dodaliśmy monitoring dysków do zewnętrznego monitoringu.
Zrodziła się myśl - czy mogliby nam pomóc fensting lub programowy watchdog? Zastanawialiśmy się, że w tym przypadku raczej by nam nie pomogło, ponieważ podczas problemów Patroni nadal komunikował się z klastrem DCS i nie zauważał żadnych problemów. Z punktu widzenia DCS i Patroni z klastrem wszystko było w porządku, chociaż w rzeczywistości były problemy z dyskiem, a także problemy z dostępnością bazy danych.

Moim zdaniem, to jeden z najdziwniejszych problemów, które badałem bardzo długo, przeczytałem bardzo dużo logów i nazwałem go klastrem-symulantem.

Problem polegał na tym, że stary master nie mógł stać się normalną repliką, to znaczy Patroni go uruchamiał, Patroni wskazywał, że ten węzeł występuje jako replika, ale jednocześnie nie był normalną repliką. Teraz zobaczycie, dlaczego. To mi zostało z analizy tego problemu.

I od czego to wszystko się zaczęło? Zaczęło się, jak w poprzednim problemie, od zastoju dysków. Mieliśmy po jednej, dwie komendy na sekundę.

Były zerwania połączeń, to znaczy klienci przerywali.

Były blokady o różnym nasileniu.

I, co za tym idzie, podsystem dyskowy nie był zbyt responsywny.

Najbardziej tajemnicze dla mnie to przychodzące natychmiastowe żądanie wyłączenia. Postgres ma trzy tryby wyłączania:
- To graceful, kiedy czekamy, aż wszyscy klienci samodzielnie się rozłączą.
- Jest fast, kiedy zmuszamy klientów do rozłączenia, ponieważ idziemy na wyłączenie.
- I immediate. In this case, immediate does not even inform clients to disconnect; it just shuts down without warning. All clients receive a message from the operating system: RST (TCP message, indicating that the connection has been interrupted and the client has nothing more to catch).
Who sent this signal? Background Postgres processes do not send such signals to each other, i.e., this is a kill-9. They do not send such signals to each other; they only react to them, meaning this is an emergency restart of Postgres. I do not know who sent it.
I looked at the 'last' command and saw one person who also logged into this server with us, but I hesitated to ask the question. Perhaps this was a kill -9. I would have seen kill -9 in the logs, as Postgres indicates that it received a kill -9, but I did not see this in the logs.

Digging further, I noticed that Patroni had not written to the log for quite a long time – 54 seconds. And if we compare the two timestamps, there were approximately 54 seconds with no messages.

During this time, an auto-failover occurred. Patroni worked flawlessly again. Our old master was unavailable, something was happening with it. New leader elections began. Everything worked well here. Our pgsql01 became the new leader.

We have a replica that became the master. And there is a second replica. And there were problems with the second replica. It was trying to reconfigure itself. As I understand it, it was trying to change recovery.conf, restart Postgres, and connect to the new master. Every 10 seconds, it writes messages that it is trying, but it is unsuccessful.

And during these attempts, the old master received an immediate-shutdown signal. The master restarts. And recovery also stops because the old master is rebooting. That is, the replica cannot connect to it because it is in shut down mode.

At some point, it worked, but replication did not start.
I have a single hypothesis that the recovery.conf contained the address of the old master. And when the new master appeared, the second replica was still trying to connect to the old master.

When Patroni started on the second replica, the node started up but could not connect via replication. And a replication lag was formed, which looked something like this. That is, all three nodes were present, but the second node was lagging.

Jednak patrząc na zapisy w logach, można było zobaczyć, że replikacja nie może się uruchomić, ponieważ dzienniki transakcji się różnią. Te dzienniki transakcji, które proponuje master, wskazane w recovery.conf, po prostu nie pasują do naszego obecnego węzła.

I tutaj popełniłem błąd. Powinienem był sprawdzić, co znajduje się w recovery.conf, aby zweryfikować moją hipotezę, że łączymy się z niewłaściwym masterem. Ale wtedy dopiero zaczynałem z tym i nie przyszło mi to do głowy, albo zauważyłem, że replikacja się opóźnia i trzeba będzie ją przeładować, czyli jakoś na to wpadłem z lenistwa. To była moja wina.

Po 30 minutach przyszedł administrator, tzn. zrestartowałem Patroni na replikacji. Już postawiłem na niej krzyżyk, myślałem, że trzeba będzie ją przeładować. I pomyślałem – zrestartuję Patroni, może coś dobrego z tego wyjdzie. Aktywowano recovery. I baza nawet się otworzyła, była gotowa przyjmować połączenia.

Replikacja wystartowała. Ale po minucie odpadła z błędem, że nie pasują do niej dzienniki transakcji.

Pomyślałem, że spróbuję jeszcze raz zrestartować. Zrestartowałem jeszcze raz Patroni, przy czym nie restartowałem Postgresa, a właśnie Patroni, mając nadzieję, że w magiczny sposób uruchomi bazę.

Replikacja znowu wystartowała, ale znaczniki w dzienniku transakcji się różniły, były inne niż te przy poprzedniej próbie uruchomienia. Replikacja znowu się zatrzymała. I komunikat był już trochę inny. I nie był dla mnie szczególnie informacyjny.

I wtedy przeszło mi przez myśl – a co jeśli zrestartuję Postgresa, a w tym czasie na bieżącym masterze zrobię checkpoint, aby przesunąć punkt w dzienniku transakcji trochę do przodu, żeby recovery rozpoczęło się z innego momentu? Dodatkowo mieliśmy też zapasy WAL.

Zrestartowałem Patroni, zrobiłem kilka checkpointów na masterze, kilka punktów restartowych na replikacji, kiedy się otworzyła. I to pomogło. Długo myślałem, dlaczego to pomogło i jak to zadziałało. I replikacja wystartowała. I replikacja nie przerywała się już więcej.

Taki problem jest dla mnie jednym z bardziej zagadkowych, nad którym nadal się zastanawiam, co tak naprawdę się tam działo.
Jakie wnioski można tutaj wyciągnąć? Patroni może działać zgodnie z zamysłem i bez żadnych błędów. Ale to nie jest 100% gwarancją, że wszystko u nas jest w porządku. Replika może ruszyć, ale może być w pół-roboczym stanie, a aplikacja nie powinna korzystać z takiej repliki, ponieważ będą tam stare dane.
I po failoverze zawsze trzeba sprawdzić, czy wszystko w porządku z klastrem, tzn. czy jest odpowiednia liczba replik, a także czy nie ma opóźnienia w replikacji.

I w trakcie rozważania tych problemów sformułuję rekomendacje. Próbowałem je połączyć w dwa slajdy. Prawdopodobnie wszystkie historie można by złączyć w dwa slajdy i tylko je opowiedzieć.

Kiedy używasz Patroni, musisz mieć monitoring. Zawsze powinieneś wiedzieć, kiedy nastąpił auto-failover, ponieważ jeśli nie wiesz, że miałeś auto-failover, nie kontrolujesz klastra. I to jest złe.
Po każdym failoverze zawsze musimy ręcznie sprawdzić klaster. Musimy upewnić się, że zawsze mamy aktualną liczbę replik, nie ma opóźnienia w replikacji, a w logach nie ma błędów związanych z replikacją strumieniową, z Patroni, z systemem DCS.
Automatyka może działać skutecznie, Patroni to bardzo dobre narzędzie. Może działać, ale to nie doprowadzi klastra do wymaganego stanu. A jeśli się o tym nie dowiemy, będą mieliśmy problemy.
I Patroni to nie srebrna kula. Wciąż musimy mieć pojęcie, jak działa Postgres, jak działa replikacja oraz jak Patroni współdziała z Postgres i jak odbywa się interakcja między węzłami. To jest potrzebne, aby móc ręcznie naprawiać występujące problemy.

Jak podchodzę do diagnostyki? Ułożyło się tak, że pracujemy z różnymi klientami, a nikt nie ma stosu ELK, więc muszę grzebać w logach, otwierając 6 konsol i 2 zakładki. W jednej zakładce są logi Patroni dla każdego węzła, w drugiej zakładce – logi Consul lub Postgres w razie potrzeby. Diagnozowanie tego jest bardzo trudne.
Jakie podejścia wypracowałem? Po pierwsze, zawsze patrzę, kiedy nastąpił failover. Dla mnie to jest pewien punkt zwrotny. Patrzę, co się wydarzyło przed failoverem, w trakcie failovera i po failoverze. Failover ma dwie znaczniki: to czas rozpoczęcia i zakończenia.
Następnie w logach przeglądam zdarzenia przed failoverem, co mu towarzyszyło, tj. szukam przyczyn, dlaczego nastąpił failover.
Daje to obraz zrozumienia, co się wydarzyło i co można zrobić w przyszłości, aby takie okoliczności nie miały miejsca (a tym samym aby failover się nie zdarzył).
I gdzie zwykle patrzymy? Patrzę:
- Najpierw w logi Patroni.
- Następnie przeglądam logi Postgres lub logi DCS, w zależności od tego, co znalazłem w logach Patroni.
- Czasami logi systemowe również dają zrozumienie, co było przyczyną failoveru.

Jak odnoszę się do Patroni? Patrzę na Patroni bardzo pozytywnie. Moim zdaniem, to najlepsze, co obecnie istnieje. Znam wiele innych produktów. To Stolon, Repmgr, Pg_auto_failover, PAF. 4 narzędzia. Próbowałem ich wszystkich. Patroni spodobał mi się najbardziej.
Jeśli mnie zapytają: „Czy polecam Patroni?”. Powiem, że tak, ponieważ lubię Patroni. I wydaje mi się, że nauczyłem się go dobrze obsługiwać.
Jeśli jesteś ciekawy, jakie inne problemy mogą wystąpić z Patroni, oprócz tych, które wymieniłem, zawsze możesz przejść na stronę na GitHubie. Jest tam wiele różnych historii i omawiane są ciekawe problemy. W rezultacie zgłoszono i rozwiązano niektóre błędy, tj. to interesująca lektura.
Są tam ciekawe historie o tym, jak ludzie strzelają sobie w stopę. Bardzo pouczające. Czytasz i rozumiesz, że tak nie należy robić. Postawiłem sobie znacznik.
Chciałbym serdecznie podziękować firmie Zalando za wspieranie tego projektu, a szczególnie Aleksandrowi Kukuśkinowi i Siergiejowi Kliukinowi. Siergiej Kliukin to jeden z współautorów, nie pracuje już w Zalando, ale to są dwie osoby, które zaczynały pracować z tym produktem.
Uważam, że Patroni to bardzo fajna rzecz. Cieszę się, że istnieje, jest z nią ciekawie. I wielkie dzięki wszystkim współtwórcom, którzy piszą poprawki do Patroni. Mam nadzieję, że Patroni z wiekiem stanie się bardziej dojrzały, świetny i wydajny. Już teraz jest wydajny, ale mam nadzieję, że będzie jeszcze lepszy. Dlatego jeśli planujesz wdrożyć Patroni, nie bój się. To dobre rozwiązanie, które można wdrażać i używać.
To wszystko. Jeśli masz jakieś pytania, pytaj.

Pytania
Dziękuję za prezentację! Jeśli po failoverze i tak musisz tam dokładnie zajrzeć, to po co nam automatyczny failover?
Ponieważ to nowa rzecz. Pracujemy z nią dopiero od roku. Lepiej być ostrożnym. Chcemy zajrzeć i upewnić się, że wszystko działa tak, jak powinno. To poziom dorosłego niedowiarstwa – lepiej znowu to sprawdzić i zobaczyć.
Na przykład, weszliśmy rano i sprawdziliśmy, tak?
Nie rano, zwykle dowiadujemy się o auto-failoverze prawie natychmiast. Otrzymujemy powiadomienia, widzimy, że wystąpił auto-failover. Prawie natychmiast wchodzimy i sprawdzamy. Ale wszystkie te kontrole powinny być przeprowadzone na poziomie monitorowania. Jeśli odwołujemy się do Patroni za pomocą REST API, jest historia. Na podstawie historii można sprawdzić znaczniki czasowe, kiedy wystąpił failover. Na tej podstawie można zrobić monitoring. Można sprawdzić historię, ile tam było zdarzeń. Jeśli liczba zdarzeń wzrosła, to znaczy, że wystąpił auto-failover. Można przejść i sprawdzić. Albo nasza automatyka w monitorowaniu sprawdziła, że wszystkie nasze repliki są na miejscu, nie ma opóźnień i wszystko jest w porządku.
Dziękuję!
Dziękuję bardzo za wspaniałą opowieść! Jeśli przenieśliśmy klaster DCS w jakieś odległe miejsce od klastra Postgres, to czy ten klaster też trzeba okresowo obsługiwać? Jakie są najlepsze praktyki dotyczące wyłączania niektórych segmentów klastra DCS, co trzeba z nimi robić itd.? Jak w tym czasie funkcjonuje cała ta konstrukcja? I w jaki sposób te rzeczy robić?
Dla jednej firmy trzeba było stworzyć macierz problemów, co się dzieje, jeśli którykolwiek z komponentów lub kilka komponentów ulega awarii. Na podstawie tej macierzy kolejno przeglądamy wszystkie komponenty i tworzymy scenariusze w przypadku awarii tych komponentów. Odpowiednio na każdy scenariusz awarii można mieć plan działania na rzecz naprawy. I w przypadku DCS jest to część standardowej infrastruktury. Administrator to administrował, a my polegamy na administratorach, którzy to zarządzają i na jego zdolności do naprawy w przypadku awarii. Jeśli DCS w ogóle nie istnieje, to go wdrażamy, ale nie monitorujemy go szczególnie, ponieważ nie odpowiadamy za infrastrukturę, ale dajemy zalecenia jak i co monitorować.
To znaczy, czy dobrze rozumiem, że należy wyłączyć Patroni, wyłączyć failover, wyłączyć wszystko przed podjęciem jakichkolwiek działań z hostami?
To zależy od tego, ile węzłów mamy w klastrze DCS. Jeśli jest ich dużo i w przypadku awarii tylko jednego z węzłów (replik), to klaster zachowuje quorum. Patroni pozostaje wtedy sprawny i nic się nie uruchamia. Jeśli mamy jakieś skomplikowane operacje, które angażują więcej węzłów, których brak może zburzyć quorum, to tak, być może warto, aby Patroni został wstrzymany. Ma na to odpowiednią komendę – patronictl pause, patronictl resume. Po prostu wstrzymujemy, a autofailover w tym czasie się nie uruchamia. Wykonujemy prace konserwacyjne na klastrze DCS, potem wznawiamy i kontynuujemy pracę.
Dziękuję bardzo!
Bardzo dziękuję za referat! Jak zespół produktowy odnosi się do ryzyka utraty danych?
Zespoły produktowe się tym nie przejmują, ale liderzy zespołu się martwią.
Jakie są tam gwarancje?
Z gwarancjami jest bardzo trudno. Jest referat Aleksandra Kukuśkina „Jak obliczać RPO i RTO”, tzn. czas odzyskiwania i ile danych możemy stracić. Myślę, że warto znaleźć te slajdy i je przeanalizować. O ile pamiętam, są tam konkretne kroki do obliczenia tych parametrów. Ile transakcji możemy stracić, ile danych możemy utracić. Możemy rozważyć użycie synchronnej replikacji na poziomie Patroni, ale to miecz obosieczny: albo mamy niezawodność danych, albo tracimy na szybkości. Jest synchronna replikacja, ale też nie gwarantuje 100% ochrony przed utratą danych.
Alekseju, dziękuję za wspaniały referat! Czy masz doświadczenie z używaniem Patroni do ochrony zerowego poziomu? Tzn. w połączeniu z synchronnym standby? To pierwsze pytanie. A drugie pytanie. Korzystaliście z różnych rozwiązań. Używaliśmy Repmgr, ale bez autofailovera i teraz planujemy włączyć autofailover. Rozważamy Patroni jako alternatywne rozwiązanie. Co możesz powiedzieć o zaletach w porównaniu do Repmgr?
Pierwsze pytanie dotyczyło synchronnych replik. Nikt u nas nie używa synchronnej replikacji, bo wszyscy się boją (już kilku klientów ją stosuje, generalnie nie zauważyli problemów z wydajnością - Notatka prelegenta). Jednakże, ustaliliśmy zasadę, że w klastrze synchronnej replikacji powinny być minimum trzy węzły, ponieważ jeśli mamy dwa węzły i jeśli master lub replika zawiodą, Patroni przekształca ten węzeł w tryb Standalone, aby aplikacja mogła nadal działać. W tym przypadku istnieje ryzyko utraty danych.
W odniesieniu do drugiego pytania, używaliśmy Repmgr i wciąż go stosujemy u niektórych klientów z powodów historycznych. Co można powiedzieć? W Patroni autofailover jest dostępny od razu, w Repmgr autofailover jest dodatkiem, który trzeba aktywować. Musimy uruchomić demon Repmgr na każdym węźle, aby móc skonfigurować autofailover.
Repmgr sprawdza, czy węzły Postgres są aktywne. Procesy Repmgr sprawdzają istnienie siebie nawzajem, co nie jest zbyt efektywnym podejściem, ponieważ mogą wystąpić skomplikowane przypadki izolacji sieciowej, przez co duży klaster Repmgr może się rozpaść na kilka mniejszych i kontynuować działanie. Od dłuższego czasu nie obserwuję Repmgr, może to naprawili... a może nie. Natomiast wyodrębnienie informacji o stanie klastra do DCS, jak robią Stolon i Patroni, to najżywotniejsza opcja.
Alieksiej, mam pytanie, może nieco naiwne. W jednym z pierwszych przykładów DCS przenieśliście z lokalnej maszyny na zdalny węzeł. Rozumiemy, że sieć to rzecz, która ma swoje specyfiki, żyje samodzielnie. Co się stanie, jeśli z jakiegoś powodu klaster DCS stanie się niedostępny? Nie będę mówił o przyczynach, może ich być wiele: od złej pracy sieciowców po realne problemy.
Nie powiedziałem tego na głos, ale klaster DCS powinien być również odporny na awarie, tj. musi mieć nieparzystą liczbę węzłów, aby można było osiągnąć kworum. Co się stanie, jeśli klaster DCS stanie się niedostępny lub nie można osiągnąć kworum, t.j. jakiś podział sieciowy lub awaria węzłów? W tym przypadku klaster Patroni przechodzi w tryb tylko do odczytu. Klaster Patroni nie może określić stanu klastra ani co ma robić. Nie może połączyć się z DCS i zapisać tam nowego stanu klastra, dlatego cały klaster przechodzi w tryb tylko do odczytu. Oczekuje na ręczną interwencję operatora lub na przywrócenie DCS.
Mówiąc wprost, DCS staje się dla nas usługą równie ważną, jak sama baza danych?
Tak, tak. W wielu nowoczesnych firmach Discovery usług jest nieodłączną częścią infrastruktury. Wprowadza się go nawet wcześniej, zanim pojawi się baza danych w infrastrukturze. Mówiąc w skrócie, uruchamia się infrastrukturę, rozbudowuje w centrum danych, a my natychmiast mamy Discovery usług. Jeśli to jest Consul, to na nim może być zbudowany także DNS. Jeśli to Etcd, może być częścią klastra Kubernetes, w którym będzie już wszystko inne rozbudowywane. Uważam, że Discovery usług jest już nieodłączną częścią nowoczesnych infrastruktur. Myślą o tym znacznie wcześniej, niż o bazach danych.
Dziękuję!
Źródło: habr.com
