{"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. Implementierungserfahrung","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>In diesem Artikel erkl\u00e4re ich, wie wir das Thema Hochverf\u00fcgbarkeit von PostgreSQL angegangen sind, warum es f\u00fcr uns wichtig wurde und was dabei herausgekommen ist.<\/p>\n<p>Wir haben einen stark frequentierten Service: 2,5 Millionen Nutzer weltweit, \u00fcber 50.000 aktive Nutzer jeden Tag. Die Server befinden sich in AWS in einer Region Irland: st\u00e4ndig sind \u00fcber 100 verschiedene Server damit besch\u00e4ftigt, von denen fast 50 Datenbanken hosten.<\/p>\n<p>Der gesamte Backend-Bereich besteht aus einer gro\u00dfen monolithischen Stateful-Anwendung in Java, die eine dauerhafte WebSocket-Verbindung zum Client h\u00e4lt. Wenn mehrere Benutzer gleichzeitig an einem Board arbeiten, sehen sie alle die \u00c4nderungen in Echtzeit, da wir jede \u00c4nderung in der Datenbank festhalten. Wir haben etwa 10.000 Anfragen pro Sekunde an unsere Datenbanken. Bei Spitzenlast 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. Implementierungserfahrung\" \/><br \/>\n<a rel=\"nofollow\" name=\"habracut\"><\/a><\/p>\n<h2>Warum wir von Redis zu PostgreSQL gewechselt sind<\/h2>\n<p>Urspr\u00fcnglich arbeitete unser Service mit Redis, einem Key-Value-Speicher, der alle Daten im Arbeitsspeicher speichert. <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 gespeichert wird;<\/li>\n<li>Bequemlichkeit bei Backup und Replikation.<\/li>\n<\/ol>\n<p>Nachteile von Redis f\u00fcr uns:<\/p>\n<ol>\n<li>Es gibt keine echten Transaktionen. Wir haben versucht, diese auf Anwendungsebene zu simulieren. Leider hat das nicht immer gut funktioniert und erforderte sehr komplexen Code.<\/li>\n<li>Das Datenvolumen ist durch den verf\u00fcgbaren Speicher begrenzt. Wenn die Datenmenge ansteigt, w\u00e4chst der Speicherbedarf, und letztendlich sto\u00dfen wir an die Grenzen des gew\u00e4hlten Instances, was bei AWS bedeutet, dass wir unseren Service f\u00fcr einen Typenwechsel anhalten m\u00fcssen.<\/li>\n<li>Wir m\u00fcssen st\u00e4ndig ein niedriges Latenzniveau aufrechterhalten, da wir eine sehr hohe Anzahl an Anfragen haben. Das optimale Latenzniveau f\u00fcr uns liegt bei 17-20 ms. Bei 30-40 ms erhalten wir lange Antwortzeiten auf die Anfragen unserer Anwendung und eine Verschlechterung des Dienstes. Leider kam es im September 2018 dazu, dass eine unserer Redis-Instanzen aus unbekannten Gr\u00fcnden eine doppelt so hohe Latenz wie gewohnt hatte. Um das Problem zu l\u00f6sen, haben wir den Service mitten am Arbeitstag f\u00fcr Wartungsarbeiten gestoppt und die problematische Redis-Instanz ersetzt.<\/li>\n<li>Es ist leicht, Inkonsistenzen in den Daten zu erhalten, selbst bei kleinen Fehlern im Code, und dann viel Zeit mit dem Schreiben von Code zu verbringen, um diese Daten zu beheben.<\/li>\n<\/ol>\n<p>Wir haben die Nachteile ber\u00fccksichtigt und festgestellt, dass wir auf etwas Bequemeres umsteigen m\u00fcssen, mit normalen Transaktionen und weniger Abh\u00e4ngigkeit von der Latenz. Wir haben eine Untersuchung durchgef\u00fchrt, viele Optionen analysiert und uns f\u00fcr PostgreSQL entschieden.<\/p>\n<p>Die Migration zur neuen Datenbank dauert bereits seit 1,5 Jahren und wir haben nur einen kleinen Teil der Daten \u00fcbertragen, daher arbeiten wir momentan parallel mit Redis und PostgreSQL. Mehr zu den Phasen der Migration und dem Wechsel der Daten zwischen den Datenbanken steht in <a href=\"https:\/\/habr.com\/ru\/company\/miro\/blog\/437826\/\" rel=\"nofollow\">dem Artikel meines Kollegen<\/a>.<\/p>\n<p>Als wir mit der Migration begannen, arbeitete unsere Anwendung direkt mit der Datenbank und sprach mit dem Master von Redis und PostgreSQL. Der PostgreSQL-Cluster bestand aus einem Master und einer Replik mit asynchroner Replikation. So sah das Schema der Datenbankinteraktion 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. Implementierungserfahrung\" \/><\/p>\n<h2>Implementierung von PgBouncer<\/h2>\n<p>W\u00e4hrend unserer Migration entwickelte sich auch das Produkt weiter: Die Anzahl der Nutzer und der Server, die mit PostgreSQL arbeiteten, nahm zu, und wir hatten nicht gen\u00fcgend Verbindungen. 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, ansonsten besteht die Gefahr, dass die Datenbank nicht optimal arbeitet. In solch einer Situation w\u00e4re die Wahl eines Verbindungsmanagers ideal, der vor der Datenbank steht.<\/p>\n<p>Wir hatten zwei Optionen f\u00fcr den Verbindungsmanager: Pgpool und PgBouncer. Aber der erste unterst\u00fctzt den transaktionsbasierten Betrieb mit der Datenbank nicht, daher w\u00e4hlten wir PgBouncer.<\/p>\n<p>Wir haben das folgende Arbeitsschema eingerichtet: Unsere Anwendung spricht mit einem PgBouncer, hinter dem sich die Master von PostgreSQL befinden, und hinter jedem Master gibt es 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. Implementierungserfahrung\" \/><\/p>\n<p>Dabei konnten wir nicht den gesamten Datenvolumen in PostgreSQL speichern und die Geschwindigkeit der Datenbank war f\u00fcr uns wichtig, daher begannen wir, PostgreSQL auf Anwendungsebene zu sharden. Das oben beschriebene Schema ist daf\u00fcr relativ praktisch: Bei der Hinzuf\u00fcgung eines neuen Shards gen\u00fcgt es, die Konfiguration von PgBouncer 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 der einzige PgBouncer-Instanz ausfiel. Wir befinden uns in AWS, wo alle Instanzen auf Hardware laufen, die gelegentlich ausf\u00e4llt. In solchen F\u00e4llen wird die Instanz einfach auf neue Hardware verschoben und funktioniert wieder. Das geschah auch mit PgBouncer, allerdings wurde es unzug\u00e4nglich. Das Ergebnis dieses Ausfalls war die Nichterreichbarkeit unseres Dienstes f\u00fcr 25 Minuten. AWS empfiehlt in solchen Situationen eine Benutzerseitige Redundanz, die bei uns zu diesem Zeitpunkt nicht umgesetzt war.<\/p>\n<p>Daraufhin machten wir uns ernsthaft Gedanken \u00fcber die Fehlertoleranz von PgBouncer und PostgreSQL-Clustern, da eine \u00e4hnliche Situation mit jeder Instanz in unserem AWS-Konto auftreten k\u00f6nnte.<\/p>\n<p>Wir haben das Schema zur Fehlertoleranz von PgBouncer wie folgt aufgebaut: Alle Anwendungsserver greifen auf einen Network Load Balancer zu, hinter dem sich zwei PgBouncer-Instanzen befinden. Jeder PgBouncer vergeht auf dieselben Master PostgreSQL jeder Shard. Im Falle eines erneuten Ausfalls einer AWS-Instanz wird der gesamte Datenverkehr \u00fcber den anderen PgBouncer umgeleitet. Die Fehlertoleranz des Network Load Balancer wird von AWS bereitgestellt.<\/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. Implementierungserfahrung\" \/><\/p>\n<h2>Aufbau eines fehlertoleranten PostgreSQL-Clusters<\/h2>\n<p>Bei der L\u00f6sung dieser Aufgabe haben wir verschiedene Optionen in Betracht gezogen: Selbstgeschriebene Failover-Skripte, repmgr, AWS RDS, Patroni.<\/p>\n<h3>Selbstgeschriebene Skripte<\/h3>\n<p>Kann den Betrieb des Masters \u00fcberwachen und im Falle eines Ausfalls die Replikation zum Master vorantreiben sowie die PgBouncer-Konfiguration aktualisieren.<\/p>\n<p>Die Vorteile dieses Ansatzes liegen in der 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 ausgefallen sein; es k\u00f6nnte auch ein Netzwerkfehler aufgetreten sein. Das Failover w\u00fcrde, ohne dies zu wissen, die Replikation zum Master vorantreiben, w\u00e4hrend der alte Master weiterhin arbeitet. Infolgedessen h\u00e4tten wir zwei Server in der Rolle des Masters und w\u00fcssten nicht, auf welchem die aktuellsten Daten vorliegen. Eine solche Situation wird auch als Split-Brain bezeichnet.<\/li>\n<li>Wir verblieben ohne Replik. In unserer Konfiguration gibt es einen Master und eine Replik, nach dem Switch wird die Replik zum Master bef\u00f6rdert, und wir haben keine Replik mehr, weshalb wir manuell eine neue Replik hinzuf\u00fcgen m\u00fcssen.<\/li>\n<li>Es ist zus\u00e4tzliches Monitoring f\u00fcr das Failover erforderlich und wir haben 12 PostgreSQL-Shards, was bedeutet, dass wir 12 Cluster \u00fcberwachen m\u00fcssen. Bei einer Erh\u00f6hung der Anzahl der Shards darf man nicht vergessen, das Failover ebenfalls zu aktualisieren.<\/li>\n<\/ul>\n<p>Ein selbstgeschriebener Failover sieht sehr kompliziert aus und erfordert nicht triviale Unterst\u00fctzung. Bei einem PostgreSQL-Cluster ist dies die einfachste Variante, aber sie ist nicht skalierbar, daher kommt sie f\u00fcr uns nicht in Frage.<\/p>\n<h3>Repmgr<\/h3>\n<p>Replication Manager f\u00fcr PostgreSQL-Cluster, der die Arbeit eines PostgreSQL-Clusters verwalten kann. Dabei gibt es keinen automatischen Failover 'out of the box', weshalb man eine eigene 'Wrapper'-L\u00f6sung \u00fcber die vorhandene L\u00f6sung schreiben muss. Das kann also sogar komplizierter sein als mit selbstgeschriebenen Skripten, weshalb wir Repmgr gar nicht ausprobiert haben.<\/p>\n<h3>AWS RDS<\/h3>\n<p>Unterst\u00fctzt alles Notwendige f\u00fcr uns, kann Backups erstellen und unterst\u00fctzt Connection Pools. Es gibt ein automatisches Umschalten: Wenn der Master ausf\u00e4llt, wird die Replik zur neuen Master, und AWS \u00e4ndert den DNS-Eintrag auf den neuen Master, wobei die Replikate in verschiedenen AZs sein k\u00f6nnen.<\/p>\n<p>Zu den Nachteilen z\u00e4hlt das Fehlen feiner Einstellungen. Ein Beispiel f\u00fcr feine Einstellungen: Auf unseren Instanzen gibt es Einschr\u00e4nkungen f\u00fcr TCP-Verbindungen, die leider nicht in RDS 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>Au\u00dferdem ist der Preis f\u00fcr AWS RDS fast doppelt so hoch wie der normale Preis f\u00fcr Instanzen, was der Hauptgrund f\u00fcr die Ablehnung dieser L\u00f6sung war.<\/p>\n<h3>Patroni<\/h3>\n<p>Das ist ein Python-Template zur Verwaltung von PostgreSQL mit guter Dokumentation, automatischem Failover und Quellcode auf GitHub.<\/p>\n<p>Vorteile von Patroni:<\/p>\n<ul>\n<li>Jeder Konfigurationsparameter ist detailliert beschrieben, es ist klar, wie alles funktioniert;<\/li>\n<li>Der automatische Failover funktioniert sofort;<\/li>\n<li>Ist in Python geschrieben, und da wir selbst viel in Python programmieren, wird es uns leichter fallen, Probleme zu l\u00f6sen und m\u00f6glicherweise sogar zur Weiterentwicklung des Projekts beizutragen;<\/li>\n<li>Verwaltet PostgreSQL vollst\u00e4ndig, erm\u00f6glicht das gleichzeitige \u00c4ndern der Konfiguration auf allen Knoten des Clusters. Wenn ein Neustart des Clusters zur Anwendung der neuen Konfiguration erforderlich ist, kann dies ebenfalls mit Patroni erfolgen.<\/li>\n<\/ul>\n<p>Nachteile:<\/p>\n<ul>\n<li>Aus der Dokumentation ist nicht klar, wie man richtig mit PgBouncer arbeitet. Das ist zwar schwer als Nachteil zu bezeichnen, weil es die Aufgabe von Patroni ist, PostgreSQL zu verwalten, und wie die Verbindungen zu Patroni laufen \u2013 das ist unser Problem;<\/li>\n<li>Es gibt nur wenige Beispiele f\u00fcr die Implementierung von Patroni bei gro\u00dfen Volumen, aber viele Beispiele f\u00fcr Implementierungen von Grund auf.<\/li>\n<\/ul>\n<p>Letztlich haben wir uns f\u00fcr die Erstellung eines hochverf\u00fcgbaren Clusters f\u00fcr Patroni entschieden.<\/p>\n<h2>Der Implementierungsprozess von Patroni<\/h2>\n<p>Vor Patroni hatten wir 12 PostgreSQL-Shards in einer Konfiguration mit einem Master und einem Replica mit asynchroner Replikation. Die Anwendungsserver kommunizierten \u00fcber einen Network Load Balancer mit den Datenbanken, hinter dem zwei Instanzen mit PgBouncer standen, und dahinter befanden sich 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. Implementierungserfahrung\" \/><\/p>\n<p>F\u00fcr die Einf\u00fchrung von Patroni mussten wir ein verteiltes Konfigurationsspeicher f\u00fcr den Cluster ausw\u00e4hlen. Patroni funktioniert mit verteilten Konfigurationsspeichersystemen wie etcd, Zookeeper und Consul. Bei uns gibt es in der Produktion einen vollwertigen Consul-Cluster, der in Verbindung mit Vault betrieben wird, und wir nutzen ihn sonst nicht. Das ist eine gute Gelegenheit, Consul wie vorgesehen zu verwenden.<\/p>\n<h3>Wie Patroni mit Consul funktioniert<\/h3>\n<p>Wir haben einen Consul-Cluster, der aus drei Knoten besteht, und einen Patroni-Cluster, der aus einem Leader und einem Replica besteht (im Patroni hei\u00dft der Master Leader des Clusters und die Slaves \u2013 Replikate). Jede Instanz des Patroni-Clusters sendet st\u00e4ndig Informationen \u00fcber den Zustand des Clusters an Consul. Daher kann man immer \u00fcber Consul die aktuelle Konfiguration des Patroni-Clusters und den aktuellen Leader erfahren.<\/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. Implementierungserfahrung\" \/><\/p>\n<p>Um Patroni mit Consul zu verbinden, reicht es aus, die offizielle Dokumentation zu studieren, die besagt, dass man den Host im Format http oder https angeben muss, je nachdem, wie wir mit Consul arbeiten, sowie das Schema f\u00fcr die Verbindung, optional:<\/p>\n<pre><code class=\"plaintext\">host: der Host:Port f\u00fcr den Consul-Endpunkt, im Format: http(s):\/\/host:port\nschema: (optional) http oder https, standardm\u00e4\u00dfig http<\/code><\/pre>\n<p>Das sieht einfach aus, aber hier beginnen die Fallstricke. Mit Consul arbeiten wir \u00fcber eine sichere Verbindung \u00fcber https, und unsere Verbindungskonfiguration sieht folgenderma\u00dfen aus:<\/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>Aber so funktioniert es nicht. Beim Start kann sich Patroni nicht mit Consul verbinden, weil es trotzdem versucht, \u00fcber http zu gehen.<\/p>\n<p>Bei der L\u00f6sung des Problems half der Quellcode von Patroni. Gut, dass er in Python geschrieben ist. Es stellt sich heraus, dass der Host-Parameter nicht geparst wird, und das Protokoll muss im Schema angegeben werden. So sieht der funktionierende Konfigurationsblock f\u00fcr die Arbeit mit Consul bei uns 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>Wir haben also das Speicher-Repository 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 keine Antwort auf diese Frage, da die Zusammenarbeit mit PgBouncer dort grunds\u00e4tzlich nicht beschrieben ist.<\/p>\n<p>Auf der Suche nach einer L\u00f6sung fanden wir einen Artikel (leider erinnere ich mich nicht an den Titel), in dem geschrieben stand, dass Consul-template sehr hilfreich bei der Verbindung von PgBouncer und Patroni war. Das motivierte uns, die Funktionsweise von Consul-template zu untersuchen.<\/p>\n<p>Es stellte sich heraus, dass Consul-template st\u00e4ndig die Konfiguration des PostgreSQL-Clusters in Consul \u00fcberwacht. Beim Wechsel des Leaders aktualisiert es die PgBouncer-Konfiguration und sendet einen Befehl zum Neuladen.<\/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. Implementierungserfahrung\" \/><\/p>\n<p>Ein gro\u00dfer Vorteil des Templates ist, dass es in Form von Code gespeichert wird. Daher reicht es aus, einen neuen Commit vorzunehmen und das Template automatisch zu aktualisieren, um das Prinzip Infrastructure as Code zu unterst\u00fctzen.<\/p>\n<h3>Die neue Architektur mit Patroni<\/h3>\n<p>In der Folge erhielten wir ein solches Arbeitschema:<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. Implementierungserfahrung\" \/><\/p>\n<p>Alle Anwendungsserver wenden sich an den Lastenausgleich \u2192 Hinter ihm stehen zwei PgBouncer-Instanzen \u2192 Auf jeder Instanz l\u00e4uft Consul-template, das den Status jedes Patroni-Clusters \u00fcberwacht und die Aktualit\u00e4t der PgBouncer-Konfiguration sicherstellt, die Anfragen an den aktuellen Leader jedes Clusters weiterleitet.<\/p>\n<h3>Manuelle Tests<\/h3>\n<p>Dieses Schema haben wir vor der Produktion in einer kleinen Testumgebung gestartet und das automatische Umschalten \u00fcberpr\u00fcft. Wir \u00f6ffneten das Board, bewegten einen Sticker und in diesem Moment \"t\u00f6teten\" wir den Cluster-Leader. In AWS reicht es aus, 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. Implementierungserfahrung\" \/><\/p>\n<p>Der Sticker kehrte innerhalb von 10-20 Sekunden zur\u00fcck und begann dann wieder normal zu wandern. Das bedeutet, dass der Patroni-Cluster korrekt gearbeitet hat: Der Leader wurde gewechselt, die Informationen wurden an Consul gesendet und Consul-template erfasste diese Informationen sofort, ersetzte die PgBouncer-Konfiguration und sendete den Neuladungsbefehl.<\/p>\n<h2>Wie \u00fcberlebt man unter hoher Belastung und h\u00e4lt die Ausfallzeiten minimal?<\/h2>\n<p>Alles funktioniert hervorragend! Aber neue Fragen tauchen auf: Wie funktioniert das unter hoher Last? Wie kann man alles schnell und sicher in der Produktion ausrollen?<\/p>\n<p>Die Antwort auf die erste Frage erhalten wir mit Hilfe einer Testumgebung, auf der wir Lasttests durchf\u00fchren. Sie ist in der Architektur identisch mit der Produktionsumgebung und hat generierte Testdaten, die in etwa dem Umfang der Produktion entsprechen. Wir versuchen einfach, einen der PostgreSQL-Master w\u00e4hrend des Tests \"auszuschalten\" und zu sehen, was passiert. Aber vorher ist es wichtig, das automatische Rollout zu \u00fcberpr\u00fcfen, denn in dieser Umgebung haben wir mehrere PostgreSQL-Shards, sodass wir ein hervorragendes Testen der Konfigurationsskripte vor der Produktion erhalten.<\/p>\n<p>Beide Aufgaben erscheinen ehrgeizig, aber wir haben PostgreSQL 9.6. Vielleicht sollten wir gleich auf 11.2 upgraden?<\/p>\n<p>Wir haben uns entschieden, dies in zwei Schritten zu tun: Zuerst die Version auf 11.2 zu aktualisieren, dann Patroni zu starten.<\/p>\n<h3>PostgreSQL-Update<\/h3>\n<p>Um das Version-Upgrade von PostgreSQL schnell durchzuf\u00fchren, ist es notwendig, die Option zu verwenden <b>-k<\/b>, bei der Hardlinks auf der Festplatte erstellt werden und keine Notwendigkeit besteht, Ihre Daten zu kopieren. Bei Datenbanken mit 300-400 GB dauert das Upgrade 1 Sekunde.<\/p>\n<p>Wir haben viele Shards, daher muss das Upgrade automatisch erfolgen. Zu diesem Zweck 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--altes-datadir=&#039;&#039; --neues-datadir=&#039;&#039; \n --altes-bindir=&#039;&#039;  --neues-bindir=&#039;&#039; \n --alte-optionen=&#039; -c konfigurationsdatei=&#039; \n --neue-optionen=&#039; -c konfigurationsdatei=&#039;<\/code><\/pre>\n<p>Dabei ist es wichtig zu beachten, dass vor dem Start des Upgrades das Upgrade mit dem Parameter <b>\u2013check<\/b>durchgef\u00fchrt werden muss, um sicherzustellen, dass das Upgrade m\u00f6glich ist. Auch unser Skript ersetzt w\u00e4hrend des Upgrades die Konfigurationen. Unser Skript wurde in 30 Sekunden ausgef\u00fchrt, das ist ein hervorragendes Ergebnis.<\/p>\n<h3>Start von Patroni<\/h3>\n<p>Um das zweite Problem zu l\u00f6sen, reicht es aus, einen Blick auf die Patroni-Konfiguration zu werfen. Im offiziellen Repository gibt es ein Beispiel f\u00fcr eine Konfiguration mit initdb, die 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 begannen, Patroni auf einem bereits vorhandenen PostgreSQL-Cluster zu installieren und zu starten, stie\u00dfen wir auf ein neues Problem: Beide Server starteten als Leader. Patroni wei\u00df nichts \u00fcber den fr\u00fcheren 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>Das muss nur auf dem Slave gemacht werden!<\/b><\/p>\n<p>Beim Anschlie\u00dfen einer sauberen Replikat macht Patroni ein Basis-Backup des Leaders und stellt es auf der Replikation wieder her, und gleicht dann den aktuellen Zustand \u00fcber die WAL-Logs an.<\/p>\n<p>Eine weitere Herausforderung, mit der wir konfrontiert waren, 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 eindeutigen Namen haben. Die L\u00f6sung besteht darin, den Namen des Clusters in der PostgreSQL-Konfiguration zu \u00e4ndern.<\/p>\n<h3>Lasttest<\/h3>\n<p>Wir haben einen Test gestartet, der die Aktivit\u00e4ten der Benutzer auf den Boards simuliert. Als die Last unseren Durchschnittswert f\u00fcr den Tag erreichte, haben wir denselben Test wiederholt und einen Instance mit dem Leader PostgreSQL abgeschaltet. Der automatische Failover funktionierte wie erwartet: Patroni wechselte den Leader, der Consul-Template aktualisierte die PgBouncer-Konfiguration und sendete den Reload-Befehl. In unseren Grafana-Diagrammen war zu sehen, dass es Verz\u00f6gerungen von 20-30 Sekunden gab und eine geringe Anzahl von Fehlern von den Servern, die mit der Verbindung zur Datenbank zusammenhingen. Das ist eine normale Situation, solche Werte sind f\u00fcr unseren Failover akzeptabel und auf jeden Fall besser als ein Ausfall des Dienstes.<\/p>\n<h2>Patroni in der Produktion<\/h2>\n<p>Am Ende hatten wir folgenden Plan:<\/p>\n<ul>\n<li>Deployment des Consul-Templates auf die PgBouncer-Server und dessen 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>Dabei erlaubt unser Schema, den ersten Punkt praktisch jederzeit umzusetzen; wir k\u00f6nnen jeden PgBouncer nacheinander au\u00dfer Betrieb nehmen und das Deployment sowie den Start des Consul-Templates durchf\u00fchren. So haben wir es auch gemacht.<\/p>\n<p>F\u00fcr ein schnelles Rollout haben wir Ansible verwendet, da wir alle Playbooks bereits in der Testumgebung gepr\u00fcft hatten, und die Ausf\u00fchrungszeit des vollst\u00e4ndigen Szenarios lag zwischen 1,5 und 2 Minuten pro Shard. Wir konnten alles nacheinander auf jeden Shard ausrollen, ohne unseren Dienst anzuhalten, aber wir m\u00fcssten jeweils f\u00fcr einige Minuten jeden PostgreSQL abschalten. In diesem Fall k\u00f6nnten die Benutzer, deren Daten sich auf diesem Shard befinden, w\u00e4hrenddessen nicht vollumf\u00e4nglich arbeiten, was f\u00fcr uns inakzeptabel ist.<\/p>\n<p>Die L\u00f6sung f\u00fcr diese Situation war eine geplante Wartung, die alle 3 Monate bei uns durchgef\u00fchrt wird. Dieses Zeitfenster ist f\u00fcr geplante Arbeiten, in dem wir unseren Dienst vollst\u00e4ndig ausschalten und die Datenbankinstanzen aktualisieren. Eine Woche vor dem n\u00e4chsten Wartungszeitraum entschieden wir uns, einfach abzuwarten und uns zus\u00e4tzlich vorzubereiten. W\u00e4hrend der Wartezeit haben wir uns zus\u00e4tzlich absichert: F\u00fcr jedes PostgreSQL-Shard haben wir eine Backup-Replikation hochgezogen, um die neuesten Daten im Falle eines Fehlers zu sichern, und haben f\u00fcr jedes Shard eine neue Instanz hinzugef\u00fcgt, die als neue Replik im Patroni-Cluster fungieren sollte, um den Befehl zum L\u00f6schen der Daten zu vermeiden. All dies half, das Risiko von Fehlern 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. Implementierungserfahrung\" \/><\/p>\n<p>Wir haben unseren Dienst neu gestartet, alles funktionierte wie vorgesehen, die Benutzer konnten weiterarbeiten, aber auf den Grafiken bemerkten wir eine anomal hohe Last auf den Consul-Servern.<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. Implementierungserfahrung\" \/><\/p>\n<p>Warum haben wir das in der Testumgebung nicht gesehen? Dieses Problem verdeutlicht sehr gut, dass man dem Prinzip Infrastructure as Code folgen und die gesamte Infrastruktur von der Testumgebung bis zur Produktion weiterentwickeln sollte. Andernfalls ist es sehr einfach, auf ein solches Problem zu sto\u00dfen, wie wir es erlebt haben. Was ist passiert? Consul erschien zun\u00e4chst in der Produktion und dann in den Testumgebungen, und letztendlich war in den Testumgebungen die Version von Consul h\u00f6her als in der Produktion. In einem der Releases wurde ein CPU-Leck beim Arbeiten mit consul-template behoben. Daher haben wir einfach Consul aktualisiert und dieses Problem somit gel\u00f6st.<\/p>\n<h3>Patroni-Cluster neu starten<\/h3>\n<p>Doch wir haben ein neues Problem festgestellt, von dem wir nicht einmal ahnten. Bei der Aktualisierung 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 Knoten im Consul-Cluster gelangten und ihm den Befehl consul leave gaben, starteten alle Patroni-Cluster neu, und in den Logs sahen wir den folgenden Fehler:<\/p>\n<pre><code class=\"plaintext\">FEHLER: get_cluster\nR&uuml;ckverfolgung (letzter Aufruf letzte):\n...\nRetryFailedError: &#039;Frist f&uuml;r den erneuten Versuch &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 erhalten und startete neu.<\/p>\n<p>Um eine L\u00f6sung zu finden, wandten wir uns \u00fcber ein Issue auf GitHub an die Autoren von Patroni. Sie schlugen Verbesserungen f\u00fcr unsere Konfigurationsdateien vor:<\/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 der Testumgebung reproduzieren und testeten diese Parameter dort, aber leider funktionierten sie nicht.<\/p>\n<p>Das Problem ist nach wie vor ungel\u00f6st. Wir planen, die folgenden L\u00f6sungsans\u00e4tze auszuprobieren:<\/p>\n<ul>\n<li>Verwendung des Consul-Agents auf jeder Instanz des Patroni-Clusters;<\/li>\n<li>Fehler im Code beheben.<\/li>\n<\/ul>\n<p>Uns ist der Ort des Fehlers bekannt: Wahrscheinlich liegt das Problem in der Verwendung des Standard-Timeouts, das nicht \u00fcber 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, wodurch Patroni den Status des Clusters nicht abrufen kann und das gesamte Cluster neu startet.<\/p>\n<p>Gl\u00fccklicherweise haben wir keine weiteren Fehler festgestellt.<\/p>\n<h2>Ergebnisse der 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 Leader und zwei Replikate, um f\u00fcr den Fall eines Split-Brains 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. Implementierungserfahrung\" \/><\/p>\n<p>In der Produktion l\u00e4uft Patroni seit \u00fcber drei Monaten. In dieser Zeit hat er uns bereits gerettet. K\u00fcrzlich ist in AWS der Leader eines der Cluster ausgefallen, der automatische Failover hat funktioniert und die Benutzer konnten weiterarbeiten. Patroni hat seine Hauptaufgabe erf\u00fcllt.<\/p>\n<p><b>Ein kurzes Fazit zur Verwendung von Patroni:<\/b><\/p>\n<ul>\n<li>Die Bequemlichkeit der Konfigurations\u00e4nderung. Es reicht aus, die Konfiguration auf einer Instanz zu \u00e4ndern, und sie wird in den gesamten Cluster \u00fcbertragen. Falls ein Neustart zur Anwendung der neuen Konfiguration erforderlich ist, wird Patroni dies mitteilen. Patroni kann das gesamte Cluster mit einem einzigen Befehl neu starten, was ebenfalls sehr praktisch ist.<\/li>\n<li>Der automatische Failover funktioniert und hat uns bereits gerettet.<\/li>\n<li>Aktualisierung von PostgreSQL ohne Ausfallzeiten der Anwendung. Zuerst m\u00fcssen die Replikate auf die neue Version aktualisiert werden, dann den Leader im Patroni-Cluster wechseln und den alten Leader aktualisieren. Dabei findet das notwendige Testen des automatischen Failover statt.<\/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 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\/de\/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=\"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.\" \/>\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\udd47Ausfallsicherer PostgreSQL-Cluster + Patroni. Implementierungserfahrung | ProHoster","description":"In diesem Artikel erkl\u00e4re ich, wie wir das Thema Hochverf\u00fcgbarkeit von PostgreSQL angegangen sind, warum es f\u00fcr uns wichtig wurde und was dabei herausgekommen ist.","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.","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","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"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}]}}