{"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\/de\/blog\/administrirovanie\/otkazoustojchivyj-klaster-postgresql-patroni-opyt-vnedreniya","title":{"rendered":"Hochverf\u00fcgbarer PostgreSQL-Cluster + Patroni. Unsere Erfahrungen mit der Implementierung","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>In diesem Artikel erkl\u00e4re ich, wie wir das Thema Hochverf\u00fcgbarkeit bei PostgreSQL angegangen sind, warum es f\u00fcr uns wichtig wurde und was letztendlich das Ergebnis war.<\/p>\n<p>Wir betreiben einen stark ausgelasteten Service: 2,5 Millionen Nutzer weltweit, \u00fcber 50.000 aktive Nutzer t\u00e4glich. Die Server befinden sich in einer Amazon-Region in Irland: st\u00e4ndig sind \u00fcber 100 verschiedene Server in Betrieb, davon fast 50 mit Datenbanken.<\/p>\n<p>Unser gesamter Backend besteht aus einer gro\u00dfen monolithischen stateful Anwendung in Java, die eine permanente WebSocket-Verbindung zum Client h\u00e4lt. Wenn mehrere Nutzer gleichzeitig an einem Board arbeiten, sehen sie alle \u00c4nderungen in Echtzeit, da wir jede \u00c4nderung in der Datenbank festhalten. Wir verarbeiten etwa 10.000 Anfragen pro Sekunde an unsere Datenbanken. In Spitzenzeiten schreiben wir in Redis 80.000 bis 100.000 Anfragen pro Sekunde.<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/443f85815b0560fade7db5b639942935.png\" alt=\"Hochverf\u00fcgbarer PostgreSQL-Cluster + Patroni. Unsere Erfahrungen mit der Implementierung\" \/><br \/>\n<a rel=\"nofollow\" name=\"habracut\"><\/a><\/p>\n<h2>Warum wir von Redis auf PostgreSQL umgestiegen sind<\/h2>\n<p>Urspr\u00fcnglich arbeitete unser Service mit Redis, einem Key-Value-Speicher, der alle Daten im Arbeitsspeicher h\u00e4lt. <a href=\"https:\/\/prohoster.info\/de\/server\/\">Server<\/a>.<\/p>\n<p>Vorteile von Redis:<\/p>\n<ol>\n<li>Hohe Antwortgeschwindigkeit, da alles im Speicher gehalten wird;<\/li>\n<li>Bequeme Backup- und Replikationsm\u00f6glichkeiten.<\/li>\n<\/ol>\n<p>Nachteile von Redis f\u00fcr uns:<\/p>\n<ol>\n<li>Es gibt keine echten Transaktionen. Wir haben versucht, sie auf Anwendungsebene zu simulieren. Leider hat das nicht immer gut funktioniert und erforderte das Schreiben von sehr komplexem Code.<\/li>\n<li>Das Datenvolumen ist durch den verf\u00fcgbaren Speicher begrenzt. Wenn die Datenmenge zunimmt, w\u00e4chst auch der Speicher, und schlie\u00dflich sto\u00dfen wir an die Grenzen des ausgew\u00e4hlten Instanztyps, was in AWS einen Stopp unseres Dienstes zur \u00c4nderung des Instanztyps erforderlich macht.<\/li>\n<li>Es ist wichtig, ein niedriges Latency-Niveau aufrechtzuerhalten, da wir eine sehr hohe Anzahl an Anfragen haben. Das optimale Latency-Niveau f\u00fcr uns liegt bei 17-20 ms. Bei einem Wert von 30-40 ms erhalten wir lange Antwortzeiten auf die Anfragen unserer Anwendung, was zu einer Beeintr\u00e4chtigung des Dienstes f\u00fchrt. Leider ist uns das im September 2018 passiert, als eine unserer Redis-Instanzen aus unerkl\u00e4rlichen Gr\u00fcnden eine doppelt so hohe Latenz aufwies. Um das Problem zu l\u00f6sen, haben wir den Dienst mitten am Arbeitstag f\u00fcr eine au\u00dferplanm\u00e4\u00dfige Wartung gestoppt und die betroffene Redis-Instanz ersetzt.<\/li>\n<li>Es ist leicht, Inkonsistenzen in den Daten zu erhalten, selbst bei geringf\u00fcgigen Fehlern im Code, und dann viel Zeit mit dem Schreiben von Code zur Behebung dieser Datenverschmutzung zu verbringen.<\/li>\n<\/ol>\n<p>Wir haben die Herausforderungen erkannt und verstanden, dass wir auf eine benutzerfreundlichere L\u00f6sung mit reibungsloseren Transaktionen und weniger Abh\u00e4ngigkeit von der Latenz umsteigen m\u00fcssen. Nach einer gr\u00fcndlichen Untersuchung und Analyse vieler Optionen haben wir uns f\u00fcr PostgreSQL entschieden.<\/p>\n<p>Seit 1,5 Jahren verlagern wir unsere Daten auf die neue Datenbank und haben bisher nur einen kleinen Teil \u00fcbertragen, weshalb wir derzeit parallel mit Redis und PostgreSQL arbeiten. Mehr \u00fcber die Phasen des Umstiegs und den Datenaustausch zwischen den Datenbanken steht in der <a href=\"https:\/\/habr.com\/ru\/company\/miro\/blog\/437826\/\" rel=\"nofollow\">Artikel meines Kollegen<\/a>.<\/p>\n<p>Als wir mit der Migration begannen, lief unsere Anwendung direkt mit der Datenbank und griff sowohl auf den Master von Redis als auch auf PostgreSQL zu. Der PostgreSQL-Cluster bestand aus einem Master und einer Replica mit asynchroner Replikation. So sah das Schema f\u00fcr die Datenbankverbindungen aus:<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/674dd77de4c8b48a946c9f64c0b2fdd1.png\" alt=\"Hochverf\u00fcgbarer PostgreSQL-Cluster + Patroni. Unsere Erfahrungen mit der Implementierung\" \/><\/p>\n<h2>Implementierung von PgBouncer<\/h2>\n<p>W\u00e4hrend unseres Umzugs entwickelte sich auch das Produkt weiter: Die Anzahl der Benutzer und der mit PostgreSQL betriebenen Server nahm zu, sodass uns die Verbindungen ausgingen. PostgreSQL erstellt f\u00fcr jede Verbindung einen separaten Prozess und verbraucht Ressourcen. Die Anzahl der Verbindungen kann bis zu einem bestimmten Punkt erh\u00f6ht werden, andernfalls besteht die Gefahr, dass die Datenbank nicht optimal arbeitet. In einer solchen Situation ist die Auswahl eines Verbindungspoolers der ideale Weg, der vor der Datenbank platziert wird.<\/p>\n<p>Wir hatten zwei Optionen f\u00fcr den Verbindungspooler: Pgpool und PgBouncer. Der erste unterst\u00fctzt jedoch keinen Transaktionsmodus mit der Datenbank, daher entschieden wir uns f\u00fcr PgBouncer.<\/p>\n<p>Wir haben folgendes Arbeitsmuster eingerichtet: Unsere Anwendung verbindet sich mit einem PgBouncer, hinter dem die Master-PostgreSQL-Server stehen, und hinter jedem Master befindet sich eine Replik mit asynchroner Replikation.<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/dabcb7f6006520c4fec85fb925e8975e.png\" alt=\"Hochverf\u00fcgbarer PostgreSQL-Cluster + Patroni. Unsere Erfahrungen mit der Implementierung\" \/><\/p>\n<p>Dabei konnten wir nicht alle Datenmengen in PostgreSQL speichern, und die Geschwindigkeit der Datenbank war uns wichtig. Daher begannen wir, PostgreSQL auf Anwendungsebene zu sharden. Das oben beschriebene Schema ist relativ praktisch daf\u00fcr: Wenn ein neuer Shard hinzugef\u00fcgt wird, reicht es, die PgBouncer-Konfiguration zu aktualisieren, und die Anwendung kann sofort mit dem neuen Shard arbeiten.<\/p>\n<h3>Ausfallsicherheit von PgBouncer<\/h3>\n<p>Dieses Schema funktionierte bis zu dem Zeitpunkt, als der einzige PgBouncer-Instanz ausfiel. Wir befinden uns in AWS, wo alle Instanzen auf Hardware betrieben werden, die gelegentlich ausf\u00e4llt. In solchen F\u00e4llen wird die Instanz einfach auf neue Hardware \u00fcbertragen und l\u00e4uft wieder. So geschah es auch mit PgBouncer, doch er wurde nicht mehr erreichbar. Das Resultat dieses Ausfalls war, dass unser Dienst f\u00fcr 25 Minuten nicht verf\u00fcgbar war. AWS empfiehlt in solchen Situationen, auf der Benutzerseite Redundanz zu nutzen, was zu diesem Zeitpunkt bei uns nicht implementiert war.<\/p>\n<p>Nach diesem Vorfall haben wir ernsthaft dar\u00fcber nachgedacht, die Ausfallsicherheit von PgBouncer und PostgreSQL-Clustern zu verbessern, da eine \u00e4hnliche Situation mit jeder Instanz in unserem AWS-Konto wieder auftreten k\u00f6nnte.<\/p>\n<p>Wir haben das Failover-Schema f\u00fcr PgBouncer wie folgt aufgebaut: Alle Anwendungsserver greifen auf den Network Load Balancer zu, hinter dem zwei PgBouncer laufen. Jeder PgBouncer \u00fcberwacht dieselben Master-PostgreSQL-Datenbanken jedes Shards. Falls es erneut zu einem Ausfall des AWS-Instances kommt, wird der gesamte Datenverkehr \u00fcber einen anderen PgBouncer umgeleitet. Die Ausfallsicherheit des Network Load Balancers wird von AWS gew\u00e4hrleistet.<\/p>\n<p>Dieses Schema erm\u00f6glicht es, problemlos neue PgBouncer-Server hinzuzuf\u00fcgen.<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/2c1f1e7d9f7be4fdeb43858520d55e36.png\" alt=\"Hochverf\u00fcgbarer PostgreSQL-Cluster + Patroni. Unsere Erfahrungen mit der Implementierung\" \/><\/p>\n<h2>Erstellung eines ausfallsicheren PostgreSQL-Clusters<\/h2>\n<p>Bei der L\u00f6sung dieser Aufgabe haben wir verschiedene Optionen in Betracht gezogen: selbstgeschriebener Failover, repmgr, AWS RDS und Patroni.<\/p>\n<h3>Selbstgeschriebene Skripte<\/h3>\n<p>k\u00f6nnen die Funktion des Masters \u00fcberwachen und im Falle eines Ausfalls die Replikate zum Master bef\u00f6rdern sowie die Konfiguration von PgBouncer aktualisieren.<\/p>\n<p>Die Vorteile dieses Ansatzes liegen in ihrer maximalen Einfachheit, da Sie die Skripte selbst schreiben und genau verstehen, wie sie funktionieren.<\/p>\n<p>Nachteile:<\/p>\n<ul>\n<li>Der Master k\u00f6nnte nicht gestorben sein; stattdessen k\u00f6nnte ein Netzwerkfehler aufgetreten sein. Failover, ohne dar\u00fcber Bescheid zu wissen, wird die Replikation zum Master voranbringen, w\u00e4hrend der alte Master weiterarbeitet. Infolgedessen haben wir zwei Server, die als Master fungieren, und wissen nicht, auf welchem die aktuellsten Daten liegen. Solch eine Situation wird auch als Split-Brain bezeichnet.<\/li>\n<li>Wir sind ohne Replikation geblieben. In unserer Konfiguration gibt es einen Master und eine Replikation; nach dem Failover wird die Replikation zum Master bef\u00f6rdert, und wir haben keine weiteren Replikate mehr. Daher m\u00fcssen wir manuell eine neue Replikation hinzuf\u00fcgen.<\/li>\n<li>Zus\u00e4tzliche \u00dcberwachung des Failovers ist erforderlich, da wir 12 Shards von PostgreSQL haben, was bedeutet, dass wir 12 Cluster \u00fcberwachen m\u00fcssen. Bei einer Erh\u00f6hung der Anzahl der Shards d\u00fcrfen wir nicht vergessen, das Failover zu aktualisieren.<\/li>\n<\/ul>\n<p>Selbstprogrammierter Failover sieht sehr kompliziert aus und erfordert nicht triviale Unterst\u00fctzung. Bei einem einzelnen PostgreSQL-Cluster wird dies die einfachste L\u00f6sung sein, ist aber nicht skalierbar, weshalb es f\u00fcr uns nicht geeignet ist.<\/p>\n<h3>Repmgr<\/h3>\n<p>Replication Manager f\u00fcr PostgreSQL-Cluster, der die Verwaltung eines PostgreSQL-Clusters \u00fcbernimmt. Dabei bietet er jedoch keinen automatischen Failover \"out of the box\", weshalb eine eigene \"Wrapper\"-L\u00f6sung entwickelt werden muss. Das kann sogar komplizierter sein als mit selbstgeschriebenen Skripten, weshalb wir Repmgr nicht einmal ausprobiert haben.<\/p>\n<h3>AWS RDS<\/h3>\n<p>Bietet alles, was wir ben\u00f6tigen, kann Backups erstellen und unterst\u00fctzt einen Connection-Pool. Verf\u00fcgt \u00fcber automatisches Switching: wenn der Master ausf\u00e4llt, wird die Replikation zum neuen Master und AWS aktualisiert den DNS-Eintrag auf den neuen Master, w\u00e4hrend sich die Replikate in verschiedenen AZs befinden k\u00f6nnen.<\/p>\n<p>Ein Nachteil ist das Fehlen feiner Einstellungen. Ein Beispiel f\u00fcr feine Einstellungen: In unseren Instanzen gibt es Einschr\u00e4nkungen f\u00fcr TCP-Verbindungen, die leider in RDS nicht umgesetzt werden k\u00f6nnen:<\/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>Zudem ist der Preis f\u00fcr AWS RDS fast doppelt so hoch wie der regul\u00e4re Preis f\u00fcr Instanzen, was der Hauptgrund f\u00fcr den Verzicht auf diese L\u00f6sung war.<\/p>\n<h3>Patroni<\/h3>\n<p>Dies ist ein Python-Template zur Verwaltung von PostgreSQL mit guter Dokumentation, automatischem Failover und dem Quellcode auf GitHub.<\/p>\n<p>Vorteile von Patroni:<\/p>\n<ul>\n<li>Jeder Konfigurationsparameter ist klar beschrieben, sodass nachvollziehbar ist, wie alles funktioniert.<\/li>\n<li>Der automatische Failover funktioniert out of the box.<\/li>\n<li>In Python geschrieben, und da wir selbst viel in Python programmieren, wird es uns leichter fallen, Probleme zu beheben und m\u00f6glicherweise sogar das Projekt weiterzuentwickeln.<\/li>\n<li>Verwalten Sie PostgreSQL vollst\u00e4ndig, \u00e4ndern Sie die Konfiguration sofort auf allen Knoten im Cluster, und wenn ein Neustart des Clusters f\u00fcr die Anwendung der neuen Konfiguration erforderlich ist, kann dies ebenfalls \u00fcber Patroni erfolgen.<\/li>\n<\/ul>\n<p>Nachteile:<\/p>\n<ul>\n<li>Aus der Dokumentation geht nicht klar hervor, wie man richtig mit PgBouncer arbeitet. Das als Nachteil zu werten, ist allerdings schwierig, da es Patrons Aufgabe ist, PostgreSQL zu verwalten, w\u00e4hrend wir uns um die Anbindung an Patroni k\u00fcmmern m\u00fcssen.<\/li>\n<li>Es gibt nur wenige Implementierungsbeispiele von Patroni in gro\u00dfen Umgebungen, jedoch viele Beispiele f\u00fcr Neubauten.<\/li>\n<\/ul>\n<p>Letztendlich haben wir uns zur Erstellung eines ausfallsicheren Clusters genau f\u00fcr Patroni entschieden.<\/p>\n<h2>Prozess der Implementierung von Patroni<\/h2>\n<p>Vor Patroni hatten wir 12 PostgreSQL-Shards in einer Konfiguration mit einem Master und einem Replikat mit asynchroner Replikation. Die Anwendungsserver kommunizierten \u00fcber einen Network Load Balancer mit den Datenbanken, hinter dem sich zwei Instanzen mit PgBouncer befanden, und dahinter lagen alle PostgreSQL-Server.<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/8e5350d2466311593e11add30d5b1bdc.png\" alt=\"Hochverf\u00fcgbarer PostgreSQL-Cluster + Patroni. Unsere Erfahrungen mit der Implementierung\" \/><\/p>\n<p>F\u00fcr die Implementierung von Patroni mussten wir ein verteiltes Konfigurationsspeicher f\u00fcr den Cluster w\u00e4hlen. Patroni funktioniert mit verteilten Konfigurationsspeichersystemen wie etcd, Zookeeper und Consul. Wir haben bereits einen voll funktionsf\u00e4higen Consul-Cluster in der Produktion, der zusammen mit Vault betrieben wird und derzeit nicht anderweitig genutzt wird. Das ist eine ausgezeichnete Gelegenheit, Consul wie vorgesehen zu verwenden.<\/p>\n<h3>Wie funktioniert Patroni mit Consul<\/h3>\n<p>Wir haben einen Consul-Cluster, der aus drei Knoten besteht, und einen Patroni-Cluster, der aus einem F\u00fchrer und Replikaten besteht (im Patroni wird der Master als Clusterf\u00fchrer bezeichnet und die Slaves als Replikate). Jede Instanz des Patroni-Clusters sendet st\u00e4ndig Informationen \u00fcber den Zustand des Clusters an Consul. Daher kann man jederzeit die aktuelle Konfiguration des Patroni-Clusters und den aktuellen F\u00fchrer in Consul einsehen.<\/p>\n<p><img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/3234594b363904424fdce64c9d77fca5.png\" alt=\"Hochverf\u00fcgbarer PostgreSQL-Cluster + Patroni. Unsere Erfahrungen mit der Implementierung\" \/><\/p>\n<p>Um Patroni mit Consul zu verbinden, gen\u00fcgt es, die offizielle Dokumentation zu studieren, aus der hervorgeht, dass der Host im Format http oder https angegeben werden muss, je nachdem, wie wir mit Consul arbeiten, sowie das Verbindungsprotokoll optional:<\/p>\n<pre><code class=\"plaintext\">host: der host:port f\u00fcr den Consul-Endpunkt im Format: http(s):\/\/host:port\nscheme: (optional) http oder https, standardm\u00e4\u00dfig http<\/code><\/pre>\n<p>Es sieht einfach aus, aber hier beginnen die Fallstricke. Mit Consul arbeiten wir \u00fcber eine gesicherte Verbindung via https, und unsere Verbindungs-Konfiguration wird folgenderma\u00dfen aussehen:<\/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>Doch so funktioniert es nicht. Beim Start kann Patroni sich nicht mit Consul verbinden, da es trotzdem versucht, \u00fcber http zu gehen.<\/p>\n<p>Das Verst\u00e4ndnis des Problems wurde durch den Quellcode von Patroni unterst\u00fctzt. Zum Gl\u00fcck ist er in Python geschrieben. Es stellt sich heraus, dass der Parameter host nicht geparsed wird, und das Protokoll muss in der scheme angegeben werden. So sieht der funktionierende Konfigurationsblock f\u00fcr die Arbeit mit Consul aus:<\/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>Also, wir haben ein Speicherort f\u00fcr die Konfiguration ausgew\u00e4hlt. Jetzt m\u00fcssen wir verstehen, wie PgBouncer seine Konfiguration beim Wechsel des Leaders im Patroni-Cluster umschaltet. In der Dokumentation gibt es darauf keine Antwort, da die Arbeit mit PgBouncer dort grunds\u00e4tzlich nicht beschrieben wird.<\/p>\n<p>Auf der Suche nach einer L\u00f6sung fanden wir einen Artikel (den Titel erinnere ich leider nicht), in dem stand, dass Consul-Template sehr hilfreich in der Verbindung zwischen PgBouncer und Patroni war. Das hat uns dazu angeregt, die Funktionsweise von Consul-Template zu erforschen.<\/p>\n<p>Es stellte sich heraus, dass der Consul-Template kontinuierlich die Konfiguration des PostgreSQL-Clusters in Consul \u00fcberwacht. Bei einem Leader-Wechsel aktualisiert er die PgBouncer-Konfiguration und sendet den Befehl zur Neuladung.<\/p>\n<p><img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/6cf92996a127bb6637ab81dc45fdd60a.png\" alt=\"Hochverf\u00fcgbarer PostgreSQL-Cluster + Patroni. Unsere Erfahrungen mit der Implementierung\" \/><\/p>\n<p>Ein gro\u00dfer Vorteil des Templates besteht darin, dass es in Codeform gespeichert wird. Daher gen\u00fcgt es bei der Hinzuf\u00fcgung eines neuen Shards, einen neuen Commit zu machen und das Template automatisch zu aktualisieren, wodurch das Prinzip \"Infrastructure as Code\" unterst\u00fctzt wird.<\/p>\n<h3>Neue Architektur mit Patroni<\/h3>\n<p>Das Resultat ist folgendes Arbeitsdiagramm:<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/bf9a9eff675a26039e3f76b16c6cd713.png\" alt=\"Hochverf\u00fcgbarer PostgreSQL-Cluster + Patroni. Unsere Erfahrungen mit der Implementierung\" \/><\/p>\n<p>Alle Anwendungsserver wenden sich an den Lastenausgleich \u2192 dahinter stehen zwei PgBouncer-Instanzen \u2192 auf jeder Instanz l\u00e4uft der Consul-Template, der den Zustand jedes Patroni-Clusters \u00fcberwacht und sicherstellt, dass die PgBouncer-Konfiguration aktuell ist, die die Anfragen an den aktuellen Leader jedes Clusters weiterleitet.<\/p>\n<h3>Manuelles Testing<\/h3>\n<p>Diese Struktur haben wir vor dem Rollout in Produktion in einer kleinen Testumgebung getestet und die Funktionalit\u00e4t des automatischen Failovers \u00fcberpr\u00fcft. Wir haben das Board ge\u00f6ffnet, einen Sticker verschoben und in diesem Moment den Leader des Clusters \"get\u00f6tet\". Bei AWS gen\u00fcgt es, die Instanz \u00fcber die Konsole auszuschalten.<\/p>\n<p><img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/d670fe373b774f1b52bfd8a8c3b53a21.png\" alt=\"Hochverf\u00fcgbarer PostgreSQL-Cluster + Patroni. Unsere Erfahrungen mit der Implementierung\" \/><\/p>\n<p>Der Sticker kam innerhalb von 10-20 Sekunden zur\u00fcck, bevor er wieder normal zu bewegen begann. Das bedeutet, der Patroni-Cluster hat korrekt funktioniert: der Leader wurde gewechselt, die Informationen wurden an Consul gesendet, und das Consul-Template hat diese Informationen sofort aufgegriffen, die PgBouncer-Konfiguration ersetzt und den Reload-Befehl gesendet.<\/p>\n<h2>Wie \u00fcberlebt man unter hoher Last und minimiert Ausfallzeiten?<\/h2>\n<p>Alles funktioniert hervorragend! Aber neue Fragen tauchen auf: Wie wird es sich unter hoher Last verhalten? Wie rollt man alles schnell und sicher in die Produktion aus?<\/p>\n<p>Auf die erste Frage hilft uns eine Testumgebung, in der wir Lasttests durchf\u00fchren. Sie ist in Architektur und Testdaten vollst\u00e4ndig identisch mit der Produktion und hat generierte Testdaten, die in etwa der Produktionsgr\u00f6\u00dfe entsprechen. Wir entscheiden uns, w\u00e4hrend des Tests einen der PostgreSQL-Master absichtlich \u201eabzuw\u00fcrgen\u201c und zu beobachten, was passiert. Aber vorher ist es wichtig, das automatische Rollout zu \u00fcberpr\u00fcfen, denn in dieser Umgebung haben wir mehrere PostgreSQL-Shards, sodass wir gro\u00dfartige Tests der Konfigurationsskripte vor der Produktion erhalten.<\/p>\n<p>Beide Aufgaben erscheinen ehrgeizig, aber wir haben PostgreSQL 9.6. Vielleicht aktualisieren wir gleich auf 11.2?<\/p>\n<p>Wir haben beschlossen, dies in zwei Schritten durchzuf\u00fchren: zuerst die Version auf 11.2 zu aktualisieren, dann Patroni zu starten.<\/p>\n<h3>PostgreSQL-Upgrade<\/h3>\n<p>F\u00fcr ein schnelles Upgrade der PostgreSQL-Version sollte die Option <b>-k<\/b>, verwendet werden, bei der Hardlinks auf der Festplatte erstellt werden und kein Kopieren Ihrer Daten erforderlich ist. Bei Datenbanken mit 300-400 GB dauert das Upgrade nur 1 Sekunde.<\/p>\n<p>Wir haben viele Shards, sodass das Upgrade automatisiert erfolgen muss. Daf\u00fcr haben wir ein Ansible-Playbook geschrieben, das den gesamten Upgrade-Prozess f\u00fcr uns durchf\u00fchrt:<\/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>Hier ist es wichtig zu erw\u00e4hnen, dass vor dem Start der Aktualisierung der Parameter <b>\u2014pr\u00fcfen<\/b>verwendet werden muss, um sicherzustellen, dass das Upgrade m\u00f6glich ist. Au\u00dferdem ersetzt unser Skript w\u00e4hrend des Upgrades die Konfigurationsdateien. Unser Skript hat in 30 Sekunden ausgef\u00fchrt, was ein hervorragendes Ergebnis ist.<\/p>\n<h3>Start von Patroni<\/h3>\n<p>Um das zweite Problem zu l\u00f6sen, reicht es, sich die Konfiguration von Patroni anzusehen. Im offiziellen Repository gibt es ein Beispiel f\u00fcr eine Konfiguration mit initdb, das f\u00fcr die Initialisierung einer neuen Datenbank beim ersten Start von Patroni verantwortlich ist. Da wir jedoch bereits eine vorhandene Datenbank haben, haben wir diesen Abschnitt einfach aus der Konfiguration entfernt.<\/p>\n<p>Als wir begonnen haben, Patroni auf einen bestehenden PostgreSQL-Cluster zu installieren und zu starten, sind wir auf ein neues Problem gesto\u00dfen: Beide Server starteten als Leader. Patroni hat keine Ahnung vom vorherigen Zustand des Clusters und versucht, beide Server als zwei separate Cluster mit demselben Namen zu starten. Um dieses Problem zu l\u00f6sen, muss das Datenverzeichnis auf dem Slave gel\u00f6scht werden:<\/p>\n<pre><code class=\"plaintext\">rm -rf \/var\/lib\/postgresql\/<\/code><\/pre>\n<p><b>Dies muss nur auf dem Slave durchgef\u00fchrt werden!<\/b><\/p>\n<p>Beim Verbinden eines neuen Replikats macht Patroni ein Basebackup des Leaders und stellt es auf der Replik wieder her, und holt anschlie\u00dfend den aktuellen Zustand \u00fcber die WAL-Logs ein.<\/p>\n<p>Eine weitere Schwierigkeit, die wir festgestellt haben, ist, dass alle PostgreSQL-Cluster standardm\u00e4\u00dfig \"main\" genannt werden. Wenn jeder Cluster nichts \u00fcber den anderen wei\u00df, ist das in Ordnung. Aber wenn Sie Patroni verwenden m\u00f6chten, m\u00fcssen alle Cluster einen einzigartigen Namen haben. Die L\u00f6sung besteht darin, den Clusternamen in der PostgreSQL-Konfiguration zu \u00e4ndern.<\/p>\n<h3>Lasttest<\/h3>\n<p>Wir haben einen Test gestartet, der das Nutzerverhalten auf Boards simuliert. Als die Last unser durchschnittliches Tagesniveau erreichte, wiederholten wir denselben Test und schalteten eine Instanz mit dem f\u00fchrenden PostgreSQL aus. Der automatische Failover funktionierte wie erwartet: Patroni wechselte den Leader, Consul-Template aktualisierte die PgBouncer-Konfiguration und gab den Reload-Befehl. Unsere Grafiken in Grafana zeigten Verz\u00f6gerungen von 20-30 Sekunden und eine geringe Anzahl von Fehlern bei der Verbindung mit der Datenbank. Das ist eine normale Situation, solche Werte sind f\u00fcr unseren Failover zul\u00e4ssig und eindeutig besser als Ausfallzeiten des Dienstes.<\/p>\n<h2>Patroni in der Produktion<\/h2>\n<p>Somit haben wir den folgenden Plan erstellt:<\/p>\n<ul>\n<li>Deployment von Consul-Template auf die PgBouncer-Server und Start;<\/li>\n<li>Aktualisierung von PostgreSQL auf Version 11.2;<\/li>\n<li>\u00c4nderung des Cluster-Namens;<\/li>\n<li>Start des Patroni-Clusters.<\/li>\n<\/ul>\n<p>Unser Schema erm\u00f6glicht es uns, den ersten Punkt praktisch jederzeit durchzuf\u00fchren. Wir k\u00f6nnen nacheinander jeden PgBouncer au\u00dfer Betrieb nehmen und das Deployment sowie den Start von Consul-Template darauf ausf\u00fchren. Genau das haben wir getan.<\/p>\n<p>F\u00fcr eine schnelle Bereitstellung haben wir Ansible verwendet, da wir alle Playbooks bereits in der Testumgebung gepr\u00fcft hatten, und die Ausf\u00fchrungszeit des gesamten Szenarios betrug zwischen 1,5 und 2 Minuten f\u00fcr jeden Shard. Wir konnten alles nacheinander auf jeden Shard ausrollen, ohne unseren Dienst zu unterbrechen, m\u00fcssten jedoch jeden PostgreSQL f\u00fcr mehrere Minuten abschalten. In diesem Fall k\u00f6nnten die Benutzer, deren Daten auf diesem Shard gespeichert sind, w\u00e4hrenddessen nicht richtig arbeiten, was f\u00fcr uns inakzeptabel w\u00e4re.<\/p>\n<p>Die L\u00f6sung f\u00fcr diese Situation war eine planm\u00e4\u00dfige Wartung, die alle 3 Monate bei uns stattfindet. Dies ist ein Fenster f\u00fcr geplante Arbeiten, in dem wir unseren Dienst vollst\u00e4ndig abschalten und die Datenbankinstanzen aktualisieren. Eine Woche vor dem n\u00e4chsten Wartungsfenster haben wir uns entschieden, einfach zu warten und uns zus\u00e4tzlich vorzubereiten. W\u00e4hrend der Wartezeit haben wir zus\u00e4tzliche Vorkehrungen getroffen: F\u00fcr jeden PostgreSQL-Shard haben wir eine Backup-Replikation erstellt, um die aktuellsten Daten im Falle eines Fehlers zu sichern, und f\u00fcr jeden Shard eine neue Instanz hinzugef\u00fcgt, die zur neuen Replik im Patroni-Cluster werden sollte, um den Befehl zur Datenl\u00f6schung zu vermeiden. All dies half, das Risiko eines Fehlers zu minimieren.<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/29e5c5f50dbfebfe5665fa0aeb009728.png\" alt=\"Hochverf\u00fcgbarer PostgreSQL-Cluster + Patroni. Unsere Erfahrungen mit der Implementierung\" \/><\/p>\n<p>Wir haben unseren Service neu gestartet, alles funktioniert wie gew\u00fcnscht, die Benutzer konnten weiterarbeiten, jedoch haben wir an den Grafiken eine anomal hohe Last auf den Consul-Servern bemerkt.<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/af0eef9ac235706c0aac62d2e1ee179d.png\" alt=\"Hochverf\u00fcgbarer PostgreSQL-Cluster + Patroni. Unsere Erfahrungen mit der Implementierung\" \/><\/p>\n<p>Warum haben wir das in der Testumgebung nicht bemerkt? Dieses Problem verdeutlicht sehr gut, wie wichtig es ist, dem Prinzip \"Infrastructure as Code\" zu folgen und die gesamte Infrastruktur von den Testumgebungen bis hin zur Produktionsumgebung weiterzuentwickeln. Andernfalls kann man leicht auf das Problem sto\u00dfen, das uns passiert ist. Was geschah? Consul wurde zun\u00e4chst in der Produktionsumgebung eingef\u00fchrt, bevor es auf den Testumgebungen auftauchte. Schlie\u00dflich war in den Testumgebungen die Version von Consul h\u00f6her als in der Produktionsumgebung. In einer der Versionen wurde ein CPU-Leck bei der Arbeit mit consul-template behoben. Daher haben wir Consul einfach aktualisiert, um das Problem zu l\u00f6sen.<\/p>\n<h3>Patroni-Cluster neu starten<\/h3>\n<p>Aber wir stie\u00dfen auf ein neues Problem, von dem wir nicht einmal wussten. Beim Aktualisieren von Consul entfernen wir einfach den Consul-Knoten aus dem Cluster mit dem Befehl consul leave \u2192 Patroni verbindet sich mit einem anderen Consul-Server \u2192 alles funktioniert. Aber als wir zum letzten Instanz des Consul-Clusters kamen und ihm den Befehl consul leave gaben, starteten alle Patroni-Cluster einfach neu, und in den Logs sahen wir folgenden Fehler:<\/p>\n<pre><code class=\"plaintext\">FEHLER: get_cluster\nVerlauf (letzter Aufruf zuletzt):\n...\nRetryFailedError: &#039;Zeit&uuml;berschreitung der Wiederholungsfrist &uuml;berschritten&#039;\nFEHLER: Fehler bei der Kommunikation mit DCS\n&lt;b&gt;PROTOKOLL: Datenbanksystem wurde heruntergefahren&lt;\/b&gt;<\/code><\/pre>\n<p>Der Patroni-Cluster konnte keine Informationen \u00fcber seinen Cluster abrufen und wurde neu gestartet.<\/p>\n<p>Um eine L\u00f6sung zu finden, haben wir die Autoren von Patroni \u00fcber ein Issue auf GitHub kontaktiert. Sie haben uns Verbesserungen f\u00fcr unsere Konfigurationsdateien vorgeschlagen:<\/p>\n<pre><code class=\"python\">consul:\n consul.checks: []\nbootstrap:\n dcs:\n   retry_timeout: 8<\/code><\/pre>\n<p>Wir konnten das Problem in einer Testumgebung reproduzieren und diese Parameter dort testen, aber leider haben sie nicht funktioniert.<\/p>\n<p>Das Problem bleibt weiterhin ungel\u00f6st. Wir planen, die folgenden L\u00f6sungsans\u00e4tze auszuprobieren:<\/p>\n<ul>\n<li>Den Consul-Agenten auf jeder Instanz des Patroni-Clusters zu verwenden;<\/li>\n<li>Das Problem im Code zu beheben.<\/li>\n<\/ul>\n<p>Wir verstehen, wo der Fehler auftritt: Wahrscheinlich liegt das Problem in der Verwendung des Standard-Timeouts, das nicht durch die Konfigurationsdatei \u00fcberschrieben wird. Wenn der letzte Consul-Server aus dem Cluster entfernt wird, h\u00e4ngt der gesamte Consul-Cluster f\u00fcr mehr als eine Sekunde, weshalb Patroni den Clusterzustand nicht abrufen kann und den gesamten Cluster neu startet.<\/p>\n<p>Gl\u00fccklicherweise haben wir keine weiteren Fehler festgestellt.<\/p>\n<h2>Fazit zur Verwendung von Patroni<\/h2>\n<p>Nach dem erfolgreichen Start von Patroni haben wir in jedem Cluster eine zus\u00e4tzliche Replik hinzugef\u00fcgt. Jetzt gibt es in jedem Cluster eine Art Quorum: einen F\u00fchrer und zwei Replikate, um im Falle eines Split-Brain bei einem Failover abzusichern.<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/3bb4c0495fea274b04edd2e99b59eacb.png\" alt=\"Hochverf\u00fcgbarer PostgreSQL-Cluster + Patroni. Unsere Erfahrungen mit der Implementierung\" \/><\/p>\n<p>In der Produktion l\u00e4uft Patroni bereits seit \u00fcber drei Monaten. In dieser Zeit hat er uns schon mehrfach geholfen. K\u00fcrzlich ist in AWS der F\u00fchrer eines unserer Cluster ausgefallen, der automatische Failover hat funktioniert und die Benutzer konnten weiterarbeiten. Patroni hat seine Hauptaufgabe erf\u00fcllt.<\/p>\n<p><b>Ein kleines Res\u00fcmee zur Nutzung von Patroni:<\/b><\/p>\n<ul>\n<li>Die Konfigurations\u00e4nderung ist einfach. Es gen\u00fcgt, die Konfiguration auf einer Instanz zu \u00e4ndern, und sie wird im gesamten Cluster \u00fcbernommen. Wenn ein Neustart zur Anwendung der neuen Konfiguration erforderlich ist, informiert Patroni dar\u00fcber. Patroni kann den gesamten Cluster mit einem Befehl neu starten, was ebenfalls sehr praktisch ist.<\/li>\n<li>Der automatische Failover funktioniert und hat uns bereits geholfen.<\/li>\n<li>PostgreSQL-Updates ohne Ausfallzeiten der Anwendung. Zuerst m\u00fcssen die Replikate auf die neue Version aktualisiert werden, dann wird der F\u00fchrer im Patroni-Cluster gewechselt und der alte F\u00fchrer aktualisiert. Dabei erfolgt das notwendige Testen des automatischen Failovers.<\/li>\n<\/ul>\n<p>Quelle: <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 4.9.10 - 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. \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\" \/>\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\/de\/blog\/administrirovanie\/otkazoustojchivyj-klaster-postgresql-patroni-opyt-vnedreniya\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 4.9.10\" \/>\n\t\t<meta property=\"og:locale\" content=\"de_DE\" \/>\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. \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\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/de\/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\udd47Hochverf\u00fcgbarer PostgreSQL-Cluster + Patroni. Implementierungserfahrungen | ProHoster","description":"In diesem Artikel erkl\u00e4re ich, wie wir das Thema Hochverf\u00fcgbarkeit von PostgreSQL angegangen sind, warum uns das wichtig wurde und was letztendlich herausgekommen ist. Wir betreiben einen stark frequentierten Service mit 2,5 Millionen Nutzern weltweit und \u00fcber 50.000 aktiven Nutzern t\u00e4glich. Unsere Server stehen in Amazon in einem Rechenzentrum in Irland: Im Betrieb sind permanent \u00fcber 100 verschiedene Server, davon fast 50.","canonical_url":"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/otkazoustojchivyj-klaster-postgresql-patroni-opyt-vnedreniya","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"de_DE","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. \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","og:url":"https:\/\/prohoster.info\/de\/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"},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/posts\/35706","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/comments?post=35706"}],"version-history":[{"count":1,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/posts\/35706\/revisions"}],"predecessor-version":[{"id":172654,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/posts\/35706\/revisions\/172654"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/media\/26749"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/media?parent=35706"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/categories?post=35706"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/tags?post=35706"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}