Failover-Cluster PostgreSQL + Patroni. Implementierungserfahrung

Im Artikel erzähle ich Ihnen, wie wir an das Thema PostgreSQL-Fehlertoleranz herangegangen sind, warum es für uns wichtig wurde und was am Ende passiert ist.

Wir haben einen hoch ausgelasteten Service: 2,5 Millionen Benutzer weltweit, mehr als 50 aktive Benutzer täglich. Die Server befinden sich in Amazone in einer Region Irlands: Über 100 verschiedene Server sind ständig in Betrieb, davon fast 50 mit Datenbanken.

Das gesamte Backend ist eine große monolithische Stateful-Java-Anwendung, die eine ständige Websocket-Verbindung mit dem Client aufrechterhält. Wenn mehrere Benutzer gleichzeitig am selben Board arbeiten, sehen alle die Änderungen in Echtzeit, da wir jede Änderung in die Datenbank schreiben. Wir haben ungefähr 10 Anfragen pro Sekunde an unsere Datenbanken. Bei Spitzenlast schreiben wir in Redis 80–100 Anfragen pro Sekunde.
Failover-Cluster PostgreSQL + Patroni. Implementierungserfahrung

Warum wir von Redis auf PostgreSQL umgestiegen sind

Zunächst arbeitete unser Dienst mit Redis, einem Schlüsselwertspeicher, der alle Daten im RAM des Servers speichert.

Vorteile von Redis:

  1. Hohe Reaktionsgeschwindigkeit, weil alles ist im Gedächtnis gespeichert;
  2. Einfache Sicherung und Replikation.

Nachteile von Redis für uns:

  1. Es gibt keine echten Transaktionen. Wir haben versucht, sie auf der Ebene unserer Anwendung zu simulieren. Leider funktionierte dies nicht immer gut und erforderte das Schreiben von sehr komplexem Code.
  2. Die Datenmenge ist durch die Speicherkapazität begrenzt. Mit zunehmender Datenmenge wächst auch der Speicher und am Ende stoßen wir auf die Eigenschaften der ausgewählten Instanz, was in AWS das Stoppen unseres Dienstes erfordert, um den Instanztyp zu ändern.
  3. Es ist notwendig, ständig ein niedriges Latenzniveau aufrechtzuerhalten, weil. Wir haben sehr viele Anfragen. Die optimale Verzögerungsstufe liegt für uns bei 17–20 ms. Bei einem Wert von 30–40 ms erhalten wir lange Antworten auf Anfragen unserer Anwendung und eine Verschlechterung des Dienstes. Leider ist uns dies im September 2018 passiert, als eine der Instanzen mit Redis aus irgendeinem Grund eine doppelt so hohe Latenz wie üblich aufwies. Um das Problem zu beheben, haben wir den Dienst mittags wegen außerplanmäßiger Wartungsarbeiten gestoppt und die problematische Redis-Instanz ersetzt.
  4. Selbst bei geringfügigen Fehlern im Code kann es leicht zu Dateninkonsistenzen kommen und dann viel Zeit damit verbringen, Code zu schreiben, um diese Daten zu korrigieren.

Wir haben die Nachteile berücksichtigt und erkannt, dass wir auf etwas Bequemeres umsteigen müssen, mit normalen Transaktionen und weniger Abhängigkeit von der Latenz. Ich habe Nachforschungen angestellt, viele Optionen analysiert und mich für PostgreSQL entschieden.

Wir sind bereits seit 1,5 Jahren auf eine neue Datenbank umgestiegen und haben nur einen kleinen Teil der Daten verschoben, sodass wir jetzt gleichzeitig mit Redis und PostgreSQL arbeiten. Weitere Informationen zu den Phasen des Verschiebens und Wechselns von Daten zwischen Datenbanken finden Sie in Der Artikel meines Kollegen.

Als wir mit dem Umzug begannen, arbeitete unsere Anwendung direkt mit der Datenbank und griff auf die Master-Redis und PostgreSQL zu. Der PostgreSQL-Cluster bestand aus einem Master und einem Replikat mit asynchroner Replikation. So sah das Datenbankschema aus:
Failover-Cluster PostgreSQL + Patroni. Implementierungserfahrung

Implementierung von PgBouncer

Während wir umzogen, entwickelte sich auch das Produkt weiter: Die Anzahl der Benutzer und die Anzahl der Server, die mit PostgreSQL arbeiteten, stiegen und es begannen uns die Verbindungen zu fehlen. PostgreSQL erstellt für jede Verbindung einen separaten Prozess und verbraucht Ressourcen. Sie können die Anzahl der Verbindungen bis zu einem bestimmten Punkt erhöhen, andernfalls besteht die Möglichkeit, dass die Datenbankleistung nicht optimal ist. Die ideale Option in einer solchen Situation wäre die Wahl eines Verbindungsmanagers, der vor der Basis steht.

Für den Verbindungsmanager hatten wir zwei Optionen: Pgpool und PgBouncer. Da ersteres jedoch den Transaktionsmodus für die Arbeit mit der Datenbank nicht unterstützt, haben wir uns für PgBouncer entschieden.

Wir haben das folgende Arbeitsschema eingerichtet: Unsere Anwendung greift auf einen PgBouncer zu, hinter dem sich PostgreSQL-Master befinden, und hinter jedem Master befindet sich ein Replikat mit asynchroner Replikation.
Failover-Cluster PostgreSQL + Patroni. Implementierungserfahrung

Gleichzeitig konnten wir nicht die gesamte Datenmenge in PostgreSQL speichern und die Geschwindigkeit der Arbeit mit der Datenbank war uns wichtig, also haben wir mit dem Sharding von PostgreSQL auf Anwendungsebene begonnen. Das oben beschriebene Schema ist hierfür relativ praktisch: Beim Hinzufügen eines neuen PostgreSQL-Shards reicht es aus, die PgBouncer-Konfiguration zu aktualisieren, und die Anwendung kann sofort mit dem neuen Shard arbeiten.

PgBouncer-Failover

Dieses Schema funktionierte bis zu dem Moment, als die einzige PgBouncer-Instanz starb. Wir befinden uns in AWS, wo alle Instanzen auf Hardware laufen, die regelmäßig ausfällt. In solchen Fällen wechselt die Instanz einfach auf neue Hardware und funktioniert wieder. Dies geschah mit PgBouncer, es war jedoch nicht mehr verfügbar. Die Folge dieses Herbstes war die Nichtverfügbarkeit unseres Dienstes für 25 Minuten. AWS empfiehlt für solche Situationen den Einsatz benutzerseitiger Redundanz, die in unserem Land zu diesem Zeitpunkt noch nicht implementiert war.

Danach haben wir ernsthaft über die Fehlertoleranz von PgBouncer- und PostgreSQL-Clustern nachgedacht, da eine ähnliche Situation bei jeder Instanz in unserem AWS-Konto auftreten könnte.

Wir haben das PgBouncer-Fehlertoleranzschema wie folgt aufgebaut: Alle Anwendungsserver greifen auf den Network Load Balancer zu, hinter dem sich zwei PgBouncer befinden. Jeder PgBouncer betrachtet den gleichen PostgreSQL-Master jedes Shards. Tritt erneut ein AWS-Instanzabsturz auf, wird der gesamte Datenverkehr über einen anderen PgBouncer umgeleitet. Das Network Load Balancer-Failover wird von AWS bereitgestellt.

Dieses Schema erleichtert das Hinzufügen neuer PgBouncer-Server.
Failover-Cluster PostgreSQL + Patroni. Implementierungserfahrung

Erstellen Sie einen PostgreSQL-Failover-Cluster

Bei der Lösung dieses Problems haben wir verschiedene Optionen in Betracht gezogen: selbstgeschriebenes Failover, repmgr, AWS RDS, Patroni.

Selbstgeschriebene Skripte

Sie können die Arbeit des Masters überwachen und bei einem Ausfall das Replikat zum Master hochstufen und die PgBouncer-Konfiguration aktualisieren.

Die Vorteile dieses Ansatzes sind maximale Einfachheit, da Sie selbst Skripte schreiben und genau verstehen, wie sie funktionieren.

Nachteile:

  • Möglicherweise ist der Master nicht ausgefallen, stattdessen ist möglicherweise ein Netzwerkfehler aufgetreten. Wenn ein Failover davon nichts weiß, wird das Replikat zum Master heraufgestuft, während der alte Master weiterarbeitet. Dadurch erhalten wir zwei Server in der Rolle des Masters und wissen nicht, welcher von ihnen über die aktuellsten Daten verfügt. Diese Situation wird auch Split-Brain genannt;
  • Wir blieben ohne Antwort. In unserer Konfiguration, dem Master und einem Replikat, wandert das Replikat nach dem Wechsel zum Master und wir haben keine Replikate mehr, sodass wir manuell ein neues Replikat hinzufügen müssen;
  • Wir benötigen eine zusätzliche Überwachung des Failover-Vorgangs, da wir über 12 PostgreSQL-Shards verfügen, was bedeutet, dass wir 12 Cluster überwachen müssen. Bei einer Erhöhung der Anzahl der Shards müssen Sie auch daran denken, das Failover zu aktualisieren.

Selbstgeschriebenes Failover sieht sehr kompliziert aus und erfordert nicht triviale Unterstützung. Mit einem einzelnen PostgreSQL-Cluster wäre dies die einfachste Option, lässt sich aber nicht skalieren und ist daher für uns nicht geeignet.

Repmgr

Replikationsmanager für PostgreSQL-Cluster, der den Betrieb eines PostgreSQL-Clusters verwalten kann. Gleichzeitig verfügt es nicht über ein automatisches Failover, sodass Sie für die Arbeit Ihren eigenen „Wrapper“ über die fertige Lösung schreiben müssen. Es kann also alles noch komplizierter ausfallen als bei selbstgeschriebenen Skripten, deshalb haben wir Repmgr gar nicht erst ausprobiert.

AWS-RDS

Unterstützt alles, was wir brauchen, weiß, wie man Backups erstellt und unterhält einen Pool an Verbindungen. Es verfügt über eine automatische Umschaltung: Wenn der Master stirbt, wird das Replikat zum neuen Master und AWS ändert den DNS-Eintrag auf den neuen Master, während sich die Replikate in verschiedenen AZs befinden können.

Zu den Nachteilen gehört das Fehlen von Feineinstellungen. Als Beispiel für die Feinabstimmung: Unsere Instanzen haben Einschränkungen für TCP-Verbindungen, was in RDS leider nicht möglich ist:

net.ipv4.tcp_keepalive_time=10
net.ipv4.tcp_keepalive_intvl=1
net.ipv4.tcp_keepalive_probes=5
net.ipv4.tcp_retries2=3

Zudem ist AWS RDS fast doppelt so teuer wie der reguläre Instanzpreis, was der Hauptgrund für den Verzicht auf diese Lösung war.

Patron

Dies ist eine Python-Vorlage zur Verwaltung von PostgreSQL mit guter Dokumentation, automatischem Failover und Quellcode auf Github.

Vorteile von Patroni:

  • Jeder Konfigurationsparameter wird beschrieben, es ist klar, wie er funktioniert;
  • Automatisches Failover funktioniert sofort;
  • In Python geschrieben, und da wir selbst viel in Python schreiben, wird es für uns einfacher sein, mit Problemen umzugehen und vielleicht sogar die Entwicklung des Projekts zu unterstützen;
  • Verwaltet PostgreSQL vollständig und ermöglicht Ihnen, die Konfiguration auf allen Knoten des Clusters gleichzeitig zu ändern. Wenn der Cluster neu gestartet werden muss, um die neue Konfiguration anzuwenden, kann dies erneut mit Patroni erfolgen.

Nachteile:

  • Aus der Dokumentation geht nicht klar hervor, wie man mit PgBouncer richtig arbeitet. Obwohl es schwierig ist, es als Minuspunkt zu bezeichnen, da die Aufgabe von Patroni darin besteht, PostgreSQL zu verwalten, und wie die Verbindungen zu Patroni funktionieren, ist bereits unser Problem;
  • Es gibt nur wenige Beispiele für die Implementierung von Patroni auf großen Volumina, während es viele Beispiele für die Implementierung von Grund auf gibt.

Aus diesem Grund haben wir uns für Patroni entschieden, um einen Failover-Cluster zu erstellen.

Patroni-Implementierungsprozess

Vor Patroni hatten wir 12 PostgreSQL-Shards in einer Konfiguration aus einem Master und einem Replikat mit asynchroner Replikation. Die Anwendungsserver griffen über den Network Load Balancer auf die Datenbanken zu, dahinter befanden sich zwei Instanzen mit PgBouncer und dahinter befanden sich alle PostgreSQL-Server.
Failover-Cluster PostgreSQL + Patroni. Implementierungserfahrung

Um Patroni zu implementieren, mussten wir eine verteilte Speicherclusterkonfiguration auswählen. Patroni arbeitet mit verteilten Konfigurationsspeichersystemen wie etcd, Zookeeper, Consul. Wir haben gerade einen vollwertigen Consul-Cluster auf dem Markt, der in Verbindung mit Vault funktioniert, und wir verwenden ihn nicht mehr. Ein guter Grund, Consul für den vorgesehenen Zweck zu nutzen.

Wie Patroni mit Consul zusammenarbeitet

Wir haben einen Consul-Cluster, der aus drei Knoten besteht, und einen Patroni-Cluster, der aus einem Leader und einer Replik besteht (in Patroni wird der Master Cluster-Leader genannt und die Slaves werden Repliken genannt). Jede Instanz des Patroni-Clusters sendet ständig Informationen über den Status des Clusters an Consul. Daher können Sie bei Consul jederzeit die aktuelle Konfiguration des Patroni-Clusters erfahren und erfahren, wer derzeit der Anführer ist.

Failover-Cluster PostgreSQL + Patroni. Implementierungserfahrung

Um Patroni mit Consul zu verbinden, reicht es aus, die offizielle Dokumentation zu lesen, die besagt, dass Sie einen Host im http- oder https-Format angeben müssen, je nachdem, wie wir mit Consul arbeiten, und optional das Verbindungsschema:

host: the host:port for the Consul endpoint, in format: http(s)://host:port
scheme: (optional) http or https, defaults to http

Es sieht einfach aus, aber hier beginnen die Fallstricke. Mit Consul arbeiten wir über eine sichere Verbindung über https und unsere Verbindungskonfiguration sieht folgendermaßen aus:

consul:
  host: https://server.production.consul:8080 
  verify: true
  cacert: {{ consul_cacert }}
  cert: {{ consul_cert }}
  key: {{ consul_key }}

Aber das funktioniert nicht. Beim Start kann Patroni keine Verbindung zu Consul herstellen, da es ohnehin versucht, über http zu gehen.

Der Quellcode von Patroni half bei der Lösung des Problems. Gut, dass es in Python geschrieben ist. Es stellt sich heraus, dass der Host-Parameter in keiner Weise analysiert wird und das Protokoll im Schema angegeben werden muss. So sieht für uns der funktionierende Konfigurationsblock für die Arbeit mit Consul aus:

consul:
  host: server.production.consul:8080
  scheme: https
  verify: true
  cacert: {{ consul_cacert }}
  cert: {{ consul_cert }}
  key: {{ consul_key }}

Konsul-Vorlage

Daher haben wir den Speicher für die Konfiguration ausgewählt. Jetzt müssen wir verstehen, wie PgBouncer seine Konfiguration ändert, wenn der Anführer im Patroni-Cluster wechselt. Auf diese Frage gibt es in der Dokumentation keine Antwort, denn. Dort wird die Arbeit mit PgBouncer grundsätzlich nicht beschrieben.

Auf der Suche nach einer Lösung haben wir einen Artikel gefunden (ich erinnere mich leider nicht an den Titel), in dem geschrieben stand, dass „Consul-template“ bei der Paarung von PgBouncer und Patroni sehr geholfen hat. Dies veranlasste uns, zu untersuchen, wie Consul-template funktioniert.

Es stellte sich heraus, dass Consul-template ständig die Konfiguration des PostgreSQL-Clusters in Consul überwacht. Wenn sich der Anführer ändert, aktualisiert er die PgBouncer-Konfiguration und sendet einen Befehl zum Neuladen.

Failover-Cluster PostgreSQL + Patroni. Implementierungserfahrung

Ein großer Vorteil der Vorlage besteht darin, dass sie als Code gespeichert wird. Wenn Sie also einen neuen Shard hinzufügen, reicht es aus, einen neuen Commit durchzuführen und die Vorlage automatisch zu aktualisieren, wodurch das Prinzip „Infrastruktur als Code“ unterstützt wird.

Neue Architektur mit Patroni

Als Ergebnis haben wir das folgende Arbeitsschema erhalten:
Failover-Cluster PostgreSQL + Patroni. Implementierungserfahrung

Alle Anwendungsserver greifen auf den Balancer zu → dahinter befinden sich zwei Instanzen von PgBouncer → auf jeder Instanz wird die Consul-Vorlage gestartet, die den Status jedes Patroni-Clusters überwacht und die Relevanz der PgBouncer-Konfiguration überwacht, die Anfragen an den aktuellen Leiter sendet jedes Clusters.

Manuelles Testen

Wir haben dieses Schema vor dem Start in einer kleinen Testumgebung ausgeführt und die Funktionsweise der automatischen Umschaltung überprüft. Sie öffneten die Tafel, bewegten den Aufkleber und „töteten“ in diesem Moment den Anführer des Clusters. In AWS ist dies so einfach wie das Herunterfahren der Instanz über die Konsole.

Failover-Cluster PostgreSQL + Patroni. Implementierungserfahrung

Der Aufkleber kehrte innerhalb von 10 bis 20 Sekunden zurück und begann sich dann wieder normal zu bewegen. Das bedeutet, dass der Patroni-Cluster ordnungsgemäß funktioniert hat: Er hat den Anführer geändert, die Informationen an Consul gesendet, und Consul-template hat diese Informationen sofort abgerufen, die PgBouncer-Konfiguration ersetzt und den Befehl zum Neuladen gesendet.

Wie kann man unter hoher Belastung überleben und die Ausfallzeit minimal halten?

Alles funktioniert perfekt! Aber es gibt neue Fragen: Wie funktioniert es unter hoher Last? Wie kann man alles schnell und sicher in die Produktion einführen?

Die Testumgebung, in der wir Lasttests durchführen, hilft uns bei der Beantwortung der ersten Frage. Es ist hinsichtlich der Architektur völlig identisch mit der Produktion und hat Testdaten generiert, deren Volumen in etwa der Produktion entspricht. Wir beschließen, während des Tests einfach einen der PostgreSQL-Master zu „killen“ und zu sehen, was passiert. Zuvor ist es jedoch wichtig, das automatische Rollieren zu überprüfen, da wir in dieser Umgebung über mehrere PostgreSQL-Shards verfügen, sodass wir vor der Produktion hervorragende Tests der Konfigurationsskripte erhalten.

Beide Aufgaben sehen ehrgeizig aus, aber wir haben PostgreSQL 9.6. Können wir sofort auf 11.2 upgraden?

Wir entscheiden uns für zwei Schritte: Zuerst ein Upgrade auf 2 und dann den Start von Patroni.

PostgreSQL-Update

Um die PostgreSQL-Version schnell zu aktualisieren, verwenden Sie die Option -k, bei dem Hardlinks auf der Festplatte erstellt werden und ein Kopieren Ihrer Daten nicht erforderlich ist. Auf Basis von 300-400 GB dauert das Update 1 Sekunde.

Wir haben viele Shards, daher muss das Update automatisch durchgeführt werden. Dazu haben wir ein Ansible-Playbook geschrieben, das den gesamten Update-Prozess für uns abwickelt:

/usr/lib/postgresql/11/bin/pg_upgrade 
<b>--link </b>
--old-datadir='' --new-datadir='' 
 --old-bindir=''  --new-bindir='' 
 --old-options=' -c config_file=' 
 --new-options=' -c config_file='

Hierbei ist zu beachten, dass Sie vor Beginn des Upgrades dieses mit dem Parameter durchführen müssen --überprüfenum sicherzustellen, dass Sie ein Upgrade durchführen können. Unser Skript führt auch die Ersetzung von Konfigurationen für die Dauer des Upgrades durch. Unser Skript war in 30 Sekunden fertig, was ein hervorragendes Ergebnis ist.

Starten Sie Patroni

Um das zweite Problem zu lösen, schauen Sie sich einfach die Patroni-Konfiguration an. Das offizielle Repository verfügt über eine Beispielkonfiguration mit initdb, die für die Initialisierung einer neuen Datenbank beim ersten Start von Patroni verantwortlich ist. Da wir aber bereits über eine fertige Datenbank verfügen, haben wir diesen Abschnitt einfach aus der Konfiguration entfernt.

Als wir begannen, Patroni auf einem bereits vorhandenen PostgreSQL-Cluster zu installieren und auszuführen, stießen wir auf ein neues Problem: Beide Server starteten als Leader. Patroni weiß nichts über den frühen Zustand des Clusters und versucht, beide Server als zwei separate Cluster mit demselben Namen zu starten. Um dieses Problem zu lösen, müssen Sie das Verzeichnis mit den Daten auf dem Slave löschen:

rm -rf /var/lib/postgresql/

Dies muss nur am Slave durchgeführt werden!

Wenn ein sauberes Replikat verbunden ist, erstellt Patroni einen Basebackup-Leader, stellt ihn auf dem Replikat wieder her und holt dann den aktuellen Status gemäß den Wal-Protokollen ein.

Eine weitere Schwierigkeit, auf die wir gestoßen sind, besteht darin, dass alle PostgreSQL-Cluster standardmäßig den Namen „main“ tragen. Wenn jeder Cluster nichts über den anderen weiß, ist das normal. Wenn Sie jedoch Patroni verwenden möchten, müssen alle Cluster einen eindeutigen Namen haben. Die Lösung besteht darin, den Clusternamen in der PostgreSQL-Konfiguration zu ändern.

lade Test

Wir haben einen Test gestartet, der die Benutzererfahrung auf Boards simuliert. Als die Auslastung unseren durchschnittlichen Tageswert erreichte, wiederholten wir genau denselben Test und schalteten eine Instanz mit dem PostgreSQL-Leader ab. Das automatische Failover funktionierte wie erwartet: Patroni änderte den Anführer, Consul-template aktualisierte die PgBouncer-Konfiguration und sendete einen Befehl zum Neuladen. Laut unseren Grafiken in Grafana war klar, dass es Verzögerungen von 20 bis 30 Sekunden und eine kleine Anzahl von Fehlern von den Servern im Zusammenhang mit der Verbindung zur Datenbank gab. Dies ist eine normale Situation. Solche Werte sind für unser Failover akzeptabel und definitiv besser als die Ausfallzeit des Dienstes.

Patroni in die Produktion bringen

Als Ergebnis haben wir folgenden Plan entwickelt:

  • Stellen Sie die Consul-Vorlage auf den PgBouncer-Servern bereit und starten Sie sie.
  • PostgreSQL-Updates auf Version 11.2;
  • Ändern Sie den Namen des Clusters;
  • Gründung des Patroni-Clusters.

Gleichzeitig ermöglicht uns unser Schema, den ersten Punkt fast jederzeit zu machen: Wir können jeden PgBouncer nacheinander aus der Arbeit nehmen und consul-template darauf bereitstellen und ausführen. Also haben wir es getan.

Für eine schnelle Bereitstellung haben wir Ansible verwendet, da wir bereits alle Playbooks in einer Testumgebung getestet haben und die Ausführungszeit des vollständigen Skripts für jeden Shard zwischen 1,5 und 2 Minuten betrug. Wir könnten alles der Reihe nach auf jedem Shard ausrollen, ohne unseren Dienst zu stoppen, aber wir müssten jedes PostgreSQL für mehrere Minuten ausschalten. In diesem Fall könnten Benutzer, deren Daten sich auf diesem Shard befinden, zu diesem Zeitpunkt nicht vollständig arbeiten, und dies ist für uns inakzeptabel.

Der Ausweg aus dieser Situation war die geplante Wartung, die alle 3 Monate stattfindet. Dies ist ein Zeitfenster für geplante Arbeiten, in denen wir unseren Dienst vollständig herunterfahren und unsere Datenbankinstanzen aktualisieren. Es blieb noch eine Woche bis zum nächsten Fenster und wir beschlossen, einfach abzuwarten und uns weiter vorzubereiten. Während der Wartezeit haben wir uns zusätzlich abgesichert: Für jeden PostgreSQL-Shard haben wir ein Ersatzreplikat erstellt, falls die neuesten Daten nicht gespeichert werden konnten, und für jeden Shard eine neue Instanz hinzugefügt, die ein neues Replikat im Patroni-Cluster werden sollte. um keinen Befehl zum Löschen von Daten auszuführen. All dies trug dazu bei, das Fehlerrisiko zu minimieren.
Failover-Cluster PostgreSQL + Patroni. Implementierungserfahrung

Wir haben unseren Dienst neu gestartet, alles funktionierte wie es sollte, die Benutzer arbeiteten weiter, aber in den Grafiken stellten wir eine ungewöhnlich hohe Auslastung der Consul-Server fest.
Failover-Cluster PostgreSQL + Patroni. Implementierungserfahrung

Warum haben wir das in der Testumgebung nicht gesehen? Dieses Problem verdeutlicht sehr gut, dass es notwendig ist, dem Infrastructure-as-Code-Prinzip zu folgen und die gesamte Infrastruktur, von Testumgebungen bis hin zur Produktion, zu verfeinern. Ansonsten ist es sehr einfach, das Problem zu bekommen, das wir haben. Was ist passiert? Consul erschien zuerst in der Produktion und dann in Testumgebungen. Infolgedessen war die Version von Consul in Testumgebungen höher als in der Produktion. Nur in einer der Versionen wurde ein CPU-Leck bei der Arbeit mit consul-template behoben. Deshalb haben wir Consul einfach aktualisiert und so das Problem gelöst.

Starten Sie den Patroni-Cluster neu

Wir bekamen jedoch ein neues Problem, von dem wir nicht einmal wussten, dass es so weit war. Beim Aktualisieren von Consul entfernen wir einfach den Consul-Knoten mit dem Befehl „consul Leave“ aus dem Cluster → Patroni stellt eine Verbindung zu einem anderen Consul-Server her → alles funktioniert. Aber als wir die letzte Instanz des Consul-Clusters erreichten und ihr den Befehl „consul Leave“ schickten, wurden alle Patroni-Cluster einfach neu gestartet und in den Protokollen sahen wir den folgenden Fehler:

ERROR: get_cluster
Traceback (most recent call last):
...
RetryFailedError: 'Exceeded retry deadline'
ERROR: Error communicating with DCS
<b>LOG: database system is shut down</b>

Der Patroni-Cluster konnte keine Informationen über seinen Cluster abrufen und wurde neu gestartet.

Um eine Lösung zu finden, haben wir die Patroni-Autoren über einen Issue auf Github kontaktiert. Sie schlugen Verbesserungen an unseren Konfigurationsdateien vor:

consul:
 consul.checks: []
bootstrap:
 dcs:
   retry_timeout: 8

Wir konnten das Problem in einer Testumgebung reproduzieren und haben dort diese Optionen getestet, leider funktionierten sie nicht.

Das Problem ist immer noch ungelöst. Wir planen, die folgenden Lösungen auszuprobieren:

  • Verwenden Sie Consul-Agent auf jeder Patroni-Cluster-Instanz;
  • Beheben Sie das Problem im Code.

Wir verstehen, wo der Fehler aufgetreten ist: Das Problem liegt wahrscheinlich in der Verwendung des Standard-Timeouts, das nicht durch die Konfigurationsdatei überschrieben wird. Wenn der letzte Consul-Server aus dem Cluster entfernt wird, bleibt der gesamte Consul-Cluster länger als eine Sekunde hängen. Aus diesem Grund kann Patroni den Status des Clusters nicht abrufen und startet den gesamten Cluster vollständig neu.

Glücklicherweise sind uns keine weiteren Fehler aufgefallen.

Ergebnisse der Verwendung von Patroni

Nach dem erfolgreichen Start von Patroni haben wir in jedem Cluster ein zusätzliches Replikat hinzugefügt. Jetzt gibt es in jedem Cluster so etwas wie ein Quorum: einen Anführer und zwei Replikate, als Sicherheitsnetz für den Fall, dass beim Wechsel ein Split-Brain auftritt.
Failover-Cluster PostgreSQL + Patroni. Implementierungserfahrung

Patroni arbeitet seit mehr als drei Monaten an der Produktion. In dieser Zeit hat er es bereits geschafft, uns zu helfen. Kürzlich starb der Leiter eines der Cluster in AWS, das automatische Failover funktionierte und die Benutzer arbeiteten weiter. Patroni hat seine Hauptaufgabe erfüllt.

Eine kleine Zusammenfassung der Verwendung von Patroni:

  • Einfache Konfigurationsänderungen. Es reicht aus, die Konfiguration einer Instanz zu ändern, und sie wird auf den gesamten Cluster übertragen. Wenn ein Neustart erforderlich ist, um die neue Konfiguration anzuwenden, werden Sie von Patroni darüber informiert. Patroni kann den gesamten Cluster mit einem einzigen Befehl neu starten, was ebenfalls sehr praktisch ist.
  • Das automatische Failover funktioniert und hat uns bereits geholfen.
  • PostgreSQL-Update ohne Anwendungsausfallzeit. Sie müssen zuerst die Replikate auf die neue Version aktualisieren, dann den Leader im Patroni-Cluster ändern und den alten Leader aktualisieren. In diesem Fall erfolgt die notwendige Prüfung des automatischen Failovers.

Source: habr.com

Kommentar hinzufügen