{"id":35706,"date":"2019-10-31T22:05:50","date_gmt":"2019-10-31T19:05:50","guid":{"rendered":"https:\/\/prohoster.info\/blog\/otkazoustojchivyj-klaster-postgresql-patroni-opyt-vnedreniya\/"},"modified":"2026-05-18T20:58:46","modified_gmt":"2026-05-18T18:58:46","slug":"otkazoustojchivyj-klaster-postgresql-patroni-opyt-vnedreniya","status":"publish","type":"post","link":"https:\/\/prohoster.info\/pl\/blog\/administrirovanie\/otkazoustojchivyj-klaster-postgresql-patroni-opyt-vnedreniya","title":{"rendered":"Odporna na awarie klaster PostgreSQL + Patroni. Do\u015bwiadczenia wdro\u017ceniowe","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>W artykule opowiem, jak podeszli\u015bmy do kwestii odporno\u015bci PostgreSQL, dlaczego sta\u0142o si\u0119 to dla nas wa\u017cne i co ostatecznie osi\u0105gn\u0119li\u015bmy.<\/p>\n<p>Mamy wysoko obci\u0105\u017con\u0105 us\u0142ug\u0119: 2,5 miliona u\u017cytkownik\u00f3w na ca\u0142ym \u015bwiecie, ponad 50 tysi\u0119cy aktywnych u\u017cytkownik\u00f3w ka\u017cdego dnia. Serwery znajduj\u0105 si\u0119 w Amazone w jednym regionie Irlandii: w dzia\u0142aniu jest ci\u0105gle ponad 100 r\u00f3\u017cnych serwer\u00f3w, z czego prawie 50 \u2013 z bazami danych.<\/p>\n<p>Ca\u0142y backend to du\u017ca monolityczna aplikacja stateful napisana w Javie, kt\u00f3ra utrzymuje sta\u0142e po\u0142\u0105czenie websocket z klientem. Kiedy kilku u\u017cytkownik\u00f3w pracuje jednocze\u015bnie na jednej tablicy, wszyscy widz\u0105 zmiany w czasie rzeczywistym, poniewa\u017c ka\u017cd\u0105 zmian\u0119 zapisujemy w bazie. Mamy oko\u0142o 10 tysi\u0119cy zapyta\u0144 na sekund\u0119 do naszych baz. W szczytowym obci\u0105\u017ceniu w Redis wysy\u0142amy od 80 do 100 tysi\u0119cy zapyta\u0144 na sekund\u0119.<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/443f85815b0560fade7db5b639942935.png\" alt=\"Odporna na awarie klaster PostgreSQL + Patroni. Do\u015bwiadczenia wdro\u017ceniowe\" \/><br \/>\n<a rel=\"nofollow\" name=\"habracut\"><\/a><\/p>\n<h2>Dlaczego przeszli\u015bmy z Redis na PostgreSQL<\/h2>\n<p>Pocz\u0105tkowo nasza us\u0142uga dzia\u0142a\u0142a z Redis, magazynem kluczy-warto\u015bci, kt\u00f3ry przechowuje wszystkie dane w pami\u0119ci operacyjnej. <a href=\"https:\/\/prohoster.info\/pl\/server\/\">serwera<\/a>.<\/p>\n<p>Zalety Redis:<\/p>\n<ol>\n<li>Wysoka pr\u0119dko\u015b\u0107 odpowiedzi, poniewa\u017c wszystko jest przechowywane w pami\u0119ci;<\/li>\n<li>Wygoda tworzenia kopii zapasowych i replikacji.<\/li>\n<\/ol>\n<p>Wady Redis dla nas:<\/p>\n<ol>\n<li>Brak prawdziwych transakcji. Pr\u00f3bowali\u015bmy je na\u015bladowa\u0107 na poziomie naszej aplikacji. Niestety, nie zawsze dzia\u0142a\u0142o to dobrze i wymaga\u0142o napisania bardzo z\u0142o\u017conego kodu.<\/li>\n<li>Obj\u0119to\u015b\u0107 danych jest ograniczona przez ilo\u015b\u0107 pami\u0119ci. Przy wzro\u015bcie ilo\u015bci danych pami\u0119\u0107 b\u0119dzie ros\u0142a, a w ko\u0144cu napotkamy na w\u0142a\u015bciwo\u015bci wybranego instancji, co w AWS wymaga zatrzymania naszej us\u0142ugi, aby zmieni\u0107 typ instancji.<\/li>\n<li>Nale\u017cy nieustannie utrzymywa\u0107 poziom niskiego op\u00f3\u017anienia, poniewa\u017c mamy bardzo du\u017c\u0105 liczb\u0119 zapyta\u0144. Optymalny dla nas poziom op\u00f3\u017anienia to 17-20 ms. Przy poziomie 30-40 ms otrzymujemy d\u0142ugie odpowiedzi na zapytania naszej aplikacji oraz degradacj\u0119 us\u0142ugi. Niestety, wydarzy\u0142o si\u0119 to we wrze\u015bniu 2018 roku, kiedy jedna z instancji z Redis z jakiego\u015b powodu uzyska\u0142a op\u00f3\u017anienie dwukrotnie wi\u0119ksze od normalnego. Aby rozwi\u0105za\u0107 problem, zatrzymali\u015bmy us\u0142ug\u0119 w po\u0142owie dnia roboczego na nieplanow\u0105 konserwacj\u0119 i wymienili\u015bmy problemow\u0105 instancj\u0119 Redis.<\/li>\n<li>\u0141atwo mo\u017cna uzyska\u0107 niesp\u00f3jno\u015b\u0107 danych nawet przy niewielkich b\u0142\u0119dach w kodzie, a nast\u0119pnie sp\u0119dzi\u0107 du\u017co czasu na pisaniu kodu, aby naprawi\u0107 te dane.<\/li>\n<\/ol>\n<p>Zidentyfikowali\u015bmy wady i zrozumieli\u015bmy, \u017ce musimy przenie\u015b\u0107 si\u0119 na co\u015b bardziej wygodnego, z normalnymi transakcjami i mniejsz\u0105 zale\u017cno\u015bci\u0105 od op\u00f3\u017anie\u0144. Przeprowadzili\u015bmy badania, przeanalizowali\u015bmy wiele opcji i wybrali\u015bmy PostgreSQL.<\/p>\n<p>Migracja do nowej bazy danych trwa ju\u017c 1,5 roku i przenie\u015bli\u015bmy tylko niewielk\u0105 cz\u0119\u015b\u0107 danych, dlatego pracujemy jednocze\u015bnie z Redis i PostgreSQL. Wi\u0119cej na temat etap\u00f3w migracji i prze\u0142\u0105czania danych mi\u0119dzy bazami opisano w <a href=\"https:\/\/habr.com\/ru\/company\/miro\/blog\/437826\/\" rel=\"nofollow\">artyku\u0142ach mojego kolegi.<\/a>.<\/p>\n<p>Gdy tylko zaczynali\u015bmy migracj\u0119, nasza aplikacja dzia\u0142a\u0142a bezpo\u015brednio z baz\u0105 danych i \u0142\u0105czy\u0142a si\u0119 z g\u0142\u00f3wnym Redis i PostgreSQL. Klaster PostgreSQL sk\u0142ada\u0142 si\u0119 z g\u0142\u00f3wnego i repliki z asynchroniczn\u0105 replikacj\u0105. Tak wygl\u0105da\u0142a schemat pracy z bazami:<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/674dd77de4c8b48a946c9f64c0b2fdd1.png\" alt=\"Odporna na awarie klaster PostgreSQL + Patroni. Do\u015bwiadczenia wdro\u017ceniowe\" \/><\/p>\n<h2>Wdro\u017cenie PgBouncer<\/h2>\n<p>Podczas gdy migrowali\u015bmy, produkt r\u00f3wnie\u017c si\u0119 rozwija\u0142: zwi\u0119ksza\u0142a si\u0119 liczba u\u017cytkownik\u00f3w i liczba serwer\u00f3w pracuj\u0105cych z PostgreSQL, i zacz\u0119\u0142o brakowa\u0107 nam po\u0142\u0105cze\u0144. PostgreSQL dla ka\u017cdego po\u0142\u0105czenia tworzy osobny proces i zu\u017cywa zasoby. Mo\u017cliwo\u015b\u0107 zwi\u0119kszenia liczby po\u0142\u0105cze\u0144 istnieje do pewnego momentu, w przeciwnym razie istnieje ryzyko nieoptymalnej pracy bazy danych. Idealnym rozwi\u0105zaniem w takiej sytuacji by\u0142by wyb\u00f3r mened\u017cera po\u0142\u0105cze\u0144, kt\u00f3ry stanie przed baz\u0105.<\/p>\n<p>Mieli\u015bmy dwa warianty dla mened\u017cera po\u0142\u0105cze\u0144: Pgpool i PgBouncer. Jednak pierwszy nie obs\u0142uguje trybu transakcyjnego pracy z baz\u0105, dlatego wybrali\u015bmy PgBouncer.<\/p>\n<p>Skonfigurowali\u015bmy nast\u0119puj\u0105cy schemat dzia\u0142ania: nasza aplikacja \u0142\u0105czy si\u0119 z jednym PgBouncer, za kt\u00f3rym znajduj\u0105 si\u0119 g\u0142\u00f3wne bazy PostgreSQL, a za ka\u017cd\u0105 g\u0142\u00f3wn\u0105 - jedna replika z asynchroniczn\u0105 replikacj\u0105.<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/dabcb7f6006520c4fec85fb925e8975e.png\" alt=\"Odporna na awarie klaster PostgreSQL + Patroni. Do\u015bwiadczenia wdro\u017ceniowe\" \/><\/p>\n<p>W tym czasie nie mogli\u015bmy przechowywa\u0107 ca\u0142ej obj\u0119to\u015bci danych w PostgreSQL i dla nas liczy\u0142a si\u0119 szybko\u015b\u0107 pracy z baz\u0105, dlatego zacz\u0119li\u015bmy szardowa\u0107 PostgreSQL na poziomie aplikacji. Opisana powy\u017cej schemata jest stosunkowo wygodna do tego: przy dodawaniu nowego sharda w PostgreSQL wystarczy zaktualizowa\u0107 konfiguracj\u0119 PgBouncer, a aplikacja mo\u017ce natychmiast pracowa\u0107 z nowym shardem.<\/p>\n<h3>Niezawodno\u015b\u0107 PgBouncer<\/h3>\n<p>Ten schemat dzia\u0142a\u0142 do momentu, gdy jedyny instancja PgBouncer si\u0119 zawiesi\u0142. Jeste\u015bmy w AWS, gdzie wszystkie instancje dzia\u0142aj\u0105 na sprz\u0119cie, kt\u00f3ry okresowo zawodzi. W takich przypadkach instancja po prostu przenosi si\u0119 na nowy sprz\u0119t i dzia\u0142a z powrotem. Tak sta\u0142o si\u0119 r\u00f3wnie\u017c z PgBouncer, jednak sta\u0142 si\u0119 on niedost\u0119pny. Efektem tej awarii by\u0142a niedost\u0119pno\u015b\u0107 naszej us\u0142ugi przez 25 minut. AWS zaleca stosowanie redundancji po stronie u\u017cytkownika w takich sytuacjach, co nie zosta\u0142o wtedy przez nas wdro\u017cone.<\/p>\n<p>Po tym zdarzeniu powa\u017cnie zacz\u0119li\u015bmy my\u015ble\u0107 o odporno\u015bci PgBouncer i klastr\u00f3w PostgreSQL, poniewa\u017c podobna sytuacja mog\u0142a si\u0119 powt\u00f3rzy\u0107 z dowoln\u0105 instancj\u0105 w naszym koncie AWS.<\/p>\n<p>Schemat odporno\u015bci PgBouncer zbudowali\u015bmy w nast\u0119puj\u0105cy spos\u00f3b: wszystkie serwery aplikacji kieruj\u0105 swoje zapytania do Network Load Balancer, za kt\u00f3rym znajduj\u0105 si\u0119 dwa PgBouncer. Ka\u017cdy z PgBouncer obserwuje te same g\u0142\u00f3wne instancje PostgreSQL ka\u017cdego shardu. W przypadku powt\u00f3rzenia sytuacji z awari\u0105 instancji AWS, ca\u0142y ruch jest przekierowywany przez inny PgBouncer. Odporno\u015b\u0107 Network Load Balancer zapewnia AWS.<\/p>\n<p>Taki schemat umo\u017cliwia bezproblemowe dodawanie nowych serwer\u00f3w PgBouncer.<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/2c1f1e7d9f7be4fdeb43858520d55e36.png\" alt=\"Odporna na awarie klaster PostgreSQL + Patroni. Do\u015bwiadczenia wdro\u017ceniowe\" \/><\/p>\n<h2>Tworzenie odpornego klastra PostgreSQL<\/h2>\n<p>Rozwa\u017caj\u0105c to zadanie, brali\u015bmy pod uwag\u0119 r\u00f3\u017cne opcje: w\u0142asne rozwi\u0105zanie failover, repmgr, AWS RDS, Patroni.<\/p>\n<h3>W\u0142asne skrypty<\/h3>\n<p>Mog\u0105 monitorowa\u0107 dzia\u0142anie g\u0142\u00f3wnej instancji i, w przypadku jej awarii, promowa\u0107 replik\u0119 do roli g\u0142\u00f3wnej oraz aktualizowa\u0107 konfiguracj\u0119 PgBouncer.<\/p>\n<p>Zalety tego podej\u015bcia polegaj\u0105 na maksymalnej prostocie, poniewa\u017c sami piszecie skrypty i dok\u0142adnie rozumiecie, jak one dzia\u0142aj\u0105.<\/p>\n<p>Wady:<\/p>\n<ul>\n<li>G\u0142\u00f3wna instancja mog\u0142a nie umrze\u0107, lecz mog\u0142o doj\u015b\u0107 do awarii sieci. Failover, nie wiedz\u0105c o tym, promuje replik\u0119 do roli g\u0142\u00f3wnej, a stara g\u0142\u00f3wna instancja nadal dzia\u0142a. W rezultacie mamy dwa serwery pe\u0142ni\u0105ce rol\u0119 g\u0142\u00f3wnej i nie b\u0119dziemy wiedzie\u0107, na kt\u00f3rym z nich znajduj\u0105 si\u0119 najbardziej aktualne dane. Tak\u0105 sytuacj\u0119 nazywa si\u0119 split-brain.<\/li>\n<li>Pozostali\u015bmy bez repliki. W naszej konfiguracji mamy g\u0142\u00f3wn\u0105 instancj\u0119 i jedn\u0105 replik\u0119, po prze\u0142\u0105czeniu replika staje si\u0119 g\u0142\u00f3wn\u0105, a my nie mamy wi\u0119cej replik, wi\u0119c musimy r\u0119cznie doda\u0107 now\u0105 replik\u0119.<\/li>\n<li>Potrzebny jest dodatkowy monitoring pracy failover, a mamy 12 shard\u00f3w PostgreSQL, co oznacza, \u017ce musimy monitorowa\u0107 12 klastr\u00f3w. Przy zwi\u0119kszaniu liczby shard\u00f3w nie mo\u017cemy zapomnie\u0107 o aktualizacji failover.<\/li>\n<\/ul>\n<p>Samodzielne rozwi\u0105zanie failover wydaje si\u0119 skomplikowane i wymaga nietrywialnego wsparcia. W przypadku jednego klastra PostgreSQL b\u0119dzie to najprostsza opcja, ale nie scala si\u0119, wi\u0119c nie b\u0119dzie odpowiednia dla nas.<\/p>\n<h3>Repmgr<\/h3>\n<p>Menad\u017cer replikacji dla klastr\u00f3w PostgreSQL, kt\u00f3ry potrafi zarz\u0105dza\u0107 dzia\u0142aniem klastra PostgreSQL. Nie ma automatycznego failover \u201ez pude\u0142ka\u201d, wi\u0119c aby to dzia\u0142a\u0142o, trzeba napisa\u0107 w\u0142asn\u0105 \u201eowijk\u0119\u201d nad gotowym rozwi\u0105zaniem. Dlatego mo\u017ce by\u0107 to nawet bardziej skomplikowane ni\u017c z samodzielnie napisanymi skryptami, a wi\u0119c Repmgr nawet nie pr\u00f3bowali\u015bmy.<\/p>\n<h3>AWS RDS<\/h3>\n<p>Obs\u0142uguje wszystko, co jest nam potrzebne, potrafi robi\u0107 kopie zapasowe i obs\u0142uguje pul\u0119 po\u0142\u0105cze\u0144. Ma automatyczne prze\u0142\u0105czanie: po \u015bmierci g\u0142\u00f3wnego w\u0119z\u0142a replika staje si\u0119 nowym g\u0142\u00f3wnym, a AWS zmienia rekord DNS na nowego g\u0142\u00f3wnego, przy czym repliki mog\u0105 znajdowa\u0107 si\u0119 w r\u00f3\u017cnych AZ.<\/p>\n<p>Do minus\u00f3w mo\u017cna zaliczy\u0107 brak drobnych ustawie\u0144. Jako przyk\u0142ad drobnych ustawie\u0144: na naszych instancjach obowi\u0105zuj\u0105 ograniczenia dla po\u0142\u0105cze\u0144 TCP, czego niestety nie mo\u017cna zrobi\u0107 w RDS:<\/p>\n<pre><code class=\"python\">net.ipv4.tcp_keepalive_time=10\nnet.ipv4.tcp_keepalive_intvl=1\nnet.ipv4.tcp_keepalive_probes=5\nnet.ipv4.tcp_retries2=3\n<\/code><\/pre>\n<p>Ponadto cena AWS RDS jest prawie dwa razy dro\u017csza od standardowej ceny instancji, co by\u0142o g\u0142\u00f3wnym powodem rezygnacji z tego rozwi\u0105zania.<\/p>\n<h3>Patroni<\/h3>\n<p>To jest szablon w Pythonie do zarz\u0105dzania PostgreSQL z dobr\u0105 dokumentacj\u0105, automatycznym failover i kodem \u017ar\u00f3d\u0142owym na GitHubie.<\/p>\n<p>Zalety Patroni:<\/p>\n<ul>\n<li>Ka\u017cdy parametr konfiguracji jest dok\u0142adnie opisany, jasno jak to dzia\u0142a;<\/li>\n<li>Automatyczny failover dzia\u0142a z pude\u0142ka;<\/li>\n<li>Napisany w Pythonie, a poniewa\u017c sami du\u017co piszemy w Pythonie, \u0142atwiej b\u0119dzie nam zajmowa\u0107 si\u0119 problemami i by\u0107 mo\u017ce nawet pom\u00f3c w rozwoju projektu;<\/li>\n<li>Ca\u0142kowicie zarz\u0105dza PostgreSQL, pozwala na zmian\u0119 konfiguracji na wszystkich w\u0119z\u0142ach klastra, a je\u015bli nowa konfiguracja wymaga ponownego uruchomienia klastra, mo\u017cna to zrobi\u0107 ponownie za pomoc\u0105 Patroni.<\/li>\n<\/ul>\n<p>Wady:<\/p>\n<ul>\n<li>Z dokumentacji nie jest jasne, jak prawid\u0142owo pracowa\u0107 z PgBouncer. Chocia\u017c trudno to nazwa\u0107 wad\u0105, poniewa\u017c zadaniem Patroni jest zarz\u0105dzanie PostgreSQL, a jak b\u0119d\u0105 odbywa\u0107 si\u0119 po\u0142\u0105czenia z Patroni \u2014 to ju\u017c nasz problem;<\/li>\n<li>Ma\u0142o przyk\u0142ad\u00f3w wdro\u017cenia Patroni przy du\u017cych obj\u0119to\u015bciach, przy tym wiele przyk\u0142ad\u00f3w wdro\u017cenia od zera.<\/li>\n<\/ul>\n<p>W rezultacie do stworzenia odpornego na awarie klastra wybrali\u015bmy w\u0142a\u015bnie Patroni.<\/p>\n<h2>Proces wdra\u017cania Patroni<\/h2>\n<p>Przed wprowadzeniem Patroni mieli\u015bmy 12 shard\u00f3w PostgreSQL w konfiguracji z jednym masterem i jedn\u0105 replik\u0105 z asynchroniczn\u0105 replikacj\u0105. Serwery aplikacji \u0142\u0105czy\u0142y si\u0119 z bazami danych poprzez Network Load Balancer, za kt\u00f3rym sta\u0142y dwa instancje z PgBouncer, a za nimi znajdowa\u0142y si\u0119 wszystkie serwery PostgreSQL.<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/8e5350d2466311593e11add30d5b1bdc.png\" alt=\"Odporna na awarie klaster PostgreSQL + Patroni. Do\u015bwiadczenia wdro\u017ceniowe\" \/><\/p>\n<p>Aby wdro\u017cy\u0107 Patroni, musieli\u015bmy wybra\u0107 rozproszone magazynowanie konfiguracji klastra. Patroni dzia\u0142a z rozproszonymi systemami magazynowania konfiguracji, takimi jak etcd, Zookeeper, Consul. Mamy w\u0142a\u015bnie w produkcji pe\u0142noprawny klaster Consul, kt\u00f3ry wsp\u00f3\u0142pracuje z Vault, a nie u\u017cywamy go w \u017caden inny spos\u00f3b. To doskona\u0142a okazja, aby zacz\u0105\u0107 u\u017cywa\u0107 Consul zgodnie z przeznaczeniem.<\/p>\n<h3>Jak dzia\u0142a Patroni z Consul<\/h3>\n<p>Mamy klaster Consul, kt\u00f3ry sk\u0142ada si\u0119 z trzech w\u0119z\u0142\u00f3w oraz klaster Patroni, kt\u00f3ry sk\u0142ada si\u0119 z lidera i repliki (w Patroni master nazywany jest liderem klastra, a slave'y \u2013 replikami). Ka\u017cda instancja klastra Patroni nieustannie przesy\u0142a do Consul informacje o stanie klastra. Dlatego z Consul mo\u017cna zawsze uzyska\u0107 aktualn\u0105 konfiguracj\u0119 klastra Patroni i to, kto jest obecnie liderem.<\/p>\n<p><img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/3234594b363904424fdce64c9d77fca5.png\" alt=\"Odporna na awarie klaster PostgreSQL + Patroni. Do\u015bwiadczenia wdro\u017ceniowe\" \/><\/p>\n<p>Aby po\u0142\u0105czy\u0107 Patroni z Consul, wystarczy zapozna\u0107 si\u0119 z oficjaln\u0105 dokumentacj\u0105, w kt\u00f3rej napisane jest, \u017ce nale\u017cy wskaza\u0107 host w formacie http lub https, w zale\u017cno\u015bci od tego, jak pracujemy z Consul, oraz opcjonalnie schemat po\u0142\u0105czenia:<\/p>\n<pre><code class=\"plaintext\">host: host:port dla punktu ko\u0144cowego Consul, w formacie: http(s):\/\/host:port\nscheme: (opcjonalnie) http lub https, domy\u015blnie http<\/code><\/pre>\n<p>Wygl\u0105da prosto, ale tutaj zaczynaj\u0105 si\u0119 problemy. Z Consul pracujemy za pomoc\u0105 zabezpieczonego po\u0142\u0105czenia przez https, a nasza konfiguracja po\u0142\u0105czenia b\u0119dzie wygl\u0105da\u0107 nast\u0119puj\u0105co:<\/p>\n<pre><code class=\"python\">consul:\n  host: https:\/\/server.production.consul:8080 \n  verify: true\n  cacert: {{ consul_cacert }}\n  cert: {{ consul_cert }}\n  key: {{ consul_key }}<\/code><\/pre>\n<p>Ale to nie dzia\u0142a. Przy uruchamianiu Patroni nie mo\u017ce po\u0142\u0105czy\u0107 si\u0119 z Consul, poniewa\u017c wci\u0105\u017c pr\u00f3buje po\u0142\u0105czy\u0107 si\u0119 za po\u015brednictwem http.<\/p>\n<p>Zrozumienie problemu u\u0142atwi\u0142 kod \u017ar\u00f3d\u0142owy Patroni. Dobrze, \u017ce jest napisany w Pythonie. Okazuje si\u0119, \u017ce parametr host w \u017caden spos\u00f3b nie jest analizowany, a protok\u00f3\u0142 musi by\u0107 wskazany w schemacie. Oto jak wygl\u0105da dzia\u0142aj\u0105cy blok konfiguracji do pracy z Consul u nas:<\/p>\n<pre><code class=\"python\">consul:\n  host: server.production.consul:8080\n  scheme: https\n  verify: true\n  cacert: {{ consul_cacert }}\n  cert: {{ consul_cert }}\n  key: {{ consul_key }}<\/code><\/pre>\n<h3>Consul-template<\/h3>\n<p>Wi\u0119c wybrali\u015bmy magazyn konfiguracyjny. Teraz musimy zrozumie\u0107, jak PgBouncer b\u0119dzie prze\u0142\u0105cza\u0142 swoj\u0105 konfiguracj\u0119 w momencie zmiany lidera w klastrze Patroni. W dokumentacji na ten temat nie ma odpowiedzi, poniewa\u017c zasadniczo nie opisano wsp\u00f3\u0142pracy z PgBouncer.<\/p>\n<p>W poszukiwaniu rozwi\u0105zania znale\u017ali\u015bmy artyku\u0142 (tytu\u0142 niestety nie pami\u0119tam), w kt\u00f3rym napisano, \u017ce Consul-template bardzo pom\u00f3g\u0142 w po\u0142\u0105czeniu PgBouncer i Patroni. To zach\u0119ci\u0142o nas do zbadania dzia\u0142ania Consul-template.<\/p>\n<p>Okaza\u0142o si\u0119, \u017ce Consul-template nieustannie monitoruje konfiguracj\u0119 klastra PostgreSQL w Consul. W momencie zmiany lidera aktualizuje konfiguracj\u0119 PgBouncer i wysy\u0142a polecenie do jej prze\u0142adowania.<\/p>\n<p><img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/6cf92996a127bb6637ab81dc45fdd60a.png\" alt=\"Odporna na awarie klaster PostgreSQL + Patroni. Do\u015bwiadczenia wdro\u017ceniowe\" \/><\/p>\n<p>Wielk\u0105 zalet\u0105 template jest to, \u017ce jest przechowywany w postaci kodu, dzi\u0119ki czemu w przypadku dodania nowego shardu wystarczy wykona\u0107 nowy commit i zaktualizowa\u0107 template w trybie automatycznym, zachowuj\u0105c zasady Infrastructure as code.<\/p>\n<h3>Nowa architektura z Patroni<\/h3>\n<p>W rezultacie uzyskali\u015bmy tak\u0105 schem\u0119 pracy:<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/bf9a9eff675a26039e3f76b16c6cd713.png\" alt=\"Odporna na awarie klaster PostgreSQL + Patroni. Do\u015bwiadczenia wdro\u017ceniowe\" \/><\/p>\n<p>Wszystkie serwery aplikacji kieruj\u0105 zapytania do balancera \u2192 za nim stoj\u0105 dwa instancje PgBouncer \u2192 na ka\u017cdej instancji dzia\u0142a Consul-template, kt\u00f3ry monitoruje stan ka\u017cdego klastra Patroni i dba o aktualno\u015b\u0107 konfiguracji PgBouncer, kt\u00f3ry kieruje zapytania do aktualnego lidera ka\u017cdego klastra.<\/p>\n<h3>R\u0119czne testowanie<\/h3>\n<p>Przed wdro\u017ceniem na produkcj\u0119 uruchomili\u015bmy t\u0119 schem\u0119 w ma\u0142ym \u015brodowisku testowym i sprawdzili\u015bmy dzia\u0142anie automatycznego prze\u0142\u0105czania. Otwierali\u015bmy tablic\u0119, przesuwali\u015bmy naklejk\u0119 i w tym momencie \u201ezabijali\u015bmy\u201d lidera klastra. W AWS wystarczy wy\u0142\u0105czy\u0107 instancj\u0119 przez konsol\u0119.<\/p>\n<p><img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/d670fe373b774f1b52bfd8a8c3b53a21.png\" alt=\"Odporna na awarie klaster PostgreSQL + Patroni. Do\u015bwiadczenia wdro\u017ceniowe\" \/><\/p>\n<p>Naklejka wraca\u0142a w ci\u0105gu 10-20 sekund, a nast\u0119pnie zaczyna\u0142a ponownie normalnie si\u0119 przesuwa\u0107. Oznacza to, \u017ce klaster Patroni zadzia\u0142a\u0142 prawid\u0142owo: zmieni\u0142 lidera, przekaza\u0142 informacje do Consul, a Consul-template natychmiast przej\u0105\u0142 te informacje, zaktualizowa\u0142 konfiguracj\u0119 PgBouncer i wys\u0142a\u0142 polecenie do prze\u0142adowania.<\/p>\n<h2>Jak przetrwa\u0107 przy wysokim obci\u0105\u017ceniu i zachowa\u0107 minimalny czas przestoju?<\/h2>\n<p>Wszystko dzia\u0142a \u015bwietnie! Ale pojawiaj\u0105 si\u0119 nowe pytania: Jak to zadzia\u0142a przy wysokim obci\u0105\u017ceniu? Jak szybko i bezpiecznie wdro\u017cy\u0107 wszystko na produkcj\u0119?<\/p>\n<p>Odpowied\u017a na pierwsze pytanie daje nam \u015brodowisko testowe, w kt\u00f3rym przeprowadzamy testy obci\u0105\u017ceniowe. Jest ono ca\u0142kowicie identyczne z produkcyjnym pod wzgl\u0119dem architektury i ma wygenerowane dane testowe, kt\u00f3rych obj\u0119to\u015b\u0107 jest mniej wi\u0119cej r\u00f3wna obj\u0119to\u015bci danych produkcyjnych. Postanawiamy po prostu \"zabi\u0107\" jednego z master\u00f3w PostgreSQL podczas testu i zobaczy\u0107, co si\u0119 stanie. Ale przed tym wa\u017cne jest, aby sprawdzi\u0107 automatyczne wypuszczanie, poniewa\u017c w tym \u015brodowisku mamy kilka shard\u00f3w PostgreSQL, wi\u0119c zyskamy doskona\u0142e testowanie skrypt\u00f3w konfiguracyjnych przed produkcj\u0105.<\/p>\n<p>Oba zadania wydaj\u0105 si\u0119 ambitne, ale mamy PostgreSQL 9.6. Mo\u017ce od razu zaktualizujemy do 11.2?<\/p>\n<p>Zdecydowali\u015bmy si\u0119 to zrobi\u0107 w dw\u00f3ch etapach: najpierw zaktualizowa\u0107 wersj\u0119 do 11.2, a potem uruchomi\u0107 Patroni.<\/p>\n<h3>Aktualizacja PostgreSQL<\/h3>\n<p>Aby szybko zaktualizowa\u0107 wersj\u0119 PostgreSQL, nale\u017cy u\u017cy\u0107 opcji <b>-k<\/b>, w kt\u00f3rej tworzone s\u0105 twarde linki na dysku i nie ma potrzeby kopiowania Twoich danych. Na bazach o wielko\u015bci 300-400 GB aktualizacja zajmuje 1 sekund\u0119.<\/p>\n<p>Mamy wiele shard\u00f3w, dlatego aktualizacja musi by\u0107 przeprowadzona automatycznie. W tym celu napisali\u015bmy playbook Ansible, kt\u00f3ry wykonuje ca\u0142y proces aktualizacji za nas:<\/p>\n<pre><code class=\"plaintext\">\/usr\/lib\/postgresql\/11\/bin\/pg_upgrade \n&lt;b&gt;--link &lt;\/b&gt;\n--old-datadir=&#039;&#039; --new-datadir=&#039;&#039; \n --old-bindir=&#039;&#039;  --new-bindir=&#039;&#039; \n --old-options=&#039; -c config_file=&#039; \n --new-options=&#039; -c config_file=&#039;<\/code><\/pre>\n<p>Wa\u017cne jest, aby przed rozpocz\u0119ciem aktualizacji wykona\u0107 j\u0105 z parametrem <b>\u2014sprawd\u017a<\/b>, aby upewni\u0107 si\u0119 w mo\u017cliwo\u015bciach aktualizacji. Nasz skrypt r\u00f3wnie\u017c dokonuje wymiany plik\u00f3w konfiguracyjnych na czas aktualizacji. Skrypt zako\u0144czy\u0142 si\u0119 po 30 sekundach, co jest doskona\u0142ym wynikiem.<\/p>\n<h3>Uruchomienie Patroni<\/h3>\n<p>Aby rozwi\u0105za\u0107 drugi problem, wystarczy spojrze\u0107 na konfiguracj\u0119 Patroni. W oficjalnym repozytorium znajduje si\u0119 przyk\u0142ad konfiguracji z initdb, kt\u00f3ry odpowiada za inicjalizacj\u0119 nowej bazy podczas pierwszego uruchomienia Patroni. Ale poniewa\u017c mamy ju\u017c gotow\u0105 baz\u0119, po prostu usun\u0119li\u015bmy ten segment z konfiguracji.<\/p>\n<p>Kiedy zacz\u0119li\u015bmy instalowa\u0107 Patroni na gotowym klastrze PostgreSQL i uruchamia\u0107 go, napotkali\u015bmy nowy problem: oba serwery uruchamia\u0142y si\u0119 jako liderzy. Patroni nic nie wie o wczesnym stanie klastra i pr\u00f3buje uruchomi\u0107 oba serwery jako dwa osobne klastry o tej samej nazwie. Aby rozwi\u0105za\u0107 ten problem, nale\u017cy usun\u0105\u0107 katalog danych na slave:<\/p>\n<pre><code class=\"plaintext\">rm -rf \/var\/lib\/postgresql\/<\/code><\/pre>\n<p><b>Nale\u017cy to zrobi\u0107 tylko na slave!<\/b><\/p>\n<p>Podczas pod\u0142\u0105czania czystej repliki, Patroni wykonuje kopi\u0119 zapasow\u0105 bazy lidera i przywraca j\u0105 na replik\u0119, a nast\u0119pnie synchronizuje aktualny stan na podstawie log\u00f3w wal.<\/p>\n<p>Kolejnym wyzwaniem, z kt\u00f3rym si\u0119 zmierzyli\u015bmy, jest to, \u017ce wszystkie klastry PostgreSQL domy\u015blnie nazywaj\u0105 si\u0119 main. Kiedy ka\u017cdy klaster nic nie wie o innym \u2014 to w porz\u0105dku. Ale gdy chcesz u\u017cywa\u0107 Patroni, wszystkie klastry musz\u0105 mie\u0107 unikaln\u0105 nazw\u0119. Rozwi\u0105zaniem jest zmiana nazwy klastra w konfiguracji PostgreSQL.<\/p>\n<h3>Test obci\u0105\u017ceniowy<\/h3>\n<p>Przeprowadzili\u015bmy test, kt\u00f3ry imituje prac\u0119 u\u017cytkownik\u00f3w na tablicach. Gdy obci\u0105\u017cenie osi\u0105gn\u0119\u0142o nasz\u0105 \u015bredni\u0105 dzienn\u0105 warto\u015b\u0107, powt\u00f3rzyli\u015bmy dok\u0142adnie ten sam test, wy\u0142\u0105czaj\u0105c jedn\u0105 instancj\u0119 z liderem PostgreSQL. Automatyczny failover zadzia\u0142a\u0142 zgodnie z naszymi oczekiwaniami: Patroni zmieni\u0142 lidera, Consul-template zaktualizowa\u0142 konfiguracj\u0119 PgBouncer i wys\u0142a\u0142 polecenie do reload. Na naszych wykresach w Grafanie by\u0142o wida\u0107 op\u00f3\u017anienia trwaj\u0105ce od 20 do 30 sekund oraz niewielk\u0105 liczb\u0119 b\u0142\u0119d\u00f3w zwi\u0105zanych z po\u0142\u0105czeniem z baz\u0105. To normalna sytuacja, takie warto\u015bci s\u0105 dopuszczalne dla naszego failover i s\u0105 zdecydowanie lepsze ni\u017c przestoje serwisu.<\/p>\n<h2>Wdro\u017cenie Patroni w produkcji<\/h2>\n<p>Ostatecznie otrzymali\u015bmy nast\u0119puj\u0105cy plan:<\/p>\n<ul>\n<li>Wdro\u017cenie Consul-template na serwery PgBouncer i uruchomienie;<\/li>\n<li>Aktualizacja PostgreSQL do wersji 11.2;<\/li>\n<li>Zmiana nazwy klastra;<\/li>\n<li>Uruchomienie klastra Patroni.<\/li>\n<\/ul>\n<p>Nasza schemat pozwala na realizacj\u0119 pierwszego punktu praktycznie w dowolnym momencie, mo\u017cemy kolejno wy\u0142\u0105cza\u0107 ka\u017cdy PgBouncer z pracy i przeprowadza\u0107 na nim wdro\u017cenie oraz uruchomienie consul-template. Tak w\u0142a\u015bnie zrobili\u015bmy.<\/p>\n<p>Do szybkiego wdro\u017cenia u\u017cyli\u015bmy Ansible, poniewa\u017c wszystkie playbooki przetestowali\u015bmy ju\u017c w \u015brodowisku testowym, a czas wykonania pe\u0142nego scenariusza wynosi\u0142 od 1,5 do 2 minut dla ka\u017cdego shardu. Mogli\u015bmy wprowadza\u0107 zmiany po kolei na ka\u017cdym sharde bez zatrzymywania naszego serwisu, ale musieliby\u015bmy na kilka minut wy\u0142\u0105czy\u0107 ka\u017cdy PostgreSQL. W takiej sytuacji u\u017cytkownicy, kt\u00f3rzy maj\u0105 dane na tym shardzie, nie mogliby normalnie pracowa\u0107 w tym czasie, co jest dla nas nieakceptowalne.<\/p>\n<p>Wyj\u015bciem z tej sytuacji by\u0142 planowy maintenance, kt\u00f3ry odbywa si\u0119 co 3 miesi\u0105ce. To okno na prace konserwacyjne, kiedy ca\u0142kowicie wy\u0142\u0105czamy nasz serwis i aktualizujemy instancje baz danych. Do kolejnego okna pozosta\u0142 tydzie\u0144, postanowili\u015bmy wi\u0119c po prostu poczeka\u0107 i dodatkowo si\u0119 przygotowa\u0107. W czasie oczekiwania dodatkowo si\u0119 zabezpieczyli\u015bmy: dla ka\u017cdego shardu PostgreSQL uruchomili\u015bmy zapasow\u0105 replik\u0119 na wypadek awarii, aby zachowa\u0107 najnowsze dane, oraz dodali\u015bmy now\u0105 instancj\u0119 dla ka\u017cdego shardu, kt\u00f3ra ma sta\u0107 si\u0119 now\u0105 replik\u0105 w klastrze Patroni, aby nie wykonywa\u0107 polecenia do usuni\u0119cia danych. Wszystko to pomog\u0142o maksymalnie zredukowa\u0107 ryzyko b\u0142\u0119du.<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/29e5c5f50dbfebfe5665fa0aeb009728.png\" alt=\"Odporna na awarie klaster PostgreSQL + Patroni. Do\u015bwiadczenia wdro\u017ceniowe\" \/><\/p>\n<p>Uruchomili\u015bmy nasz serwis ponownie, wszystko dzia\u0142a\u0142o jak nale\u017cy, u\u017cytkownicy kontynuowali prac\u0119, ale na wykresach zauwa\u017cyli\u015bmy nienaturalnie wysokie obci\u0105\u017cenie serwer\u00f3w Consul.<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/af0eef9ac235706c0aac62d2e1ee179d.png\" alt=\"Odporna na awarie klaster PostgreSQL + Patroni. Do\u015bwiadczenia wdro\u017ceniowe\" \/><\/p>\n<p>Dlaczego nie zauwa\u017cyli\u015bmy tego w \u015brodowisku testowym? Problem ten doskonale ilustruje, \u017ce nale\u017cy przestrzega\u0107 zasady Infrastructure as code i rozwija\u0107 ca\u0142\u0105 infrastruktur\u0119, zaczynaj\u0105c od \u015brodowisk testowych, a ko\u0144cz\u0105c na produkcji. W przeciwnym razie bardzo \u0142atwo napotka\u0107 taki problem, jaki mieli\u015bmy. Co si\u0119 sta\u0142o? Consul najpierw pojawi\u0142 si\u0119 w produkcji, a potem w \u015brodowiskach testowych, w rezultacie w \u015brodowiskach testowych wersja Consul by\u0142a wy\u017csza ni\u017c w produkcji. W\u0142a\u015bnie w jednej z wersji rozwi\u0105zano wyciek CPU podczas pracy z consul-template. Dlatego po prostu zaktualizowali\u015bmy Consul, w ten spos\u00f3b rozwi\u0105zuj\u0105c problem.<\/p>\n<h3>Restartuj klaster Patroni<\/h3>\n<p>Jednak napotkali\u015bmy nowy problem, o kt\u00f3rym nawet nie mieli\u015bmy poj\u0119cia. Podczas aktualizacji Consul po prostu usuwamy w\u0119ze\u0142 Consul z klastra za pomoc\u0105 polecenia consul leave \u2192 Patroni \u0142\u0105czy si\u0119 z innym serwerem Consul \u2192 wszystko dzia\u0142a. Ale kiedy dotarli\u015bmy do ostatniej instancji klastra Consul i wys\u0142ali\u015bmy mu polecenie consul leave, wszystkie klastry Patroni po prostu si\u0119 zrestartowa\u0142y, a w logach zobaczyli\u015bmy nast\u0119puj\u0105cy b\u0142\u0105d:<\/p>\n<pre><code class=\"plaintext\">B\u0141\u0104D: get_cluster\n\u015alad stosu (ostatnie wywo\u0142anie ostatnie):\n...\nRetryFailedError: &#039;Przekroczono termin ponownego pr&oacute;by&#039;\nB\u0141\u0104D: B\u0142\u0105d w komunikacji z DCS\n&lt;b&gt;LOG: system bazy danych zosta\u0142 zamkni\u0119ty&lt;\/b&gt;<\/code><\/pre>\n<p>Klaster Patroni nie m\u00f3g\u0142 uzyska\u0107 informacji o swoim klastrze i zrestartowa\u0142 si\u0119.<\/p>\n<p>Aby znale\u017a\u0107 rozwi\u0105zanie, skontaktowali\u015bmy si\u0119 z autorami Patroni poprzez zg\u0142oszenie na Githubie. Zaproponowali poprawki naszych plik\u00f3w konfiguracyjnych:<\/p>\n<pre><code class=\"python\">consul:\n consul.checks: []\nbootstrap:\n dcs:\n   retry_timeout: 8<\/code><\/pre>\n<p>Uda\u0142o nam si\u0119 powt\u00f3rzy\u0107 problem w \u015brodowisku testowym i przetestowa\u0107 te parametry, ale niestety nie zadzia\u0142a\u0142y.<\/p>\n<p>Problem nadal nie jest rozwi\u0105zany. Planujemy spr\u00f3bowa\u0107 nast\u0119puj\u0105cych opcji rozwi\u0105zania:<\/p>\n<ul>\n<li>U\u017cy\u0107 agenta Consul na ka\u017cdym instancie klastra Patroni;<\/li>\n<li>Naprawi\u0107 problem w kodzie.<\/li>\n<\/ul>\n<p>Rozumiemy miejsce wyst\u0105pienia b\u0142\u0119du: prawdopodobnie problem le\u017cy w u\u017cyciu domy\u015blnego timeoutu, kt\u00f3ry nie jest nadpisywany przez plik konfiguracyjny. Po usuni\u0119ciu ostatniego serwera Consul z klastra, ca\u0142y klaster Consul zawiesza si\u0119 na d\u0142u\u017cej ni\u017c sekund\u0119, przez co Patroni nie mo\u017ce uzyska\u0107 stanu klastra i ca\u0142kowicie restartuje ca\u0142y klaster.<\/p>\n<p>Na szcz\u0119\u015bcie nie napotkali\u015bmy wi\u0119cej \u017cadnych b\u0142\u0119d\u00f3w.<\/p>\n<h2>Podsumowanie u\u017cycia Patroni<\/h2>\n<p>Po pomy\u015blnym uruchomieniu Patroni dodali\u015bmy dodatkow\u0105 replik\u0119 w ka\u017cdym klastrze. Teraz w ka\u017cdym klastrze wyst\u0119puje co\u015b w rodzaju kworum: jeden lider i dwie repliki, dla zabezpieczenia na wypadek split-brain podczas prze\u0142\u0105czania.<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/3bb4c0495fea274b04edd2e99b59eacb.png\" alt=\"Odporna na awarie klaster PostgreSQL + Patroni. Do\u015bwiadczenia wdro\u017ceniowe\" \/><\/p>\n<p>Na produkcji Patroni dzia\u0142a od ponad trzech miesi\u0119cy. W tym czasie zd\u0105\u017cy\u0142 nam pom\u00f3c. Niedawno w AWS lider jednego z klastr\u00f3w przesta\u0142 dzia\u0142a\u0107, automatyczny failover zadzia\u0142a\u0142 i u\u017cytkownicy mogli kontynuowa\u0107 prac\u0119. Patroni wykona\u0142 swoje g\u0142\u00f3wne zadanie.<\/p>\n<p><b>Ma\u0142e podsumowanie u\u017cycia Patroni:<\/b><\/p>\n<ul>\n<li>Wygoda zmiany konfiguracji. Wystarczy zmieni\u0107 konfiguracj\u0119 na jednym instancie, a ona zostanie przekazana na ca\u0142y klaster. Je\u015bli do zastosowania nowej konfiguracji potrzebny jest restart, Patroni o tym poinformuje. Patroni mo\u017ce zrestartowa\u0107 ca\u0142y klaster za pomoc\u0105 jednej komendy, co r\u00f3wnie\u017c jest bardzo wygodne.<\/li>\n<li>Automatyczny failover dzia\u0142a i ju\u017c zd\u0105\u017cy\u0142 nam pom\u00f3c.<\/li>\n<li>Aktualizacja PostgreSQL bez przestoj\u00f3w w aplikacji. Najpierw nale\u017cy zaktualizowa\u0107 repliki do nowej wersji, nast\u0119pnie zmieni\u0107 lidera w klastrze Patroni i zaktualizowa\u0107 starego lidera. Przy tym przeprowadzane s\u0105 niezb\u0119dne testy automatycznego failover.<\/li>\n<\/ul>\n<p>\u0179r\u00f3d\u0142o: <a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/miro\/blog\/457326\/\">habr.com<\/a><\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u0412 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443, \u043a\u0430\u043a \u043c\u044b \u043f\u043e\u0434\u043e\u0448\u043b\u0438 \u043a \u0432\u043e\u043f\u0440\u043e\u0441\u0443 \u043e\u0442\u043a\u0430\u0437\u043e\u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432\u043e\u0441\u0442\u0438 PostgreSQL, \u043f\u043e\u0447\u0435\u043c\u0443 \u044d\u0442\u043e \u0441\u0442\u0430\u043b\u043e \u0434\u043b\u044f \u043d\u0430\u0441 \u0432\u0430\u0436\u043d\u043e \u0438 \u0447\u0442\u043e \u0432 \u0438\u0442\u043e\u0433\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c. \u0423 \u043d\u0430\u0441 \u0432\u044b\u0441\u043e\u043a\u043e\u043d\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0438\u0441: 2,5 \u043c\u043b\u043d \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u043f\u043e \u0432\u0441\u0435\u043c\u0443 \u043c\u0438\u0440\u0443, 50\u041a+ \u0430\u043a\u0442\u0438\u0432\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u043a\u0430\u0436\u0434\u044b\u0439 \u0434\u0435\u043d\u044c. \u0421\u0435\u0440\u0432\u0435\u0440\u0430 \u043d\u0430\u0445\u043e\u0434\u044f\u0442\u0441\u044f \u0432 Amazone \u0432 \u043e\u0434\u043d\u043e\u043c \u0440\u0435\u0433\u0438\u043e\u043d\u0435 \u0418\u0440\u043b\u0430\u043d\u0434\u0438\u0438: \u0432 \u0440\u0430\u0431\u043e\u0442\u0435 \u043f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u043e 100+ \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u0432, \u0438\u0437 \u043d\u0438\u0445 \u043f\u043e\u0447\u0442\u0438 50 [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":26749,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-35706","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.1.1 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u0412 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443, \u043a\u0430\u043a \u043c\u044b \u043f\u043e\u0434\u043e\u0448\u043b\u0438 \u043a \u0432\u043e\u043f\u0440\u043e\u0441\u0443 \u043e\u0442\u043a\u0430\u0437\u043e\u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432\u043e\u0441\u0442\u0438 PostgreSQL, \u043f\u043e\u0447\u0435\u043c\u0443 \u044d\u0442\u043e \u0441\u0442\u0430\u043b\u043e \u0434\u043b\u044f \u043d\u0430\u0441 \u0432\u0430\u0436\u043d\u043e \u0438 \u0447\u0442\u043e \u0432 \u0438\u0442\u043e\u0433\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/pl\/blog\/administrirovanie\/otkazoustojchivyj-klaster-postgresql-patroni-opyt-vnedreniya\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.1.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"pl_PL\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47\u041e\u0442\u043a\u0430\u0437\u043e\u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432\u044b\u0439 \u043a\u043b\u0430\u0441\u0442\u0435\u0440 PostgreSQL + Patroni. \u041e\u043f\u044b\u0442 \u0432\u043d\u0435\u0434\u0440\u0435\u043d\u0438\u044f | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u0412 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443, \u043a\u0430\u043a \u043c\u044b \u043f\u043e\u0434\u043e\u0448\u043b\u0438 \u043a \u0432\u043e\u043f\u0440\u043e\u0441\u0443 \u043e\u0442\u043a\u0430\u0437\u043e\u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432\u043e\u0441\u0442\u0438 PostgreSQL, \u043f\u043e\u0447\u0435\u043c\u0443 \u044d\u0442\u043e \u0441\u0442\u0430\u043b\u043e \u0434\u043b\u044f \u043d\u0430\u0441 \u0432\u0430\u0436\u043d\u043e \u0438 \u0447\u0442\u043e \u0432 \u0438\u0442\u043e\u0433\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/pl\/blog\/administrirovanie\/otkazoustojchivyj-klaster-postgresql-patroni-opyt-vnedreniya\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2019-10-31T19:05:50+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2026-05-18T18:58:46+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47Odporna na awarie klaster PostgreSQL + Patroni. Do\u015bwiadczenia z wdro\u017cenia | ProHoster","description":"W artykule opowiem, jak podeszli\u015bmy do kwestii odporno\u015bci PostgreSQL, dlaczego sta\u0142o si\u0119 to dla nas wa\u017cne i co ostatecznie osi\u0105gn\u0119li\u015bmy.","canonical_url":"https:\/\/prohoster.info\/pl\/blog\/administrirovanie\/otkazoustojchivyj-klaster-postgresql-patroni-opyt-vnedreniya","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"pl_PL","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47\u041e\u0442\u043a\u0430\u0437\u043e\u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432\u044b\u0439 \u043a\u043b\u0430\u0441\u0442\u0435\u0440 PostgreSQL + Patroni. \u041e\u043f\u044b\u0442 \u0432\u043d\u0435\u0434\u0440\u0435\u043d\u0438\u044f | ProHoster","og:description":"\u0412 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443, \u043a\u0430\u043a \u043c\u044b \u043f\u043e\u0434\u043e\u0448\u043b\u0438 \u043a \u0432\u043e\u043f\u0440\u043e\u0441\u0443 \u043e\u0442\u043a\u0430\u0437\u043e\u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432\u043e\u0441\u0442\u0438 PostgreSQL, \u043f\u043e\u0447\u0435\u043c\u0443 \u044d\u0442\u043e \u0441\u0442\u0430\u043b\u043e \u0434\u043b\u044f \u043d\u0430\u0441 \u0432\u0430\u0436\u043d\u043e \u0438 \u0447\u0442\u043e \u0432 \u0438\u0442\u043e\u0433\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c.","og:url":"https:\/\/prohoster.info\/pl\/blog\/administrirovanie\/otkazoustojchivyj-klaster-postgresql-patroni-opyt-vnedreniya","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2019-10-31T19:05:50+00:00","article:modified_time":"2026-05-18T18:58:46+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"35706","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":"2026-01-22 00:26:19","breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-02-28 13:51:06","updated":"2026-01-22 00:26:19","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/posts\/35706","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/comments?post=35706"}],"version-history":[{"count":1,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/posts\/35706\/revisions"}],"predecessor-version":[{"id":172654,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/posts\/35706\/revisions\/172654"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/media\/26749"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/media?parent=35706"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/categories?post=35706"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/tags?post=35706"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}