"Expecto Patronum": Wie betreibe ich PostgreSQL Systeme sicher mit Patroni
Patroni ist eine Art Cluster-Manager-Framework, welches von vielen Kunden genutzt wird, um PostgreSQL (hoch)verfügbar zu betreiben. In diesem Blog-Beitrag möchten wir Ihnen zeigen, wie einfach ein Setup eines PostgreSQL-Clusters mithilfe dieser Lösung möglich ist. Wir stellen die grundlegenden Komponenten vor und erklären die Funktionsweise.
Der Grundstein des Clusters
Für unser beispielhaftes Setup nutzen wird drei Ubuntu-Linux Server (Docker Container). Die Knoten hören auf die folgenden Namen:
- node1; 172.28.0.2
- node2; 172.28.0.3
- node3; 172.28.0.4
Die Server sind netzwerktechnisch so verbunden und konfiguriert, dass sie miteinander problemlos (über Hostnamen und IPs) kommunizieren können.
root@node1:/# wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - root@node1:/# RELEASE=$(lsb_release -cs) root@node1:/# echo "deb http://apt.postgresql.org/pub/repos/apt/ ${RELEASE}"-pgdg main | tee /etc/apt/sources.list.d/pgdg.list root@node1:/# apt-get update root@node1:/# apt-get -y install postgresql-14 root@node1:/# psql --version psql (PostgreSQL) 12.9 (Ubuntu 12.9-0ubuntu0.20.04.1)
Als nächstes wurde etcd installiert.
root@node1:/# apt install etcd root@node1:/# etcd --version etcd Version: 3.2.26 Git SHA: Not provided (use ./build instead of go build) Go Version: go1.13.7 Go OS/Arch: linux/amd64
Bei etcd handelt es sich um einen hierarchisch verteilten Key-Value-Store. Er dient als Speicher für kritische Informationen von verteilten Anwendungen, so z.B. von Clustern oder komplexen IOT-Systemen. Im Umfeld von PostgreSQL und Patroni wird etcd genutzt, um festzulegen, welches System führend (Leader) ist und welche Systeme von diesem replizieren (Replica). Diese Art von Diensten wird auch DCS (Distributed Consensus Store) genannt. Generell können unterschiedliche DCS-Varianten mit Patroni genutzt werden (so auch z.B. Consul, Zookeeper).
„Last but not least" erfolgte die Installation von Patroni inkl. einer grundlegenden Python-Umgebung mit einiges Zusatzpaketen.
root@node1:/# apt-get install python3-pip python3-dev libpq-dev -y root@node1:/# pip3 install --upgrade pip root@node1:/# pip install patroni root@node1:/# pip install python-etcd root@node1:/# pip install psycopg2
„YAML" nicht rum.
Die Grundlage unseres Clusters ist etcd. Die Konfiguration erfolgt im YAML-Format uns muss auf jedem Knoten erfolgen:
root@node1:/# su - postgres postgres@node1:~$ mkdir patroni postgres@node1:~$ cd patroni/ postgres@node1:~/patroni$ cat etcd.yml name: 'node1' listen-peer-urls: 'http://172.28.0.2:2380' listen-client-urls: 'http://172.28.0.2:2379, http://127.0.0.1:2379' initial-advertise-peer-urls: 'http://172.28.0.2:2380' advertise-client-urls: 'http://172.28.0.2:2379' initial-cluster: 'node1=http://172.28.0.2:2380,node2=http://172.28.0.3:2380,node3=http://172.28.0.4:2380' initial-cluster-state: 'new' initial-cluster-token: 'etcd-cluster-1'
Nachdem auch die anderen Knoten (mit den entsprechenden Anpassungen der Hostnamen und IP-Adressen) erfolgt ist, kann der etcd-Dienst gestartet und getestet werden:
postgres@node1:~/patroni$ etcd --config-file etcd.yml > etcd.log 2>&1 & # auf allen drei Knoten postgres@node1:~/patroni$ etcdctl member list 9d46a62f4e15a43c: name=node2 peerURLs=http://172.28.0.3:2380 clientURLs=http://172.28.0.3:2379 isLeader=false ca69017d8279746a: name=node1 peerURLs=http://172.28.0.2:2380 clientURLs=http://172.28.0.2:2379 isLeader=true f53b5cb1cfa54b7b: name=node3 peerURLs=http://172.28.0.4:2380 clientURLs=http://172.28.0.4:2379 isLeader=false
Wir sehen, dass etcd den zweiten Knoten „node1" zum „Leader" erkoren hat.
Noch mehr zum „YAML"en…
Auch Patroni wird per YAML konfiguriert. Beim initialen Aufruf wird auf dem ersten Knoten das „initdb"-Kommando zur Erstellung der Datenbank aufgerufen. Zusätzlich werden einige weitere User-Accounts zur Cluster-Steuerung angelegt.
postgres@node1:~/patroni$ cat node1.yml scope: ordix_cluster name: node1 restapi: listen: 172.28.0.2:8008 connect_address: 172.28.0.2:8008 etcd: #Provide host to do the initial discovery of the cluster topology: hosts: 172.28.0.2:2379,172.28.0.3:2379,172.28.0.4:2379 bootstrap: # this section will be written into Etcd:/<namespace>/<scope>/config after initializing new cluster # and all other cluster members will use it as a `global configuration` dcs: ttl: 10 loop_wait: 5 retry_timeout: .10 maximum_lag_on_failover: 1048576 postgresql: use_pg_rewind: true use_slots: true parameters: hot_standby: "on" wal_keep_segments: 20 max_wal_senders: 8 max_replication_slots: 8 # some desired options for 'initdb' initdb: # Note: It needs to be a list (some options need values, others are switches) - encoding: UTF8 - data-checksums pg_hba: # Add following lines to pg_hba.conf after running 'initdb' - host replication replicator 172.28.0.2/32 md5 - host replication replicator 172.28.0.3/32 md5 - host replication replicator 172.28.0.4/32 md5 - host all all 0.0.0.0/0 md5 # Some additional users users which needs to be created after initializing new cluster users: admin: password: ordix options: - createrole - createdb postgresql: listen: 172.28.0.2:5432 connect_address: 172.28.0.2:5432 data_dir: /var/lib/postgresql/pgsql pgpass: /tmp/pgpass0 authentication: replication: username: replicator password: ordix superuser: username: postgres password: ordix rewind: # Has no effect on postgres 10 and lower username: rewind_user password: ordix parameters: unix_socket_directories: '.' tags: nofailover: false noloadbalance: false postgres@node1:~/patroni$ patroni node1.yml > patroni_node1.log 2>&1 &
Das Cluster lässt sich mit dem folgenden Kommando überprüfen:
postgres@node1:~/patroni$ patronictl -c node1.yml topology +---------+------------+---------+---------+----+-----------+ | Member | Host | Role | State | TL | Lag in MB | + Cluster: ordix_cluster (7066452659603940111) -+-----------+ | node1 | 172.28.0.2 | Leader | running | 3 | | | + node2 | 172.28.0.3 | Replica | running | 3 | 0 | | + node3 | 172.28.0.4 | Replica | running | 3 | 0 | +---------+------------+---------+---------+----+-----------+
Cluster-Spielereien
Nach diesem Erfolgserlebnis wollen wir noch ein bisschen mit dem Cluster spielen und ein Fehlerszenario simulieren. Dazu stoppen wir den ersten Knoten „node1" und schauen uns den Zustand des Clusters an.
postgres@node2:~/patroni$ patronictl -c node2.yml topology 2022-02-19 17:20:36,371 - ERROR - Failed to get list of machines from http://172.28.0.2:2379/v2: MaxRetryError("HTTPConnectionPool(host='172.28.0.2', port=2379): Max retries exceeded with url: /v2/machines (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x7f7f2064f6d0>, 'Connection to 172.28.0.2 timed out. (connect timeout=1)'))") +---------+------------+---------+---------+----+-----------+ | Member | Host | Role | State | TL | Lag in MB | + Cluster: ordix_cluster (7066452659603940111) -+-----------+ | node3 | 172.28.0.4 | Leader | running | 4 | | | + node2 | 172.28.0.3 | Replica | running | 3 | 0 | +---------+------------+---------+---------+----+-----------+
Der Knoten „node1" ist verschwunden und nicht mehr erreichbar. Der Cluster hat reagiert und den Knoten „node3" zum neuen Leader erkoren. Nun bringen wir den ersten Knoten „node1" wieder ins Spiel. Nach dem Start der etcd- und patroni-Dienste ist der Cluster wieder vollständig, wenn auch in einer neuen Rollenverteilung:
postgres@node2:~/patroni$ patronictl -c node2.yml topology +---------+------------+---------+---------+----+-----------+ | Member | Host | Role | State | TL | Lag in MB | + Cluster: ordix_cluster (7066452659603940111) -+-----------+ | node3 | 172.28.0.4 | Leader | running | 4 | | | + node1 | 172.28.0.2 | Replica | running | 3 | 0 | | + node2 | 172.28.0.3 | Replica | running | 4 | 0 | +---------+------------+---------+---------+----+-----------+
Zum guten Schluss versuchen wir den Originalzustand wieder herzustellen. Dazu „switchen" wir den „Leader" zurück auf „node1".
postgres@node2:~/patroni$ patronictl -c node2.yml switchover Master [node3]: Candidate ['node1', 'node2'] []: node1 When should the switchover take place (e.g. 2022-02-19T18:25 ) [now]: Current cluster topology +--------+------------+---------+---------+----+-----------+ | Member | Host | Role | State | TL | Lag in MB | + Cluster: ordix_cluster (7066452659603940111) +-----------+ | node1 | 172.28.0.2 | Replica | running | 3 | 0 | | node2 | 172.28.0.3 | Replica | running | 4 | 0 | | node3 | 172.28.0.4 | Leader | running | 4 | | +--------+------------+---------+---------+----+-----------+ Are you sure you want to switchover cluster ordix_cluster, demoting current master node3? [y/N]: y 2022-02-19 17:25:59.00638 Successfully switched over to "node1" +--------+------------+---------+---------+----+-----------+ | Member | Host | Role | State | TL | Lag in MB | + Cluster: ordix_cluster (7066452659603940111) +-----------+ | node1 | 172.28.0.2 | Leader | running | 3 | | | node2 | 172.28.0.3 | Replica | running | 4 | 0 | | node3 | 172.28.0.4 | Replica | stopped | | unknown | +--------+------------+---------+---------+----+-----------+
Direkt nach dem Switchover ist der Knoten „node3" noch nicht wieder als „Replica" online. Daher warten wir ein paar Sekunden und schauen uns den Zustand des Clusters erneut an.
postgres@node2:~/patroni$ sleep 5 postgres@node2:~/patroni$ patronictl -c node2.yml topology +---------+------------+---------+---------+----+-----------+ | Member | Host | Role | State | TL | Lag in MB | + Cluster: ordix_cluster (7066452659603940111) -+-----------+ | node1 | 172.28.0.2 | Leader | running | 5 | | | + node2 | 172.28.0.3 | Replica | running | 5 | 0 | | + node3 | 172.28.0.4 | Replica | running | 5 | 0 | +---------+------------+---------+---------+----+-----------+
Nach einigen Sekunden hat sich das Cluster wieder „gefangen". Beide „Replica" hängen wieder am „Leader" „node1".
Fazit
Sie haben Fragen rund um den Betrieb von PostgreSQL? Sprechen Sie uns an.
Seminarempfehlung
Sie haben Fragen rund um den Betrieb von PostgreSQL? Sprechen Sie uns an oder besuchen Sie unser Seminar zum Thema PostgreSQL:
POSTGRESQL ADMINISTRATION DB-PG-01
Zum SeminarPrincipal Consultant bei ORDIX
Bei Updates im Blog, informieren wir per E-Mail.
Kommentare