2

Haute disponibilité de PostgreSQL avec Patroni

twitterlinkedinmail

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 :

twitterlinkedinmail

Ludovic AUGEREAU

2 commentaires

  1. 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 !

    🙂

  2. 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.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.