Patroni est une solution de Haute disponibilité pour PostgreSQL écrite en Python, et décrite dès l’introduction sur la page officielle du projet comme un « template » :
We call Patroni a “template” because it is far from being a one-size-fits-all or plug-and-play replication system. It will have its own caveats. Use wisely. There are many ways to run high availability with PostgreSQL; for a list, see the PostgreSQL Documentation.
Il s’agit d’une contribution datant de 2016 par le département IT de Zalando (Zalando’s Patroni: a Template for High Availability PostgreSQL)
Site du projet :
Introduction — Patroni 2.0.1 documentation
1. Description de la solution
Patroni ne se contente pas des fonctions de supervision et de switchover/failover, il gère aussi la synchronisation, le « reload » des configurations des instances PostgreSQL… Il est même capable d’exécuter un « pg_rewind » sur un primaire incidenté avant de le remettre en ligne dans le cluster. Le « split brain » est évité, idem un standby ne sera pas promu si un lag existe au-delà d’une quantité paramétrée. Un standby peut ne jamais être répliqué s’il est pensé seulement pour de l’accès en lecture seule.
Architecture :
(Référence : Cybertec, High availability with Patroni – Protect your database – CYBERTEC (cybertec-postgresql.com))
- Instances Patroni : une par instance PostgreSQL pour sa supervision et son contrôle.
- Une seule instance Patroni doit être maitre (« leader ») et être la seule à pouvoir promouvoir son instance PostgreSQL lorsque nécessaire. Quand cela se présente, il y a compétition « leader-race» entre instances Patroni pour acquérir le verrou « leader lock ». Ce verrou est mutuellement exclusif (MUTEX) entre les multiples instances Patroni. Les données critiques permettant cela sont stockées dans le « DCS :Distributed Configuration Store ».
- Les données récoltées par Patroni sont stockées dans un « key-value store» répliqué du « DCS ». La solution DCS de gestion de ces données peut varier, par exemple etcd ou consul. Il s’agit de la brique essentielle garantissant l’intégrité du cluster. Les données stockées sont relatives à la santé des hôtes composant le cluster. Le DCS doit gérer les requêtes concurrentes avec l’atomicité et l’isolation requise. L’algorithme de décision de DCS comme etcd ou consul est RAFT (Raft Consensus Algorithm) qui comprend l’algorithme d’élection d’un leader.
- Une partie de ces données est consultable via une interface ReST.
- On peut configurer une architecture avec un seul membre etcd ou bien plusieurs pour une question évidente de redondance des données sur un système distribué.
- L’architecture peut être plus ou moins complexe, on peut séparer les nœuds etcd des nœuds « Patroni+PG ». A des fins d’expérimentation, on se contentera d’une archi à 3 nœuds où chacun héberge une instance etcd, patroni et PostgreSQL. Mais il est évidemment indispensable de séparer le cluster « ETCD » de celui de PostgresSQL sous patroni. Dans une logique d’objectif de haute disponibilité pour l’entreprise, l’architecture concevable est un cluster du DCS (etcd ou autre) avec ses 3 nœuds sur au moins 2 sites distincts répondant à plusieurs clusters patroni se connectant et s’enregistrant sur ce DCS.
- Au sein même du cluster de serveurs « DCS » (etcd), il faut disposer de trois nœuds DCS (etcd) pour que le vote d’un leader puisse se faire suivant l’obtention d’une majorité supérieure à 50% (lors du processus leader-race déclenché par les instances Patroni)
- Notion d’expiration de la « leader key » et de son rafraichissement régulier .
- La création des instances Patroni réalise tout le nécessaire en créant l’instance PostgreSQL ainsi que les utilisateurs/rôles déclarés dans la configuration de patroni, et enfin configure la réplication entre le primaire et ses standby.
2. Installation et initialisation progressive du cluster
A des fins de POC, on installe l’ETCD, postgreSQL et patroni sur 3 conteneurs LXC instanciées sur une seule machine virtuelle. (on pourrait faire de même sur des conteneurs docker). La distribution Linux de ce POC est Ubuntu.
Sur chaque conteneur LXC :
- Installation des packages PostgreSQL
sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list' wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add - sudo apt-get update apt-get install postgresql-12 postgresql-client-12 postgresql-server-dev-12
- Installation de patroni
apt install python3-pip python3-psycopg2 (python3-dev libpq-dev déjà installés) pip3 install python-etcd pip3 install patroni
- Installation d’etcd
apt-get install etcd -y
sur Ubuntu, la configuration de l’ETCD est dans /etc/default/etcd
Configuration des 3 etcd avec pour valeurs de IP_HOTE:
IP_HOTE:
postgres01 : 10.234.76.28
postgres02 : 10.234.76.45
postgres03 : 10.234.76.13
ETCD_DATA_DIR="/var/lib/etcd/default"/strong ETCD_LISTEN_PEER_URLS : http://127.0.0.1:2380,http://[IP_HOTE]:2380 ETCD_LISTEN_CLIENT_URLS : http://127.0.0.1:2379,http://[IP_HOTE]:2379 ETCD_NAME : [NOM_HOTE] ETCD_INITIAL_ADVERTISE_PEER_URLS : http://[IP_HOTE]:2380 ETCD_ADVERTISE_CLIENT_URLS : http://[IP_HOTE]:2379 ETCD_INITIAL_CLUSTER : [NOM_HOTE1]=http://[IP_HOTE1]:2380,[NOM_HOTE2]=http://[IP_HOTE2]:2380,[NOM_HOTE3]=http://[IP_HOTE3]:2380 ETCD_INITIAL_CLUSTER_TOKEN="pg-cluster" ETCD_INITIAL_CLUSTER_STATE="new"
Démarrage de l’etcd sur les 3 nœuds :
# systemctl start --now etcd root@postgres03:~# ps -fe|grep etcd etcd 12868 1 0 10:14 ? 00:00:00 /usr/bin/etcd root 12880 12793 0 10:14 ? 00:00:00 grep --color=auto etcd root@postgres03:~#
Edition de la configuration /etc/patroni.yml :
root@postgres01:~# more /etc/patroni.yml scope: my-pg-cluster #namespace: /service/ name: patroni01 restapi: listen: 10.234.76.28:8008 connect_address: 10.234.76.28:8008 log: level: INFO dir: /var/log/patroni/my-pg-cluster etcd: hosts: - 10.234.76.28:2379 - 10.234.76.45:2379 - 10.234.76.13:2379 bootstrap: dcs: ttl: 30 loop_wait: 10 retry_timeout: 10 maximum_lag_on_failover: 1048576 postgresql: use_pg_rewind: true parameters: wal_level: replica hot_standby: "on" wal_keep_segment: 8 max_wal_senders: 5 checkpoint_timeout: 30 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 10.234.76.0/24 md5 - host all all 0.0.0.0/0 md5 users: admin: password: admin options: - createrole - createdb postgresql: listen: "*:5433" connect_address: 10.234.76.28:5433 data_dir: /var/lib/postgresql/12/data bin_dir: /usr/lib/postgresql/12/bin pgpass: /tmp/pgpass0 authentication: replication: username: replicator password: capdata superuser: username: postgres password: capdata rewind: # Has no effect on postgres 10 and lower username: rewind_user password: capdata parameters: unix_socket_directories: '.' tags: nofailover: false noloadbalance: false clonefrom: false nosync: false
- configuration du dossier de log patroni :
# install -o postgres -g postgres -m 0750 -d /var/log/patroni/my-pg-cluster
Configuration du service système patroni /etc/systemd/system/patroni.service
[Unit] Description=Patroni instance for high-availability of PostgreSQL After=syslog.target network.target [Service] Type=simple User=postgres Group=postgres ExecStart=/usr/local/bin/patroni /etc/patroni.yml KillMode=process TimeoutSec=30 Restart=no [Install] WantedBy=multi-user.targ
Démarrage de patroni sur un premier noeud:
root@postgres01:~# systemctl start patroni root@postgres01:~# systemctl status patroni patroni.service - Patroni instance for high-availability of PostgreSQL Loaded: loaded (/etc/systemd/system/patroni.service; disabled; vendor preset: enabled) Active: active (running) since Fri 2021-01-29 10:31:33 UTC; 4s ago Main PID: 1501 (patroni) Tasks: 13 (limit: 2348) CGroup: /system.slice/patroni.service ├─1501 /usr/bin/python3 /usr/local/bin/patroni /etc/patroni.yml ├─1537 /usr/lib/postgresql/12/bin/postgres -D /var/lib/postgresql/12/data --config-file=/var/lib/postgresql/12/data/postgresql ├─1541 postgres: my-pg-cluster: checkpointer ├─1542 postgres: my-pg-cluster: background writer ├─1543 postgres: my-pg-cluster: walwriter ├─1544 postgres: my-pg-cluster: autovacuum launcher ├─1545 postgres: my-pg-cluster: stats collector ├─1546 postgres: my-pg-cluster: logical replication launcher └─1549 postgres: my-pg-cluster: postgres postgres 127.0.0.1(42324) idle Jan 29 10:31:36 postgres01 patroni[1501]: /usr/lib/postgresql/12/bin/pg_ctl -D /var/lib/postgresql/12/data -l logfile start Jan 29 10:31:36 postgres01 patroni[1501]: 2021-01-29 10:31:36.968 UTC [1537] LOG: starting PostgreSQL 12.5 (Ubuntu 12.5-1.pgdg18.04+1) on x Jan 29 10:31:36 postgres01 patroni[1501]: 2021-01-29 10:31:36.972 UTC [1537] LOG: listening on IPv4 address "0.0.0.0", port 5433 Jan 29 10:31:36 postgres01 patroni[1501]: 2021-01-29 10:31:36.972 UTC [1537] LOG: listening on IPv6 address "::", port 5433 Jan 29 10:31:36 postgres01 patroni[1501]: 2021-01-29 10:31:36.974 UTC [1537] LOG: listening on Unix socket "./.s.PGSQL.5433" Jan 29 10:31:37 postgres01 patroni[1501]: 2021-01-29 10:31:37.016 UTC [1539] LOG: database system was shut down at 2021-01-29 10:31:35 UTC Jan 29 10:31:37 postgres01 patroni[1501]: 2021-01-29 10:31:37.024 UTC [1537] LOG: database system is ready to accept connections Jan 29 10:31:37 postgres01 patroni[1501]: 2021-01-29 10:31:37.025 UTC [1540] FATAL: the database system is starting up Jan 29 10:31:37 postgres01 patroni[1501]: localhost:5433 - rejecting connections Jan 29 10:31:37 postgres01 patroni[1501]: localhost:5433 - accepting connections
Mon instance PostgreSQL “my-pg-cluster” a été créée par patroni :
root@postgres01:~# ps -fu postgres UID PID PPID C STIME TTY TIME CMD postgres 289 1 0 08:15 00:00:00 /usr/lib/postgresql/12/bin/postgres -D /var/lib/postgresql/12/main -c config_file=/etc/po postgres 304 289 0 08:15 00:00:00 postgres: 12/main: checkpointer postgres 305 289 0 08:15 00:00:00 postgres: 12/main: background writer postgres 306 289 0 08:15 00:00:00 postgres: 12/main: walwriter postgres 307 289 0 08:15 00:00:00 postgres: 12/main: autovacuum launcher postgres 308 289 0 08:15 00:00:00 postgres: 12/main: stats collector postgres 309 289 0 08:15 00:00:00 postgres: 12/main: logical replication launcher postgres 1501 1 0 10:31 00:00:00 /usr/bin/python3 /usr/local/bin/patroni /etc/patroni.yml postgres 1537 1 0 10:31 00:00:00 /usr/lib/postgresql/12/bin/postgres -D /var/lib/postgresql/12/data --config-file=/var/lib postgres 1541 1537 0 10:31 00:00:00 postgres: my-pg-cluster: checkpointer postgres 1542 1537 0 10:31 00:00:00 postgres: my-pg-cluster: background writer postgres 1543 1537 0 10:31 00:00:00 postgres: my-pg-cluster: walwriter postgres 1544 1537 0 10:31 00:00:00 postgres: my-pg-cluster: autovacuum launcher postgres 1545 1537 0 10:31 00:00:00 postgres: my-pg-cluster: stats collector postgres 1546 1537 0 10:31 00:00:00 postgres: my-pg-cluster: logical replication launcher postgres 1549 1537 0 10:31 00:00:00 postgres: my-pg-cluster: postgres postgres 127.0.0.1(42324) idle
Log patroni avec promotion du seul serveur actif:
root@postgres01:~# tail -100 /var/log/patroni/my-pg-cluster/patroni.log 2021-01-29 10:31:34,687 INFO: Selected new etcd server http://10.234.76.28:2379 2021-01-29 10:31:34,696 INFO: No PostgreSQL configuration items changed, nothing to reload. 2021-01-29 10:31:34,710 INFO: Lock owner: None; I am patroni01 2021-01-29 10:31:34,714 INFO: trying to bootstrap a new cluster 2021-01-29 10:31:36,980 INFO: postmaster pid=1537 2021-01-29 10:31:37,052 INFO: establishing a new patroni connection to the postgres cluster 2021-01-29 10:31:37,062 INFO: running post_bootstrap 2021-01-29 10:31:37,081 WARNING: Could not activate Linux watchdog device: "Can't open watchdog device: [Errno 2] No such file or directory: '/dev/watchdog'" 2021-01-29 10:31:37,094 INFO: initialized a new cluster 2021-01-29 10:31:47,082 INFO: Lock owner: patroni01; I am patroni01 2021-01-29 10:31:47,088 INFO: no action. i am the leader with the lock 2021-01-29 10:31:47,116 INFO: Lock owner: patroni01; I am patroni01 2021-01-29 10:31:47,123 INFO: no action. i am the leader with the lock
Parmi les actions inhérentes à la création de mon instance PostgreSQL par patroni, nous remarquerons que le fichier de configuration postgresql.conf automatiquement généré par patroni est non éditable car il sera maintenu par patroni :
root@postgres01:~# more /var/lib/postgresql/12/data/postgresql.conf # Do not edit this file manually! # It will be overwritten by Patroni! include 'postgresql.base.conf' checkpoint_timeout = '30' cluster_name = 'my-pg-cluster' hot_standby = 'on' listen_addresses = '*' max_connections = '100' max_locks_per_transaction = '64' max_prepared_transactions = '0' max_replication_slots = '10' max_wal_senders = '5' max_worker_processes = '8' port = '5433' track_commit_timestamp = 'off' unix_socket_directories = '.' wal_keep_segments = '8' wal_level = 'replica' wal_log_hints = 'on' hba_file = '/var/lib/postgresql/12/data/pg_hba.conf' ident_file = '/var/lib/postgresql/12/data/pg_ident.conf' # recovery.conf recovery_target = '' recovery_target_lsn = '' recovery_target_name = '' recovery_target_time = '' recovery_target_timeline = 'latest' recovery_target_xid = ''
Cela a un impact évident sur la maintenance du paramétrage (procédures à adapter). La configuration exhaustive est conservée dans « /var/lib/postgresql/12/data/postgresql.base.conf », fichier qui est inclus au démarrage par clause « include ‘postgresql.base.conf’ ». Les paramètres contrôlés par patroni le sont avec patronictl.
Je démarre patroni sur les 2 autres serveurs :
root@postgres02:~# systemctl start patroni root@postgres02:~# systemctl status patroni
On voit bien dans les processus la mise en place progressive du premier standby :
root@postgres02:~# ps -fu postgres … postgres 1571 1 0 11:19 ? 00:00:00 /usr/bin/python3 /usr/local/bin/patroni /etc/patroni.yml root@postgres02:~# ps -fu postgres UID PID PPID C STIME TTY TIME CMD … postgres 1571 1 0 11:19 ? 00:00:00 /usr/bin/python3 /usr/local/bin/patroni /etc/patroni.yml postgres 1590 1 0 11:19 ? 00:00:00 /usr/lib/postgresql/12/bin/postgres -D /var/lib/postgresql/12/data --config-file=/var/lib/postgresql/1 postgres 1592 1590 0 11:19 ? 00:00:00 postgres: my-pg-cluster: lt;strong gt;startup waiting for 000000020000000000000003 lt;/strong gt; postgres 1593 1590 0 11:19 ? 00:00:00 postgres: my-pg-cluster: checkpointer postgres 1594 1590 0 11:19 ? 00:00:00 postgres: my-pg-cluster: background writer postgres 1595 1590 0 11:19 ? 00:00:00 postgres: my-pg-cluster: stats collector postgres 1603 1590 0 11:19 ? 00:00:00 postgres: my-pg-cluster: postgres postgres 127.0.0.1(42670) idle   root@postgres02:~# ps -fu postgres UID PID PPID C STIME TTY TIME CMD … postgres 1571 1 0 11:19 ? 00:00:00 /usr/bin/python3 /usr/local/bin/patroni /etc/patroni.yml postgres 1590 1 0 11:19 ? 00:00:00 /usr/lib/postgresql/12/bin/postgres -D /var/lib/postgresql/12/data --config-file=/var/lib/postgresql/1 postgres 1592 1590 0 11:19 ? 00:00:00 postgres: my-pg-cluster: startup recovering 000000020000000000000003 postgres 1593 1590 0 11:19 ? 00:00:00 postgres: my-pg-cluster: checkpointer postgres 1594 1590 0 11:19 ? 00:00:00 postgres: my-pg-cluster: background writer postgres 1595 1590 0 11:19 ? 00:00:00 postgres: my-pg-cluster: stats collector postgres 1603 1590 0 11:19 ? 00:00:00 postgres: my-pg-cluster: postgres postgres 127.0.0.1(42670) idle postgres 1608 1590 0 11:19 ? 00:00:00 postgres: my-pg-cluster: walreceiver streaming 0/3000148 root@postgres02:~#
La trace de patroni est claire en indiquant que le second membre suit le leader déjà établi :
root@postgres02:~# vi /var/log/patroni/my-pg-cluster/patroni.log 2021-01-29 11:19:05,551 INFO: Selected new etcd server http://10.234.76.28:2379 2021-01-29 11:19:05,559 INFO: No PostgreSQL configuration items changed, nothing to reload. 2021-01-29 11:19:05,568 INFO: Lock owner: patroni01; I am patroni02 2021-01-29 11:19:05,570 INFO: trying to bootstrap from leader 'patroni01' 2021-01-29 11:19:06,861 INFO: replica has been created using basebackup 2021-01-29 11:19:06,862 INFO: bootstrapped from leader 'patroni01' 2021-01-29 11:19:06,875 WARNING: Removing unexpected parameter=wal_keep_segment value=8 from the config 2021-01-29 11:19:07,160 INFO: postmaster pid=1590 2021-01-29 11:19:08,219 INFO: Lock owner: patroni01; I am patroni02 2021-01-29 11:19:08,219 INFO: does not have lock 2021-01-29 11:19:08,219 INFO: establishing a new patroni connection to the postgres cluster 2021-01-29 11:19:08,268 INFO: no action. i am a secondary and i am following a leader 2021-01-29 11:19:15,183 INFO: Lock owner: patroni01; I am patroni02 2021-01-29 11:19:15,183 INFO: does not have lock 2021-01-29 11:19:15,186 INFO: no action. i am a secondary and i am following a leader 2021-01-29 11:19:25,183 INFO: Lock owner: patroni01; I am patroni02 2021-01-29 11:19:25,183 INFO: does not have lock 2021-01-29 11:19:25,186 INFO: no action. i am a secondary and i am following a leader 2021-01-29 11:19:35,183 INFO: Lock owner: patroni01; I am patroni02 2021-01-29 11:19:35,183 INFO: does not have lock
La configuration de postgresql sur postgres02 met en évidence le paramétrage spécifique dans le fichier de configuration patroni pour que le standby se connecte au primary en reprenant les éléments de configuration choisis tel l’utilisateur « replicator » ou le fichier mot de passe « /tmp/pgpass0 ». L’adresse IP du host « primary » auquel se connecter est bien celle de postgres01 (10.234.76.13):
root@postgres02:~# more /var/lib/postgresql/12/data/postgresql.conf # Do not edit this file manually! # It will be overwritten by Patroni! include 'postgresql.base.conf' checkpoint_timeout = '30' cluster_name = 'my-pg-cluster' hot_standby = 'on' listen_addresses = '*' max_connections = '100' max_locks_per_transaction = '64' max_prepared_transactions = '0' max_replication_slots = '10' max_wal_senders = '5' max_worker_processes = '8' port = '5433' track_commit_timestamp = 'off' unix_socket_directories = '.' wal_keep_segments = '8' wal_level = 'replica' wal_log_hints = 'on' hba_file = '/var/lib/postgresql/12/data/pg_hba.conf' ident_file = '/var/lib/postgresql/12/data/pg_ident.conf'
Configuration recovery.conf :
primary_conninfo = 'user=replicator passfile=/tmp/pgpass0 host=10.234.76.13 port=5433 sslmode=prefer application_name=patroni02 gssencmode=prefer primary_slot_name = 'patroni02' recovery_target = '' recovery_target_lsn = '' recovery_target_name = '' recovery_target_time = '' recovery_target_timeline = 'latest' recovery_target_xid = ''
Démarrage de patroni sur le 3ème noeud postgres03 :
root@postgres03:~# systemctl start patroni root@postgres03:~# systemctl status patroni ... patroni.service - Patroni instance for high-availability of PostgreSQL Loaded: loaded (/etc/systemd/system/patroni.service; disabled; vendor preset: enabled) Active: active (running) since Fri 2021-01-29 11:30:35 UTC; 57s ago Main PID: 1579 (patroni) Tasks: 12 (limit: 2348) CGroup: /system.slice/patroni.service ├─1579 /usr/bin/python3 /usr/local/bin/patroni /etc/patroni.yml ├─1599 /usr/lib/postgresql/12/bin/postgres -D /var/lib/postgresql/12/data --config-file=/var/lib/postgresql/12/data/postgresql.conf --liste ├─1602 postgres: my-pg-cluster: startup recovering 000000020000000000000007 ├─1603 postgres: my-pg-cluster: checkpointer ├─1604 postgres: my-pg-cluster: background writer ├─1605 postgres: my-pg-cluster: stats collector ├─1614 postgres: my-pg-cluster: postgres postgres 127.0.0.1(42770) idle └─1620 postgres: my-pg-cluster: walreceiver streaming 0/7000148
3. Scénarios d’exploitation
3.1. Redémarrage des instances patroni
Arrêts successifs, tout d’abord les 2 replica / standby :
# systemctl stop patroni
Après redémarrage des machines et des etcd, on redémarre patroni en premier sur postgres03. Ceci a pour effet de désigner postgres03 en « leader » et promouvoir postgres en primaire (avec passage à la timeline numéro 3) :
root@postgres03:~# systemctl start patroni root@postgres03:~# systemctl status patroni
Malgré des erreurs au démarrage, patroni sur postgres02 finit par raccrocher le flux sur la timeline 3 :
root@postgres02:~# systemctl status patroni patroni.service - Patroni instance for high-availability of PostgreSQL Loaded: loaded (/etc/systemd/system/patroni.service; disabled; vendor preset: enabled) Active: active (running) since Mon 2021-02-01 08:31:29 UTC; 18s ago Main PID: 1187 (patroni) Tasks: 12 (limit: 2348) CGroup: /system.slice/patroni.service ├─1187 /usr/bin/python3 /usr/local/bin/patroni /etc/patroni.yml ├─1210 /usr/lib/postgresql/12/bin/postgres -D /var/lib/postgresql/12/data --config-file=/var/lib/postgresql/12/data/postgresql.conf --liste ├─1212 postgres: my-pg-cluster: startup recovering 000000030000000000000008 ├─1213 postgres: my-pg-cluster: checkpointer ├─1214 postgres: my-pg-cluster: background writer ├─1215 postgres: my-pg-cluster: stats collector ├─1224 postgres: my-pg-cluster: postgres postgres 127.0.0.1(56750) idle └─1234 postgres: my-pg-cluster: walreceiver streaming 0/8000310 Feb 01 08:31:31 postgres02 patroni[1187]: 2021-02-01 08:31:31.244 UTC [1212] LOG: invalid record length at 0/8000148: wanted 24, got 0 Feb 01 08:31:31 postgres02 patroni[1187]: 2021-02-01 08:31:31.256 UTC [1216] LOG: fetching timeline history file for timeline 3 from primary server Feb 01 08:31:31 postgres02 patroni[1187]: 2021-02-01 08:31:31.260 UTC [1216] FATAL: could not start WAL streaming: ERROR: replication slot "patroni02" Feb 01 08:31:31 postgres02 patroni[1187]: 2021-02-01 08:31:31.262 UTC [1212] LOG: new target timeline is 3 Feb 01 08:31:31 postgres02 patroni[1187]: 2021-02-01 08:31:31.269 UTC [1217] FATAL: could not start WAL streaming: ERROR: replication slot "patroni02" Feb 01 08:31:31 postgres02 patroni[1187]: 2021-02-01 08:31:31.275 UTC [1218] FATAL: could not start WAL streaming: ERROR: replication slot "patroni02" Feb 01 08:31:32 postgres02 patroni[1187]: localhost:5433 - accepting connections Feb 01 08:31:32 postgres02 patroni[1187]: localhost:5433 - accepting connections Feb 01 08:31:36 postgres02 patroni[1187]: 2021-02-01 08:31:36.278 UTC [1230] FATAL: could not start WAL streaming: ERROR: replication slot "patroni02" Feb 01 08:31:41 postgres02 patroni[1187]: 2021-02-01 08:31:41.283 UTC [1234] LOG: started streaming WAL from primary at 0/8000000 on timeline 3
3.2. Incident sur un secondary
On procède à un arrêt brutal de l’instance sur postgres01 par interruption de son postmaster :
root@postgres01:~# kill -9 1281
On constate le redémarrage immédiat de l’instance :
root@postgres01:~# date Mon Feb 1 10:37:58 UTC 2021 root@postgres01:~# systemctl status patroni patroni.service - Patroni instance for high-availability of PostgreSQL Loaded: loaded (/etc/systemd/system/patroni.service; disabled; vendor preset: enabled) Active: active (running) since Mon 2021-02-01 08:56:26 UTC; 1h 41min ago Main PID: 1249 (patroni) Tasks: 12 (limit: 2348) CGroup: /system.slice/patroni.service ├─1249 /usr/bin/python3 /usr/local/bin/patroni /etc/patroni.yml ├─1482 /usr/lib/postgresql/12/bin/postgres -D /var/lib/postgresql/12/data --config-file=/var/lib/postgresql/12/data/postgresql.conf --liste ├─1484 postgres: my-pg-cluster: startup recovering 000000030000000000000009 ├─1488 postgres: my-pg-cluster: checkpointer ├─1489 postgres: my-pg-cluster: background writer ├─1490 postgres: my-pg-cluster: stats collector ├─1491 postgres: my-pg-cluster: walreceiver └─1494 postgres: my-pg-cluster: postgres postgres 127.0.0.1(57620) idle Feb 01 10:37:20 postgres01 patroni[1249]: localhost:5433 - rejecting connections Feb 01 10:37:20 postgres01 patroni[1249]: 2021-02-01 10:37:20.911 UTC [1487] FATAL: the database system is starting up Feb 01 10:37:20 postgres01 patroni[1249]: localhost:5433 - rejecting connections Feb 01 10:37:20 postgres01 patroni[1249]: 2021-02-01 10:37:20.925 UTC [1484] LOG:  entering standby mode Feb 01 10:37:20 postgres01 patroni[1249]: 2021-02-01 10:37:20.928 UTC [1484] LOG:  redo starts at 0/9000060 Feb 01 10:37:20 postgres01 patroni[1249]: 2021-02-01 10:37:20.929 UTC [1484] LOG:  consistent recovery state reached at 0/9000148 Feb 01 10:37:20 postgres01 patroni[1249]: 2021-02-01 10:37:20.929 UTC [1482] LOG:  database system is ready to accept read only connections Feb 01 10:37:20 postgres01 patroni[1249]: 2021-02-01 10:37:20.929 UTC [1484] LOG:  invalid record length at 0/9000148: wanted 24, got 0 Feb 01 10:37:20 postgres01 patroni[1249]: 2021-02-01 10:37:20.936 UTC [1491] LOG:  started streaming WAL from primary at 0/9000000 on timeline 3 Feb 01 10:37:21 postgres01 patroni[1249]: localhost:5433 - accepting connections
Le log patroni est clair sur l’enchaînement des événements. Après détection de l’arrêt de PostgreSQL, la vérification de l’état de l’instance est faite avec pg_controldata, puis avant de la redémarrer, le verrou partagé est aussi vérifié. Il est détenu par patroni03 alors patroni01 est redémarré au même état que précédemment, en secondary. Evidemment, nous restons sur la timeline 3, aucune promotion n’ayant été nécessaire.
2021-02-01 10:37:10,617 INFO: no action. i am a secondary and i am following a leader 2021-02-01 10:37:20,611 WARNING: Postgresql is not running. 2021-02-01 10:37:20,611 INFO: Lock owner: patroni03; I am patroni01 2021-02-01 10:37:20,619 INFO: pg_controldata: pg_control version number: 1201 Catalog version number: 201909212 Database system identifier: 6923127770418349556 Database cluster state: in archive recovery … 2021-02-01 10:37:20,635 INFO: Lock owner: patroni03; I am patroni01 2021-02-01 10:37:20,637 INFO: starting as a secondary 2021-02-01 10:37:20,638 INFO: closed patroni connection to the postgresql cluster
3.3. incident sur le primary
Même arrêt brutal, cette fois sur l’instance patroni03 actuelle leader (« lock owner »).
On constate là aussi un redémarrage rétablissant le service toujours avec patroni03 en instance primary mais avec changement de timeline, passant à la numéro 4. Passée la vérification pg_controldata que le statut de l’instance « in production » est erronée, celle-ci est redémarrée pour exécuter un « crash recovery » puis de nouveau stoppée correctement pour être au statut « shut down ».
Possédant toujours le « leader lock », patroni03 fait une auto-promotion de son instance PostgreSQL passant ainsi à la timeline numéro 4, ceci comme pour chaque redémarrage et sans lien avec l’incident.
root@postgres03:~# systemctl status patroni patroni.service - Patroni instance for high-availability of PostgreSQL Loaded: loaded (/etc/systemd/system/patroni.service; disabled; vendor preset: enabled) Active: active (running) since Mon 2021-02-01 08:10:56 UTC; 3h 16min ago Main PID: 1137 (patroni) Tasks: 15 (limit: 2348) CGroup: /system.slice/patroni.service ├─1137 /usr/bin/python3 /usr/local/bin/patroni /etc/patroni.yml ├─1863 /usr/lib/postgresql/12/bin/postgres -D /var/lib/postgresql/12/data --config-file=/var/lib/postgresql/12/data/postgresql.conf --liste ├─1869 postgres: my-pg-cluster: checkpointer ├─1870 postgres: my-pg-cluster: background writer ├─1871 postgres: my-pg-cluster: stats collector ├─1873 postgres: my-pg-cluster: postgres postgres 127.0.0.1(57950) idle ├─1878 postgres: my-pg-cluster: walwriter ├─1879 postgres: my-pg-cluster: autovacuum launcher ├─1880 postgres: my-pg-cluster: logical replication launcher ├─1885 postgres: my-pg-cluster: walsender replicator 10.234.76.28(45066) streaming 0/B000268 └─1886 postgres: my-pg-cluster: walsender replicator 10.234.76.45(44232) streaming 0/B000268 ... Feb 01 11:23:51 postgres03 patroni[1137]: 2021-02-01 11:23:51.281 UTC [1866] LOG: received promote request Feb 01 11:23:51 postgres03 patroni[1137]: 2021-02-01 11:23:51.282 UTC [1866] LOG: redo is not required Feb 01 11:23:51 postgres03 patroni[1137]: server promoting Feb 01 11:23:51 postgres03 patroni[1137]: 2021-02-01 11:23:51.285 UTC [1866] LOG: selected new timeline ID: 4 Feb 01 11:23:51 postgres03 patroni[1137]: 2021-02-01 11:23:51.400 UTC [1866] LOG: archive recovery complete Feb 01 11:23:51 postgres03 patroni[1137]: 2021-02-01 11:23:51.412 UTC [1863] LOG: database system is ready to accept connections
Log patroni :
2021-02-01 11:23:40,597 INFO: Lock owner: patroni03; I am patroni03 2021-02-01 11:23:40,601 INFO: no action. i am the leader with the lock 2021-02-01 11:23:50,597 WARNING: Postgresql is not running. 2021-02-01 11:23:50,597 INFO: Lock owner: patroni03; I am patroni03 2021-02-01 11:23:50,604 INFO: pg_controldata: pg_control version number: 1201 Catalog version number: 201909212 Database system identifier: 6923127770418349556 Database cluster state: in production pg_control last modified: Mon Feb 1 11:18:39 2021 Latest checkpoint location: 0/A000098 Latest checkpoint's REDO location: 0/A000060 Latest checkpoint's REDO WAL file: 00000003000000000000000A … 2021-02-01 11:23:50,874 INFO: Lock owner: patroni03; I am patroni03 2021-02-01 11:23:50,874 INFO: Lock owner: patroni03; I am patroni03 2021-02-01 11:23:50,885 INFO: starting as readonly because i had the session lock 2021-02-01 11:23:50,886 INFO: closed patroni connection to the postgresql cluster 2021-02-01 11:23:50,896 WARNING: Removing unexpected parameter=wal_keep_segment value=8 from the config 2021-02-01 11:23:51,163 INFO: postmaster pid=1863 2021-02-01 11:23:51,219 INFO: Lock owner: patroni03; I am patroni03 2021-02-01 11:23:51,219 INFO: establishing a new patroni connection to the postgres cluster 2021-02-01 11:23:51,260 INFO: promoted self to leader because i had the session lock 2021-02-01 11:23:51,283 INFO: cleared rewind state after becoming the leader 2021-02-01 11:23:52,304 INFO: Lock owner: patroni03; I am patroni03 2021-02-01 11:23:52,337 INFO: no action. i am the leader with the lock 2021-02-01 11:23:52,339 INFO: Lock owner: patroni03; I am patroni03 2021-02-01 11:23:52,344 INFO: no action. i am the leader with the lock
La trace des 2 autres instances patroni 01 et 02 ne montre aucune incidence et aucun changement sur les instances standby (si ce n’est le changement de timeline).
3.4. Modification de paramètre statique
Le fichier postgresql.conf est non éditable dans le contexte patroni car maintenu automatiquement par patroni. On a la main sur le fichier postgresql.base.conf qui est inclu au démarrage.
Sur les 3 instances, on active logging_collector=on en laissant le paramétrage log_% à ses valeurs par défaut :
root@postgres03:/var/lib/postgresql/12/data# grep logging_collector /var/lib/postgresql/12/data/postgresql.base.conf # requires logging_collector to be on. #logging_collector = off# Enable capturing of stderr and csvlog logging_collector = on
Au redémarrage (systemctl start patroni), on voit le dossier $PGDATA/log créé et un premier fichier log généré:
root@postgres03:/var/lib/postgresql/12/data# ls -l /var/lib/postgresql/12/data/log
total 2
-rw——- 1 postgres postgres 1110 Feb 1 13:55 postgresql-2021-02-01_135506.log
3.5. Arrêt du serveur de l’instance leader (et changement de leader)
On stoppe le serveur postgres03, ce qui aura pour effet de stopper les 2 composants etcd et patroni+PostgreSQL :
On constate avec les processus de l’instance sur postgres02 qu’elle a été promue et d’après la trace patroni, que l’absence de réponse de patroni03 est relevée, la présence de patroni01 est également signifiée.
Du coup, patroni02 se promeut leader (nouvelle timeline 6) et acquière le verrou en ce sens :
2021-02-01 14:45:57,498 INFO: no action. i am a secondary and i am following a leader 2021-02-01 14:46:07,494 INFO: Lock owner: patroni03; I am patroni02 2021-02-01 14:46:07,494 INFO: does not have lock 2021-02-01 14:46:07,498 INFO: no action. i am a secondary and i am following a leader 2021-02-01 14:46:15,845 INFO: Got response from patroni01 http://10.234.76.28:8008/patroni: {"state": "running", "postmaster_start_time": "2021-02-01 14:04:08.646 UTC", "role": "replica", "server_version": 120005, "cluster_unlocked": true, "xlog": {"received_location": 218104256, "replayed_location": 218104256, "replayed_timestamp": null, "paused": false}, "timeline": 5, "database_system_identifier": "6923127770418349556", "patroni": {"version": "2.0.1", "scope": "my-pg-cluster"}} 2021-02-01 14:46:15,864 WARNING: Request failed to patroni03: GET http://10.234.76.13:8008/patroni (HTTPConnectionPool(host='10.234.76.13', port=8008): Max retries exceeded with url: /patroni (Caused by ProtocolError('Connection aborted.', ConnectionResetError(104, 'Connection reset by peer')))) 2021-02-01 14:46:15,955 WARNING: Could not activate Linux watchdog device: "Can't open watchdog device: [Errno 2] No such file or directory: '/dev/watchdog'" 2021-02-01 14:46:15,976 INFO: promoted self to leader by acquiring session lock 2021-02-01 14:46:15,992 INFO: cleared rewind state after becoming the leader 2021-02-01 14:46:17,043 INFO: Lock owner: patroni02; I am patroni02 2021-02-01 14:46:17,101 INFO: no action. i am the leader with the lock 2021-02-01 14:46:17,109 INFO: Lock owner: patroni02; I am patroni02 2021-02-01 14:46:17,126 INFO: no action. i am the leader with the lock 2021-02-01 14:46:27,109 INFO: Lock owner: patroni02; I am patroni02 2021-02-01 14:46:27,113 INFO: no action. i am the leader with the lock 2021-02-01 14:46:37,109 INFO: Lock owner: patroni02; I am patroni02 2021-02-01 14:46:37,113 INFO: no action. i am the leader with the lock
Du côté de patroni01, la même détection a été faite. Patroni01 était en concurrence pour obtenir le verrou (« leader race ») et a échoué :
2021-02-01 14:45:57,499 INFO: no action. i am a secondary and i am following a leader 2021-02-01 14:46:07,492 INFO: Lock owner: patroni03; I am patroni01 2021-02-01 14:46:07,492 INFO: does not have lock 2021-02-01 14:46:07,499 INFO: no action. i am a secondary and i am following a leader 2021-02-01 14:46:15,852 INFO: Got response from patroni02 http://10.234.76.45:8008/patroni: {"state": "running", "postmaster_start_time": "2021-02-01 14:03:51.153 UTC", "role": "replica", "server_version": 120005, "cluster_unlocked": true, "xlog": {"received_location": 218104256, "replayed_location": 218104256, "replayed_timestamp": null, "paused": false}, "timeline": 5, "database_system_identifier": "6923127770418349556", "patroni": {"version": "2.0.1", "scope": "my-pg-cluster"}} 2021-02-01 14:46:15,866 WARNING: Request failed to patroni03: GET http://10.234.76.13:8008/patroni (HTTPConnectionPool(host='10.234.76.13', port=8008): Max retries exceeded with url: /patroni (Caused by ProtocolError('Connection aborted.', ConnectionResetError(104, 'Connection reset by peer')))) 2021-02-01 14:46:15,977 INFO: Could not take out TTL lock 2021-02-01 14:46:16,048 INFO: following new leader after trying and failing to obtain lock 2021-02-01 14:46:21,111 INFO: closed patroni connection to the postgresql cluster 2021-02-01 14:46:21,115 WARNING: Removing unexpected parameter=wal_keep_segment value=8 from the config 2021-02-01 14:46:21,351 INFO: postmaster pid=2027 2021-02-01 14:46:21,418 INFO: Lock owner: patroni02; I am patroni01 2021-02-01 14:46:21,419 INFO: does not have lock 2021-02-01 14:46:21,419 INFO: establishing a new patroni connection to the postgres cluster 2021-02-01 14:46:21,432 INFO: Local timeline=6 lsn=0/D0001C0 2021-02-01 14:46:21,436 INFO: master_timeline=6 2021-02-01 14:46:21,452 INFO: no action. i am a secondary and i am following a leader 2021-02-01 14:46:27,123 INFO: Lock owner: patroni02; I am patroni01
On en profite pour tester la commande de supervision patronictl « patronictl -c /etc/patroni.yml list »:
root@postgres01:~# patronictl -c /etc/patroni.yml list + Cluster: my-pg-cluster (6923127770418349556) -----+----+-----------+ | Member | Host | Role | State | TL | Lag in MB | +-----------+-------------------+---------+---------+----+-----------+ | patroni01 | 10.234.76.28:5433 | Replica | running | 6 | 0 | | patroni02 | 10.234.76.45:5433 | Leader | running | 6 | | +-----------+-------------------+---------+---------+----+-----------+
Notre serveur patroni03, resté éteint, va être redémarré. Etant l’ancien leader, son instance postgresql devra possiblement subir une opération « pg_rewind » avant d’être remis dans le cluster.
root@ip-192-1-1-7:~# lxc start postgres03 root@postgres03:~# systemctl start etcd root@postgres03:~# systemctl start patroni
Peu de temps après le redémarrage, patroni03 est réintégré en « replica » sur la timeline (TL) numéro 5 avant de passer sur la TL 6:
root@postgres01:~# patronictl -c /etc/patroni.yml list + Cluster: my-pg-cluster (6923127770418349556) -----+----+-----------+ | Member | Host | Role | State | TL | Lag in MB | +-----------+-------------------+---------+---------+----+-----------+ | patroni01 | 10.234.76.28:5433 | Replica | running | 6 | 0 | | patroni02 | 10.234.76.45:5433 | Leader | running | 6 | | | patroni03 | 10.234.76.13:5433 | Replica | running | 5 | 0 | +-----------+-------------------+---------+---------+----+-----------+ root@postgres01:~# patronictl -c /etc/patroni.yml list + Cluster: my-pg-cluster (6923127770418349556) -----+----+-----------+ | Member | Host | Role | State | TL | Lag in MB | +-----------+-------------------+---------+---------+----+-----------+ | patroni01 | 10.234.76.28:5433 | Replica | running | 6 | 0 | | patroni02 | 10.234.76.45:5433 | Leader | running | 6 | | | patroni03 | 10.234.76.13:5433 | Replica | running | 6 | 0 | +-----------+-------------------+---------+---------+----+-----------+
Le log de patroni03 met en évidence le passage de patroni03 en secondary, mais pas d’allusion à une action de « rewind » du fait que l’instance postgresql se sera arrêtée proprement et qu’il n’y aura pas eu de divergence par rapport au point de basculement sur patroni02 :
2021-02-01 14:45:57,478 INFO: Lock owner: patroni03; I am patroni03 2021-02-01 14:45:57,483 INFO: no action. i am the leader with the lock 2021-02-01 14:46:07,478 INFO: Lock owner: patroni03; I am patroni03 2021-02-01 14:46:07,482 INFO: no action i am the leader with the lock 2021-02-01 16:12:41,998 INFO: Selected new etcd server http://10.234.76.28:2379 2021-02-01 16:12:42,022 INFO: No PostgreSQL configuration items changed, nothing to reload. 2021-02-01 16:12:42,045 WARNING: Postgresql is not running. 2021-02-01 16:12:42,045 INFO: Lock owner: patroni02; I am patroni03 2021-02-01 16:12:42,049 INFO: pg_controldata: pg_control version number: 1201 Catalog version number: 201909212 Database system identifier: 6923127770418349556 Database cluster state: shut down pg_control last modified: Mon Feb 1 14:46:15 2021 … 2021-02-01 16:12:42,051 INFO: Lock owner: patroni02; I am patroni03 2021-02-01 16:12:42,066 INFO: Local timeline=5 lsn=0/D000148 2021-02-01 16:12:42,103 INFO: master_timeline=6 2021-02-01 16:12:42,116 INFO: master: history=2 0/8000148 no recovery target specified 0/B0000A0 no recovery target specified ... 2021-02-01 16:12:42,119 INFO: Lock owner: patroni02; I am patroni03 2021-02-01 16:12:42,122 INFO: starting as a secondary 2021-02-01 16:12:42,127 WARNING: Removing unexpected parameter=wal_keep_segment value=8 from the config 2021-02-01 16:12:42,352 INFO: postmaster pid=494 2021-02-01 16:12:43,465 INFO: Lock owner: patroni02; I am patroni03 2021-02-01 16:12:43,465 INFO: does not have lock 2021-02-01 16:12:43,465 INFO: establishing a new patroni connection to the postgres cluster 2021-02-01 16:12:43,622 INFO: no action. i am a secondary and i am following a leader 2021-02-01 16:12:47,136 INFO: Lock owner: patroni02; I am patroni03 2021-02-01 16:12:47,136 INFO: does not have lock 2021-02-01 16:12:47,146 INFO: no action. i am a secondary and i am following a leader
3.6. Maintenance
Patroni ayant le contrôle sur les membres du cluster et les instances PostgreSQL, beaucoup de tâches de maintenance doivent passent par l’utilitaire de contrôle « patronictl ». Ainsi des commandes postgresql usuelles telle qu’un « reload » sont prises en charge par patroni.
Exemple de modification dynamique d’un paramètre. Dans le fichier postgresql.base.conf des 3 serveurs, on modifie work_mem de 4MB par défaut à 10MB :
root@postgres01:~# grep work_mem /var/lib/postgresql/12/data/postgresql.base.conf #work_mem = 4MB # min 64kB work_mem = 10MB
Avant rechargement, la valeur est bien à 4MB sur toutes les instances PostgreSQL :
postgres@postgres02:~$ psql -p 5432 psql (12.5 (Ubuntu 12.5-1.pgdg18.04+1)) Type "help" for help. postgres=# show work_mem ; work_mem ---------- 4MB (1 row)
On exécute un « reload », mais seulement sur l’instance « master » :
root@postgres01:~# patronictl -c /etc/patroni.yml reload -r master my-pg-cluster + Cluster: my-pg-cluster (6923127770418349556) -----+----+-----------+ | Member | Host | Role | State | TL | Lag in MB | +-----------+-------------------+---------+---------+----+-----------+ | patroni01 | 10.234.76.28:5433 | Replica | running | 7 | 0 | | patroni02 | 10.234.76.45:5433 | Leader | running | 7 | | | patroni03 | 10.234.76.13:5433 | Replica | running | 7 | 0 | +-----------+-------------------+---------+---------+----+-----------+ Are you sure you want to reload members patroni02? [y/N]: y Reload request received for member patroni02 and will be processed within 10 seconds
Dans le log postgresql de patroni02, le reload a bien été reçu :
postgres@postgres02:~/12/data/log$ tail -2f postgresql-2021-02-05_102121.log 2021-02-05 11:16:03.337 UTC [8313] LOG: received SIGHUP, reloading configuration files 2021-02-05 11:16:03.340 UTC [8313] LOG: parameter "work_mem" changed to "10MB"
Le changement est effectif sur patroni02 (leader):
postgres@postgres02:~/12/data/log$ psql -p 5433 -h localhost psql (12.5 (Ubuntu 12.5-1.pgdg18.04+1)) Type "help" for help. postgres=# show work_mem ; work_mem ---------- 10MB (1 row)
Alors qu’il n’en est rien sur patroni01 (replica) :
postgres@postgres01:~$ psql -p 5433 -h localhost psql (12.5 (Ubuntu 12.5-1.pgdg18.04+1)) Type "help" for help. postgres=# show work_mem; work_mem ---------- 4MB (1 row)
Cette fois on refait un « reload » sur tous les membres :
root@postgres01:~# patronictl -c /etc/patroni.yml reload -r any my-pg-cluster + Cluster: my-pg-cluster (6923127770418349556) -----+----+-----------+ ... Are you sure you want to reload members patroni01, patroni03, patroni02? [y/N]: y Reload request received for member patroni01 and will be processed within 10 seconds Reload request received for member patroni03 and will be processed within 10 seconds Reload request received for member patroni02 and will be processed within 10 seconds
Le changement est bien effectif sur patroni01 (replica) :
postgres=# show work_mem; work_mem ---------- 10MB (1 row)
Conclusion
Patroni est une solution robuste présentant un niveau élevé d’intégration de PostgreSQL dans sa mise en place des instances sur les différents membres du cluster et de la réplication entre elles, ainsi que dans ses fonctions de supervision et de maintenance. Le fonctionnement avec un DCS (tel l’ETCD) garantit la haute disponibilité de service et évite les situations de “split brain” avec plusieurs “leader/master”.
Au contraire de Corosync/Pacemaker, patroni ne gère pas les instances PostgreSQL en “ressources” et ne nécessite pas pour ce faire d’agent tel que PAF requis pour pacemaker ou d’un agent pour de l’évincement de noeuds (fencing agent). L’intégrité du cluster est garantie par le DCS et la donnée de leader en acquisition mutuellement exclusive par les membres du cluster.
Une contrepartie de cette non-gestion de ressources est l’absence de ressource pour une adresse IP virtuelle (VIP), comme cela est le cas avec Corosync/Pacemaker. Il faut songer à implémenter une gestion de la VIP en point d’entrée unique pour la connexion au cluster soit via un script s’exécutant après un changement de rôle d’un membre et instanciant la VIP localement au serveur si celui-ci est leader ou au contraire supprimant cette adresse si le membre est passé de leader à replica (section callbacks de la configuration Patroni); ou via une solution externalisée de gestion des adresses IP virtuelles telle Netscaler ou F5 (pouvant interroger l’interface API REST des clusters patroni pour savoir qui est leader et modifier l’adresse VIP en conséquence, adresse caractérisée par un nom d’adresse unique dans le domaine).
Pour aller plus loin avec Patroni:
– Comparatif des gestionnaires de VIP dans un cluster Patroni : épisode 1 (KEEPALIVED)
– Comparatif des gestionnaires de VIP dans un cluster Patroni : épisode 2 (VIP-MANAGER)
Continuez votre lecture sur le blog :
- Comparatif des gestionnaires de VIP dans un cluster Patroni : épisode 2 (VIP-MANAGER) (David Baffaleuf) [ContainerPostgreSQL]
- Comparatif des gestionnaires de VIP dans un cluster Patroni : épisode 1 (KEEPALIVED) (David Baffaleuf) [ContainerPostgreSQL]
- PostgreSQL : la streaming replication en 12. (Emmanuel RAMI) [PostgreSQL]
- AWS : Configurer un cluster PostgreSQL HD avec Corosync/Pacemaker sur des EC2 Amazon (Emmanuel RAMI) [AWSPostgreSQL]
- Kubegres : l’opérateur Kubernetes clé en main pour PostgreSQL (David Baffaleuf) [ContainerDevopsPostgreSQL]
Hello
merci pour cet article !
Un petit point tout de même, un “kill” brutal du process “postgres -D” du master ne fait pas basculer le cluster comme peut le faire Corosync/Pacemaker avec PAF.
Du coup, il y a un mécanisme de supervision qui doit être mis en place afin de pouvoir redémarrer rapidement le nœud primaire pour qu’il se promotionne lui même. Sans ca, l’activité transactionnelle est bloquée !
Ou bien carrément faire un arrêt complet des process patroni et etcd pour promouvoir une standby en master.
Pour la partie VIP on pourra utiliser “KeepAlived’ par exemple !
Merci encore !
🙂
Bonjour Emmanuel,
Merci beaucoup pour ton commentaire. En fait, patroni cherche bien à redémarrer le master avant de tenter de basculer, c’est ce que le test montre. Par contre, on peut souhaiter que patroni bascule plus ou moins rapidement sur le replica en cas d’échec répété sur le redémarrage du master avec ce paramètre:
master_start_timeout: the amount of time a master is allowed to recover from failures before failover is triggered (in seconds). Default is 300 seconds. When set to 0 failover is done immediately after a crash is detected if possible. When using asynchronous replication a failover can cause lost transactions. Worst case failover time for master failure is: loop_wait + master_start_timeout + loop_wait, unless master_start_timeout is zero, in which case it’s just loop_wait. Set the value according to your durability/availability tradeoff.