PostgreSQL Failover

Alle weiteren Dienste, die nicht in die drei oberen Foren gehören.
Antworten
Benutzeravatar
heisenberg
Beiträge: 3561
Registriert: 04.06.2015 01:17:27
Lizenz eigener Beiträge: MIT Lizenz

PostgreSQL Failover

Beitrag von heisenberg » 02.11.2016 11:35:19

Hallo zusammen,

ich bastele mir gerade ein PostgresSQL mit Master + HotStandby zusammen. Wichtig ist mir dabei, das der Failover mit minimaler Downtime funktioniert, damit man da ab und an am einen oder anderen Knoten mal eine Wartung vornehmen kann.

Der Thread ist dazu da, die Erkenntnisse zu dokumentieren und gerne auch Anregungen zu bekommen.

Ich habe mir bis jetzt pg_rewind angelesen und erfolgreich getestet. Auch die Umschaltung mittels pg_ctl promote klappt einwandfrei. Jetzt heisst es nur noch, das Ganze zu automatisieren.

Zusätzlich habe ich noch eine Cluster-IP konfiguriert, die ich mittels UCARP betreibe. Das nächste Mal mache ich das vielleicht komplett mit Corosync / Pacemaker. Da muss ich mich aber noch etwas mehr mit beschäftigen.

Mein zugrundeliegendes System ist CentOS 7 und die aktuelle Postgres-Version 9.6 aus dem Repository von postgresql.org. Für CentOS 7 gibt's keine UCARP Pakete, aber die Pakete von CentOS 6 funktionieren auch, da UCARP ein eher winziges Stück Software ohne grosse Abhängigkeiten ist. Die systemd-Konfiguration musste ich mir halt selber basteln. Mit Debian ist es einfacher, da ucarp direkt verfügbar und über /etc/network/interfaces konfiguriert werden kann. In der vorhandenen Konfiguration ist die virtuelle IP träge. D. h. dort, wo die IP ist, bleibt sie erst einmal. Keine Umschaltung, wenn ein Knoten nach Abwesenheit wiederkommt.

UCARP-Konfiguration

/etc/ucarp.conf

Code: Alles auswählen

INTERFACE=enp3s0f1
SRCIP=10.200.50.35
VHID=1
PASSWORD=lsdhfsk6sdf
VIP=10.200.50.37
UPSCRIPT=/etc/vip-up.sh
DOWNSCRIPT=/etc/vip-down.sh
/etc/vip-up.sh

Code: Alles auswählen

#!/bin/bash

/sbin/ip addr add dev $1 $2/24

/etc/vip-down.sh

Code: Alles auswählen

#!/bin/bash

/sbin/ip addr del dev $1 $2/24
/lib/systemd/system/ucarp.service

Code: Alles auswählen

[Unit]
Description=UCARP Virtual IP for Postgres
After=network.service

[Service]
EnvironmentFile=/etc/ucarp.conf
ExecStartPre=/bin/echo /usr/sbin/ucarp --interface=${INTERFACE} --srcip=${SRCIP} --vhid=${VHID} --pass=${PASSWORD} --addr=${VIP} --upscript=${UPSCRIPT} --downscript=${DOWNSCRIPT}
ExecStart=/usr/sbin/ucarp --interface=${INTERFACE} --srcip=${SRCIP} --vhid=${VHID} --pass=${PASSWORD} --addr=${VIP} --upscript=${UPSCRIPT} --downscript=${DOWNSCRIPT}

# "reload" führt zu einer Umschaltung der virtuellen IP auf den anderen Knoten
ExecReload=/bin/kill -SIGUSR2 $MAINPID

# systemd beschwert sich wenn ich ${DOWNSCRIPT} direkt eintrage, weil es nicht mit / beginnt, mit dem Hinweis, dass da ein absoluter Pfad eingetragen werden muss. Deswegen habe ich halt /bin/bash -c davor gesetzt.
ExecStop=/bin/bash -c "${DOWNSCRIPT} ${INTERFACE} ${VIP}"

[Install]
WantedBy=multi-user.target
Postfix-Failover: Der Plan
  1. Bisherigen Master sanft herunterfahren (Alle Transaktionen sollen sich beenden können. Lang laufende Abfragen werden beendet. Also pg_ctl stop -m smart und nach einer Wartezeit dann pg_ctl stop -m fast)
  2. iptables Sperren auf auf beide DB-Server so dass nur die Master und Slave via gegenseitig via postgres-Port Kommunizieren können und keine Client-Anfragen rein kommen
  3. Bisherigen Master wieder hochfahren
  4. recovery.conf auf dem Master anlegen
  5. pg_ctl promote auf dem bisherigen Slave durchführen(Wird zum neuen Master)
  6. Postgres auf dem bisherigen Master herunterfahren
  7. pg_rewind auf dem bisherigen Master ausführen
  8. recovery.conf auf dem Master erneut anlegen
  9. Postgres auf dem bisherigen Master wieder starten. Der Server sollte sofort wieder ein synchronisierter Slave werden
  10. iptables-Sperren wieder aufheben. Damit ist der Normalzustand wieder hergestellt.
Beim testen bin ich mehrere Male in einer Sackgasse gelandet mit dem Ergebniss, dass sich der neue Slave nicht mehr synchronisiert hat sondern dann unwiderbringlich im Standalone Modus war(Server musste mit Basebackup vom jetzigen Master wieder neu initialisiert werden). Ich vermute, dass liegt daran, dass die recovery.conf nach Benutzung in recovery.done umbenannt wurde. Deswegen wird in dem Plan 2 x die recovery.conf erstellt.
Jede Rohheit hat ihren Ursprung in einer Schwäche.

Benutzeravatar
heisenberg
Beiträge: 3561
Registriert: 04.06.2015 01:17:27
Lizenz eigener Beiträge: MIT Lizenz

Re: PostgreSQL Failover

Beitrag von heisenberg » 02.11.2016 18:07:50

Ok. Läuft fast. (Script ist im Moment noch recht haesslich und alles andere als generisch, stelle ich dann später noch online).

Das einzige was im Moment noch fehlschlägt, ist, dass beim pg_rewind meist ein redo-Logfile(WAL-File) fehlt, das schon archiviert ist. So wie ich die Doku verstanden habe, sollte das aber auch schon mit dem recovery-command aus recovery.conf gehen, dass er sich das beim nächsten Neustart holt. Muss ich noch etws basteln.

EDIT

Also, wenn ich trotz Fehler, das ein WAL-File nicht da ist den Slave(mit recovery.conf) starte, dann bekomme ich diese Meldung.

< 2016-11-02 18:27:57.304 CET > ERROR: requested starting point 1/21000000 on timeline 28 is not in this server's history
< 2016-11-02 18:27:57.304 CET > DETAIL: This server's history forked from timeline 28 at 1/20000098.

Das wars dann. Rewind geht nicht mehr. Nur noch basebackup einspielen.

EDIT 2

Wenn ich das fehlende WAL-File hole und per Script nochmal pg_rewind ausführe, dann ist der Datenbestand auch defekt. Wenn ich das Script nur manuell ausführe im Terminal dann geht es auch ohne WAL-File holen. Scheint als ob da die Umgebung nicht passt.

EDIT 3

Hmm. Habe jetzt mal im Script PGDATA noch zusätzlich gesetzt und auch PATH. Habe auch statt su -l mal sudo -i verwendet. Hilft alles nix. Wenn ich mir die Ausgabe vom strace vom interaktiven Befehl und vom Script, dann macht das interaktive jede Menge mehr als das vom Script gestartete pg_rewind.

EDIT 4

Auch das ausführen von Screen per Script und auch das sourcen von /etc/profile hat nichts gebracht.

EDIT 5

Nachdem ich jetzt noch eine kurze Wartezeit vor den pg_rewind eingebaut habe scheint es jetzt zu funktionieren. Vielleicht war der postgres-shutdown doch noch nicht ganz fertig.
Jede Rohheit hat ihren Ursprung in einer Schwäche.

Antworten