Ich wollte mich schon länger mit Patroni (Doku) beschäftigen, um in der Lage zu sein, hochverfügbare Postgres-Datenbanken in Cloud-Umgebungen bereitstellen zu können. Insbesondere aus dem Grund, dass herkömmliche Cluster- und Failovermechanismen sich dort meist nicht realisieren lassen. Eigentlich wollte ich das Ganze mit Containern ausprobieren - aber da ich damit zunächst nicht weitergekommen bin, habe ich mir Patroni zunächst auf VMs angesehen.
Da die Dokumentation zunächst (wie so oft) von einer Installation auf nur einem Node ausgeht, war ich sehr froh, als ich auf die unten stehenden Blogeinträge stieß, die Patroni in einer Drei-Knoten Konfiguration zeigen.
Patroni konfigurieren
Patroni wird mit Hilfe einer Yaml-Datei konfiguriert. Die genannten Blogs hatten netterweise schon eine parat:
postgres@patroni1:~/patroni$ cat postgres-ha.yml
name: postgres1
scope: pglab
namespace: /digitalis.io/
consul:
url: http://127.0.0.1:8500
register_service: true
postgresql:
connect_address: 192.168.129.155:5432
bin_dir: /usr/lib/postgresql/12/bin
data_dir: /mnt/pg_data/12/main
listen: "*:5432"
authentication:
replication:
username: replicator
password: geheim
sslmode: require
restapi:
connect_address: 192.168.129.155:8008
listen: 192.168.129.155:8008
bootstrap:
dcs:
postgresql:
parameters:
ssl: "on"
ssl_ciphers: "TLSv1.2:!aNULL:!eNULL"
ssl_cert_file: /var/lib/postgresql/patroni/certs/server.crt
ssl_key_file: /var/lib/postgresql/patroni/certs/server.key
users:
app_user:
password: "geheim"
pg_hba:
- local all all md5
- hostssl all all 127.0.0.1/32 md5
- hostssl all all ::1/128 md5
- hostssl all all ::1/128 md5
- hostssl all all 0.0.0.0/0 md5
- hostssl replication replicator patroni1.node.de-gt-2.consul md5
- hostssl replication replicator patroni2.node.de-gt-2.consul md5
- hostssl replication replicator patroni3.node.de-gt-2.consul md5
- hostssl replication replicator 192.168.129.155/32 md5
- hostssl replication replicator 192.168.129.156/32 md5
- hostssl replication replicator 192.168.129.157/32 md5
initdb:
- encoding: UTF8
Meine Nodes verfügen über mindestens zwei Netzwerkinterfaces. Ich habe für die Clusterkommunikation das interne gewählt. Unten sieht man, warum die Consul-Konfiguration wichtig ist: Es hilft, wenn Consul in die DNS-Auflösung eingebunden wird. Die Client Konfiguration von Consul sieht dementsprechend aus:
postgres@patroni1:~/patroni$ cat /etc/consul/consul.hcl
datacenter = "de-gt-2"
data_dir = "/data/consul"
node_name = "patroni1"
ports {
grpc = 8502
}
connect {
enabled = true
}
recursors = [ "127.0.0.53" ]
Auf den VMs läuft systemd-resolved. In der Consul-Dokumentation gibt es Hinweise dazu, wie Consul dort eingeschleift werden kann. Damit läuft dann Consul DNS auf 127.0.0.1:53, iptables leitet Anfragen an Port 53 auf Consul-DNS auf Port 8600 um und die Consul-Clientkonfiguration sorgt dafür, dass Anfragen, die nicht an die consul.
Domain gehen, an systemd-resolved weitergegeben werden:
postgres@patroni1:~$ dig +short master.pglab.service.consul
192.168.129.156
postgres@patroni1:~$ dig +short www.heise.de
193.99.144.85
Beim Aufsetzen von Patroni bin ich dann von VM zu VM vorgegangen. Als postgres-User setzt der Aufruf von patroni <config.yml>
den Prozess in Bewegung. Die Konfigurationsdateien unterscheiden sich nur in den IP-Adressen. Die erste von Patroni gestartete Postgres-Instanz wird automatisch "Leader" und bekommt im Consul den Tag "master". Die nächste von Patroni gestartete Instanz repliziert sich automatisch den aktuellen Datenbestand (der Replikationsuser muß allerdings im Leader zunächst angelegt werden):
postgres@patroni1:~/patroni$ createuser -U postgres replicator -P --replication
Jede weitere von Patroni gestartete Node macht es ebenso. Der Aufruf von patronictl
zeigt dann den Status:
postgres@patroni1:~/patroni$ patronictl -c postgres-ha.yml list
+-----------+-----------------+---------+---------+----+-----------+
| Member | Host | Role | State | TL | Lag in MB |
+ Cluster: pglab (7111756342097541380) -+---------+----+-----------+
| postgres1 | 192.168.129.155 | Replica | running | 3 | 0 |
| postgres2 | 192.168.129.156 | Leader | running | 3 | |
| postgres3 | 192.168.129.157 | Replica | running | 3 | 0 |
+-----------+-----------------+---------+---------+----+-----------+
Consul-DNS sorgt dafür, dass unter master.pglab.service.consul
immer der aktuelle "Leader" angesprochen wird. Unter replica
sind alle Replicas zu erreichen:
postgres@patroni1:~/patroni$ dig +short replica.pglab.service.consul
192.168.129.157
192.168.129.155
Auf diese Weise lässt sich auf den Clients auch ein Loadbalancing über die Replicas realisieren. Die Funktion, die ein Loadbalancer übernehmen würde, wird hier von Consul übernommen.
Die Anzahl der Nodes ist frei wählbar (zwei sollten es schon sein, um ein Failover hinzubekommen). Consul braucht mindestens drei Nodes, fünf sind besser. Als Komponente für Service Discovery (und Einsparung von Loadbalancern) ist Consul sowieso sinnvoll und kann ggfs. auch für andere Dienste genutzt werden.
Links
- Postgresql HA cluster in docker using Patroni and Consul (Github)
- Deploying PostgreSQL for High Availability with patroni, etcd and HAProxy - Part1
- Deploying PostgreSQL for High Availability with Patroni, etcd and HAProxy - Part 2
- Using Hashicorp Consul with PostgreSQL for High Availability with no load balancers needed!
- Understanding and Implementing Postgres HA with Patroni (Training)