Proof of concept
Franck BOUDEHEN
v 1.2 (1 avril 2020)
création des 3 containers :
# apt install sudo
sudo apt update
sudo apt upgrade -y
sudo apt install -y curl wget sudo vim
# en tant que root
$ sudo -s
...
curl -s https://api.github.com/repos/etcd-io/etcd/releases/latest \
| grep browser_download_url \
| grep linux-amd64 \
| cut -d '"' -f 4 \
| wget -qi -
tar xzf etcd-v3.4.1-linux-amd64.tar
cd etcd-v3.4.1-linux-amd64
$ sudo mv etcd* /usr/local/bin/
$ sudo useradd -m -d /var/lib/etcd etcd
Configuration du premier nœud etcd, changer le nom et l’adresse ip pour les 2 suivants :
# /etc/etcd/etcd.conf.yml
# Human-readable name for this member.
name: 'etcd1'
# Path to the data directory.
data-dir: /var/lib/etcd/my-cluster.etcd
# List of comma separated URLs to listen on for peer traffic.
listen-peer-urls: http://10.0.3.64:2380
# List of comma separated URLs to listen on for client traffic.
# remplacer par l'ip de etcd2
listen-client-urls: http://localhost:2379,http://10.0.3.64:2379
# The URLs needed to be a comma-separated list.
# remplacer par l'ip de etcd2
initial-advertise-peer-urls: http://10.0.3.64:2380
# List of this member's client URLs to advertise to the public.
# The URLs needed to be a comma-separated list.
# remplacer par l'ip de etcd2
advertise-client-urls: http://10.0.3.64:2379
# Initial cluster configuration for bootstrapping.
initial-cluster: etcd1=http://10.0.3.64:2380,etcd2=http://10.0.3.78:2380
# Initial cluster token for the etcd cluster during bootstrap.
initial-cluster-token: 'patroni'
# Initial cluster state ('new' or 'existing').
initial-cluster-state: 'new'
Vérification depuis l’extérieur :
nmap -p 2379-2380 10.0.3.64,78
Starting Nmap 7.40 ( https://nmap.org ) at 2019-10-09 15:57 CEST
Nmap scan report for 10.0.3.64
Host is up (0.000074s latency).
PORT STATE SERVICE
2379/tcp open etcd-client
2380/tcp open etcd-server
Nmap scan report for 10.0.3.78
Host is up (0.00014s latency).
PORT STATE SERVICE
2379/tcp open etcd-client
2380/tcp open etcd-server
La présence de 2 nœuds n’est pas suffisante pour la haute disponibilité des clusters. Etcd s’arrête si le quorum n’est pas respecté (nombre de nœuds/2 +1 disponibles). D’autre part, les instances patroni ne peuvent démarrer que si le nœud n°1 du cluster etcd est démarré (contact initial au cluster etcd). Nous proposons donc de placer un proxy tcp (HAProxy) sur les instances pour qu’elles puissent démarrer quand même, malgré l’absence du nœud etcd1, en configurant un roundrobin sur les 3 nœuds etcd.
Configuration du nœud
sudo apt update
sudo apt upgrade -y
sudo apt install -y curl wget sudo vim
# en tant que root
$ sudo -s
...
curl -s https://api.github.com/repos/etcd-io/etcd/releases/latest \
| grep browser_download_url \
| grep linux-amd64 \
| cut -d '"' -f 4 \
| wget -qi -
gzip -d etcd-v3.4.1-linux-amd64.tar.gz
tar xf etcd-v3.4.1-linux-amd64.tar
cd etcd-v3.4.1-linux-amd64
$ sudo mv etcd* /usr/local/bin/
$ sudo useradd -m -d /var/lib/etcd etcd
La configuration etcd
devra indiquer les 3 nœuds du cluster, elle devra donc être modifiée sur chaque nœud etcd
.
La chaîne
initial-cluster: etcd1=http://10.0.3.64:2380,etcd2=http://10.0.3.78:2380,etcd3=http://10.0.3.32:2380
# /etc/etcd/etcd.conf.yml
# Human-readable name for this member.
name: 'etcd3'
# Path to the data directory.
data-dir: /var/lib/etcd/my-cluster.etcd
# List of comma separated URLs to listen on for peer traffic.
listen-peer-urls: http://10.0.3.32:2380
# List of comma separated URLs to listen on for client traffic.
# remplacer par l'ip de etcd3
listen-client-urls: http://localhost:2379,http://10.0.3.32:2379
# The URLs needed to be a comma-separated list.
# remplacer par l'ip de etcd3
initial-advertise-peer-urls: http://10.0.3.32:2380
# List of this member's client URLs to advertise to the public.
# The URLs needed to be a comma-separated list.
# remplacer par l'ip de etcd3
advertise-client-urls: http://10.0.3.32:2379
# Initial cluster configuration for bootstrapping.
initial-cluster: etcd1=http://10.0.3.64:2380,etcd2=http://10.0.3.78:2380,etcd3=http://10.0.3.32:2380
# Initial cluster token for the etcd cluster during bootstrap.
initial-cluster-token: 'patroni'
# Initial cluster state ('new' or 'existing').
initial-cluster-state: 'new'
Création des containers PostgreSQL/Patroni :
$ sudo lxc-create -n pg_patroni1 -t debian -- -r stretch
$ sudo lxc-create -n pg_patroni2 -t debian -- -r stretch
# apt update && apt upgrade -y
ajout du dépôt pgdg
Installation de Patroni :
$ curl https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
$ sudo apt update
$ sudo apt install postgresql-12
$ sudo apt install python3-pip python3-psycopg2
$ sudo pip3 install python-etcd
$ sudo pip3 install patroni
$ sudo patroni
Usage: /usr/local/bin/patroni config.yml
Patroni may also read the configuration from the PATRONI_CONFIGURATION environment variable
Création du fichier /etc/patroni/pg1.yml
scope: my-ha-cluster
name: pg-1
restapi:
listen: 0.0.0.0:8008
connect_address: 127.0.0.1:8008
etcd:
host: 10.0.3.64:2379
bootstrap:
dcs:
ttl: 30
loop_wait: 10
retry_timeout: 10
maximum_lag_on_failover: 1048576
postgresql:
use_pg_rewind: true
use_slots: true
parameters:
wal_level: replica
hot_standby: "on"
wal_keep_segment: 8
max_wal_senders: 5
max_relication_slots: 5
checkpoint_timeout: 30
initdb: UTF8
pg_hba:
- host all dba all md5
- host replication repl all md5
users:
dba:
password: secret
options:
- createrole
- createdb
repl:
password: secret
options:
- replication:
postgresql:
listen: 10.0.3.141:5434
connect_address: 10.0.3.141:5434
data_dir: /var/lib/postgresql/data
bin_dir: /usr/lib/postgresql/12/bin
authentication:
replication:
username: repl
password: secret
superuser:
username: dba
password: secret
parameters:
unix_socket_directories: '/tmp'
version patroni 2.0.0
scope: my-ha-cluster
name: pg-1
restapi:
listen: 0.0.0.0:8008
connect_address: 127.0.0.1:8008
etcd:
host: 10.0.3.244:2379
bootstrap:
dcs:
ttl: 30
loop_wait: 10
retry_timeout: 10
maximum_lag_on_failover: 1048576
postgresql:
use_pg_rewind: true
use_slots: true
parameters:
wal_level: replica
hot_standby: "on"
wal_keep_segment: 8
max_wal_senders: 5
max_relication_slots: 5
checkpoint_timeout: 30
initdb:
- encoding: UTF8
- data-checksums
pg_hba:
- host all dba all md5
- host replication repl all md5
users:
dba:
password: secret
options:
- createrole
- createdb
repl:
password: secret
options:
- replication
postgresql:
listen: 10.0.3.230:5434
connect_address: 10.0.3.230:5434
data_dir: /var/lib/postgresql/data
bin_dir: /usr/lib/postgresql/12/bin
authentication:
replication:
username: repl
password: secret
superuser:
username: dba
password: secret
parameters:
unix_socket_directories: '/tmp'
lancement de patroni
2019-10-09 15:08:24,783 INFO: Failed to import patroni.dcs.consul
2019-10-09 15:08:24,793 INFO: Selected new etcd server http://10.0.3.64:2379
2019-10-09 15:08:24,800 INFO: No PostgreSQL configuration items changed, nothing to reload.
2019-10-09 15:08:24,809 WARNING: Postgresql is not running.
2019-10-09 15:08:24,810 INFO: Lock owner: None; I am pg-1
2019-10-09 15:08:24,812 INFO: pg_controldata:
Backup start location: 0/0
Latest checkpoint's oldestCommitTsXid: 0
Database cluster state: shut down
Size of a large-object chunk: 2048
pg_control version number: 1201
Bytes per WAL segment: 16777216
max_wal_senders setting: 10
Float8 argument passing: by value
Latest checkpoint's REDO WAL file: 0000000A0000000000000003
Maximum size of a TOAST chunk: 1996
Latest checkpoint's NextXID: 0:492
Latest checkpoint's NextOID: 16393
wal_level setting: replica
track_commit_timestamp setting: off
Mock authentication nonce: 8acd62300c302ccd7e96b515e1de24171c625933d7c483e3b056e0ed955acf66
Blocks per segment of large relation: 131072
Database system identifier: 6745811559071353594
Latest checkpoint's TimeLineID: 10
pg_control last modified: Wed Oct 9 15:07:16 2019
Latest checkpoint's REDO location: 0/301DFA0
max_connections setting: 100
Time of latest checkpoint: Wed Oct 9 15:07:16 2019
Database block size: 8192
max_worker_processes setting: 8
Backup end location: 0/0
Latest checkpoint's newestCommitTsXid: 0
Latest checkpoint's NextMultiXactId: 1
Latest checkpoint's oldestXID: 480
Maximum data alignment: 8
WAL block size: 8192
Latest checkpoint's oldestMulti's DB: 1
Maximum length of identifiers: 64
Latest checkpoint's oldestXID's DB: 1
Latest checkpoint location: 0/301DFA0
Data page checksum version: 0
End-of-backup record required: no
Latest checkpoint's full_page_writes: on
Float4 argument passing: by value
Min recovery ending loc's timeline: 0
Latest checkpoint's oldestMultiXid: 1
max_locks_per_xact setting: 64
wal_log_hints setting: on
Date/time type storage: 64-bit integers
Latest checkpoint's oldestActiveXID: 0
Latest checkpoint's PrevTimeLineID: 10
Maximum columns in an index: 32
Fake LSN counter for unlogged rels: 0/1
max_prepared_xacts setting: 0
Latest checkpoint's NextMultiOffset: 0
Minimum recovery ending location: 0/0
Catalog version number: 201909212
2019-10-09 15:08:24,814 INFO: Lock owner: None; I am pg-1
2019-10-09 15:08:24,822 INFO: Lock owner: None; I am pg-1
2019-10-09 15:08:24,827 INFO: starting as a secondary
2019-10-09 15:08:24,843 INFO: postmaster pid=11557
10.0.3.141:5432 - no response
2019-10-09 15:08:24.858 UTC [11557] LOG: starting PostgreSQL 12.0 (Debian 12.0-
1.pgdg90+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 6.3.0-18+deb9u1) 6.3.0 20170516, 64-bit
2019-10-09 15:08:24.859 UTC [11557] LOG: en écoute sur IPv4, adresse «
10.0.3.141 », port 5434
2019-10-09 15:08:24.863 UTC [11557] LOG: écoute sur la socket Unix « /tmp/
.s.PGSQL.5432 »
2019-10-09 15:08:24.881 UTC [11559] LOG: le système de bases de données a été
arrêté à 2019-10-09 15:07:16 UTC
2019-10-09 15:08:24.881 UTC [11559] ATTENTION: specified neither
primary_conninfo nor restore_command
2019-10-09 15:08:24.881 UTC [11559] ASTUCE : Le serveur de la base de données
va régulièrement interroger le sous-répertoire
pg_wal pour vérifier les fichiers placés ici.
2019-10-09 15:08:24.881 UTC [11559] LOG: entre en mode standby
2019-10-09 15:08:24.884 UTC [11559] LOG: état de restauration cohérent atteint
à 0/301E030
2019-10-09 15:08:24.884 UTC [11559] LOG: longueur invalide de l'enregistrement
à 0/301E030 : voulait 24, a eu 0
2019-10-09 15:08:24.885 UTC [11557] LOG: le système de bases de données est
prêt pour accepter les connexions en lecture seule
10.0.3.141:5432 - accepting connections
10.0.3.141:5432 - accepting connections
2019-10-09 15:08:25,904 INFO: establishing a new patroni connection to the
postgres cluster
2019-10-09 15:08:25,939 WARNING: Could not activate Linux watchdog device:
"Can't open watchdog device: [Errno 2] No such file or directory: '/dev/
watchdog'"
2019-10-09 15:08:25,947 INFO: promoted self to leader by acquiring session lock
serveur en cours de promotion
2019-10-09 15:08:25.951 UTC [11559] LOG: a reçu une demande de promotion
2019-10-09 15:08:25.951 UTC [11559] LOG: la ré-exécution n'est pas nécessaire
2019-10-09 15:08:25,951 INFO: cleared rewind state after becoming the leader
2019-10-09 15:08:25.958 UTC [11559] LOG: identifiant d'un timeline
nouvellement sélectionné : 11
2019-10-09 15:08:26.065 UTC [11559] LOG: restauration terminée de l'archive
2019-10-09 15:08:26.078 UTC [11557] LOG: le système de bases de données est
prêt pour accepter les connexions
2019-10-09 15:08:26,979 INFO: Lock owner: pg-1; I am pg-1
2019-10-09 15:08:27,029 INFO: no action. i am the leader with the lock
^C
Nous arrêtons patroni après ce test.
# /etc/systemd/system/patroni.service
[Unit]
Description=patroni service
Documentation=https://github.com/zalando/patroni
[Service]
Type=simple
User=postgres
Group=postgres
ExecStart=/usr/local/bin/patroni /etc/patroni/pg1.yml
# only patroni killed
KillMode=process
TimeoutSec=30
# don't restart on failure
Restart=no
[Install]
WantedBy=multi-user.target
Prise en compte par systemd :
Vérification dans les journaux applicatifs :
Il est possible de changer la configuration de tous les nœuds en une commande, cela nécessite l’installation de l’utilitaire less
:
# apt install -y less
# patronictl -c /etc/patroni/config.yml edit-config
loop_wait: 10
maximum_lag_on_failover: 1048576
postgresql:
checkpoint_timeout: 30
hot_standby: 'on'
max_relication_slots: 5
max_wal_senders: 5
parameters: null
use_pg_rewind: true
use_slots: true
wal_keep_segment: 8
wal_level: replica
retry_timeout: 10
ttl: 15
---
+++
@@ -11,4 +11,4 @@
wal_keep_segment: 8
wal_level: replica
retry_timeout: 10
-ttl: 30
+ttl: 15
Apply these changes? [y/N]:y
Configuration changed
Consultation de la configuration
# patronictl -c /etc/patroni/config.yml show-config
loop_wait: 10
maximum_lag_on_failover: 1048576
postgresql:
checkpoint_timeout: 30
hot_standby: 'on'
max_relication_slots: 5
max_wal_senders: 5
parameters: null
use_pg_rewind: true
use_slots: true
wal_keep_segment: 8
wal_level: replica
retry_timeout: 10
ttl: 15
La deuxième instance que l’on va raccrocher au cluster.
scope: my-ha-cluster
name: pg-2
restapi:
listen: 0.0.0.0:8008
connect_address: 127.0.0.1:8008
etcd:
host: 10.0.3.64:2379
bootstrap:
dcs:
ttl: 30
loop_wait: 10
retry_timeout: 10
maximum_lag_on_failover: 1048576
postgresql:
use_pg_rewind: true
use_slots: true
parameters:
wal_level: replica
hot_standby: "on"
wal_keep_segment: 8
max_wal_senders: 5
max_relication_slots: 5
checkpoint_timeout: 30
initdb: UTF8
pg_hba:
- host all dba all md5
- host replication repl all md5
users:
dba:
password: secret
options:
- createrole
- createdb
repl:
password: secret
options:
- replication:
postgresql:
listen: 10.0.3.201:5434
connect_address: 10.0.3.201:5434
data_dir: /var/lib/postgresql/data
bin_dir: /usr/lib/postgresql/12/bin
authentication:
replication:
username: replication
password: secret
superuser:
username: dba
password: secret
parameters:
unix_socket_directories: '/tmp'
2019-10-09 15:57:37,553 INFO: Failed to import patroni.dcs.consul
2019-10-09 15:57:37,564 INFO: Selected new etcd server http://10.0.3.64:2379
2019-10-09 15:57:37,572 INFO: No PostgreSQL configuration items changed, nothing to reload.
2019-10-09 15:57:37,583 INFO: Lock owner: pg-1; I am pg-2
2019-10-09 15:57:37,586 INFO: trying to bootstrap from leader 'pg-1'
2019-10-09 15:57:38,259 INFO: replica has been created using basebackup
2019-10-09 15:57:38,261 INFO: bootstrapped from leader 'pg-1'
2019-10-09 15:57:38,280 INFO: postmaster pid=11530
10.0.3.201:5432 - no response
2019-10-09 15:57:38.293 UTC [11530] LOG: starting PostgreSQL 12.0 (Debian 12.0-
1.pgdg90+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 6.3.0-18+deb9u1) 6.3.0 20170516, 64-bit
2019-10-09 15:57:38.293 UTC [11530] LOG: en écoute sur IPv4, adresse «
10.0.3.201 », port 5432
2019-10-09 15:57:38.297 UTC [11530] LOG: écoute sur la socket Unix « /tmp/
.s.PGSQL.5432 »
2019-10-09 15:57:38.320 UTC [11532] LOG: le système de bases de données a été
interrompu ; dernier lancement connu à 2019-10-09 15:57:37 UTC
2019-10-09 15:57:38.396 UTC [11532] LOG: entre en mode standby
2019-10-09 15:57:38.400 UTC [11532] LOG: la ré-exécution commence à 0/6000028
2019-10-09 15:57:38.402 UTC [11532] LOG: état de restauration cohérent atteint
à 0/6000100
2019-10-09 15:57:38.402 UTC [11530] LOG: le système de bases de données est
prêt pour accepter les connexions en lecture seule
2019-10-09 15:57:38.409 UTC [11536] FATAL: n'a pas pu démarrer l'envoi des WAL
: ERREUR: le slot de réplication « pg_2 » n'existe pas
2019-10-09 15:57:38.414 UTC [11537] FATAL: n'a pas pu démarrer l'envoi des WAL
: ERREUR: le slot de réplication « pg_2 » n'existe pas
10.0.3.201:5432 - accepting connections
10.0.3.201:5432 - accepting connections
2019-10-09 15:57:39,318 INFO: Lock owner: pg-1; I am pg-2
2019-10-09 15:57:39,319 INFO: does not have lock
2019-10-09 15:57:39,319 INFO: establishing a new patroni connection to the
postgres cluster
2019-10-09 15:57:39,336 INFO: no action. i am a secondary and i am following a
leader
2019-10-09 15:57:43.425 UTC [11545] LOG: Commence le flux des journaux depuis
le principal à 0/7000000 sur la timeline 20
2019-10-09 15:57:48,680 INFO: Lock owner: pg-1; I am pg-2
2019-10-09 15:57:48,680 INFO: does not have lock
2019-10-09 15:57:48,689 INFO: no action. i am a secondary and i am following a
leader
2019-10-09 15:57:58,675 INFO: Lock owner: pg-1; I am pg-2
2019-10-09 15:57:58,675 INFO: does not have lock
2019-10-09 15:57:58,685 INFO: no action. i am a secondary and i am following a
leader
Patroni effectue un basebackup sur le primaire, configure et lance le secondaire en réplication physique raccrochée au primaire.
On arrête brutalement pg_patroni1
(ctrl+c du processus patroni sur pg_patroni1)
pg_patroni2
prend la relève :
2019-10-09 16:02:52,027 INFO: Lock owner: pg-1; I am pg-2
2019-10-09 16:02:52,028 INFO: does not have lock
2019-10-09 16:02:52,033 INFO: no action. i am a secondary and i am following a leader
2019-10-09 16:03:00.489 UTC [11709] LOG: réplication terminée par le serveur primaire
2019-10-09 16:03:00.489 UTC [11709] DÉTAIL: Fin du WAL atteint sur la timeline 22 à 0/70004A8
2019-10-09 16:03:00.489 UTC [11709] FATAL: n'a pas pu transmettre le message
de fin d'envoi de flux au primaire : aucun COPY en cours
2019-10-09 16:03:00.489 UTC [11705] LOG: longueur invalide de l'enregistrement à 0/70004A8 : voulait 24, a eu 0
2019-10-09 16:03:00.493 UTC [11716] FATAL: n'a pas pu se connecter au serveur
principal : n'a pas pu se connecter au serveur : Connexion refusée
Le serveur est-il actif sur l'hôte « 10.0.3.141 » et accepte-t-il les connexions
TCP/IP sur le port 5434 ?
2019-10-09 16:03:00,532 INFO: Got response from pg-1 http://127.0.0.1:8008/
patroni: b'{"state": "running", "server_version": 120000,
"postmaster_start_time": "2019-10-09 16:02:47.254 UTC", "role": "replica",
"xlog": {"paused": false, "replayed_timestamp": null, "replayed_location":
117441704, "received_location": 117441704}, "patroni": {"version": "1.6.0",
"scope": "my-ha-cluster"}, "database_system_identifier": "6745811559071353594",
"cluster_unlocked": true, "timeline": 22}'
2019-10-09 16:03:00,634 WARNING: Could not activate Linux watchdog device:
"Can't open watchdog device: [Errno 2] No such file or directory: '/dev/
watchdog'"
2019-10-09 16:03:00,638 INFO: promoted self to leader by acquiring session lock
serveur en cours de promotion
2019-10-09 16:03:00.641 UTC [11705] LOG: a reçu une demande de promotion
2019-10-09 16:03:00.641 UTC [11705] LOG: ré-exécution faite à 0/7000430
2019-10-09 16:03:00,641 INFO: cleared rewind state after becoming the leader
2019-10-09 16:03:00.645 UTC [11705] LOG: identifiant d'un timeline
nouvellement sélectionné : 23
2019-10-09 16:03:00.721 UTC [11705] LOG: restauration terminée de l'archive
2019-10-09 16:03:00.730 UTC [11703] LOG: le système de bases de données est
prêt pour accepter les connexions
2019-10-09 16:03:01,659 INFO: Lock owner: pg-2; I am pg-2
2019-10-09 16:03:01,710 INFO: no action. i am the leader with the lock
Le secondaire est promu.
Quand l’ancien primaire est rétabli, il devient secondaire, accroché au nouveau primaire promu.
patroni /etc/patroni/pg1.yml
2019-10-09 16:05:54,352 INFO: Failed to import patroni.dcs.consul
2019-10-09 16:05:54,363 INFO: Selected new etcd server http://10.0.3.64:2379
2019-10-09 16:05:54,371 INFO: No PostgreSQL configuration items changed, nothing to reload.
2019-10-09 16:05:54,378 WARNING: Postgresql is not running.
2019-10-09 16:05:54,378 INFO: Lock owner: pg-2; I am pg-1
2019-10-09 16:05:54,380 INFO: pg_controldata:
max_wal_senders setting: 10
Latest checkpoint's NextOID: 16399
Latest checkpoint's oldestMulti's DB: 1
Database cluster state: shut down
Latest checkpoint's full_page_writes: on
Latest checkpoint's PrevTimeLineID: 23
Database system identifier: 6745811559071353594
Fake LSN counter for unlogged rels: 0/1
Min recovery ending loc's timeline: 0
Maximum data alignment: 8
Bytes per WAL segment: 16777216
max_connections setting: 100
max_prepared_xacts setting: 0
track_commit_timestamp setting: off
Backup start location: 0/0
Latest checkpoint's newestCommitTsXid: 0
wal_log_hints setting: on
Mock authentication nonce: 8acd62300c302ccd7e96b515e1de24171c625933d7c483e3b056e0ed955acf66
Minimum recovery ending location: 0/0
Maximum length of identifiers: 64
Maximum size of a TOAST chunk: 1996
wal_level setting: replica
Latest checkpoint's REDO WAL file: 000000170000000000000007
Blocks per segment of large relation: 131072
Latest checkpoint's NextMultiOffset: 0
Latest checkpoint's REDO location: 0/7000588
Data page checksum version: 0
Database block size: 8192
Latest checkpoint location: 0/7000588
Latest checkpoint's oldestXID's DB: 1
Latest checkpoint's NextXID: 0:495
Latest checkpoint's oldestMultiXid: 1
Maximum columns in an index: 32
Time of latest checkpoint: Wed Oct 9 16:05:46 2019
Catalog version number: 201909212
Latest checkpoint's oldestActiveXID: 0
Latest checkpoint's oldestCommitTsXid: 0
Latest checkpoint's TimeLineID: 23
max_locks_per_xact setting: 64
pg_control version number: 1201
Latest checkpoint's oldestXID: 480
WAL block size: 8192
max_worker_processes setting: 8
Latest checkpoint's NextMultiXactId: 1
pg_control last modified: Wed Oct 9 16:05:46 2019
Float8 argument passing: by value
Backup end location: 0/0
End-of-backup record required: no
Float4 argument passing: by value
Date/time type storage: 64-bit integers
Size of a large-object chunk: 2048
2019-10-09 16:05:54,383 INFO: Lock owner: pg-2; I am pg-1
2019-10-09 16:05:54,397 INFO: Local timeline=23 lsn=0/7000588
2019-10-09 16:05:54,401 INFO: master_timeline=24
2019-10-09 16:05:54,401 INFO: master: history=1 0/1640480 no recovery target specified
2 0/1640610 no recovery target specified
3 0/16407A0 no recovery target specified
4 0/1640930 no recovery target specified
5 0/30001C0 no recovery target specified
6 0/3000350 no recovery target specified
7 0/3000468 no recovery target specified
8 0/301D768 no recovery target specified
9 0/301DEC0 no recovery target specified
10 0/301E030 no recovery target specified
11 0/301E1C0 no recovery target specified
12 0/301E2D8 no recovery target specified
13 0/301E468 no recovery target specified
14 0/40000A0 no recovery target specified
15 0/40001B8 no recovery target specified
16 0/50000A0 no recovery target specified
17 0/50001F8 no recovery target specified
18 0/50001F8 no recovery target specified
19 0/501C2E0 no recovery target specified
20 0/70001C0 no recovery target specified
21 0/7000318 no recovery target specified
22 0/70004A8 no recovery target specified
23 0/7000638 no recovery target specified
2019-10-09 16:05:54,401 INFO: Lock owner: pg-2; I am pg-1
2019-10-09 16:05:54,415 INFO: starting as a secondary
2019-10-09 16:05:54,427 INFO: postmaster pid=12090
10.0.3.141:5432 - no response
2019-10-09 16:05:54.438 UTC [12090] LOG: starting PostgreSQL 12.0 (Debian 12.0-
1.pgdg90+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 6.3.0-18+deb9u1) 6.3.0 20170516, 64-bit
2019-10-09 16:05:54.438 UTC [12090] LOG: en écoute sur IPv4, adresse «
10.0.3.141 », port 5432
2019-10-09 16:05:54.445 UTC [12090] LOG: écoute sur la socket Unix « /tmp/
.s.PGSQL.5434 »
2019-10-09 16:05:54.459 UTC [12092] LOG: le système de bases de données a été
arrêté à 2019-10-09 16:05:46 UTC
2019-10-09 16:05:54.460 UTC [12092] LOG: entre en mode standby
2019-10-09 16:05:54.463 UTC [12092] LOG: état de restauration cohérent atteint
à 0/7000600
2019-10-09 16:05:54.463 UTC [12092] LOG: longueur invalide de l'enregistrement
à 0/7000600 : voulait 24, a eu 0
2019-10-09 16:05:54.463 UTC [12090] LOG: le système de bases de données est
prêt pour accepter les connexions en lecture seule
2019-10-09 16:05:54.470 UTC [12096] LOG: récupération du fichier historique
pour la timeline 24 à partir du serveur principal
2019-10-09 16:05:54.474 UTC [12096] LOG: Commence le flux des journaux depuis
le principal à 0/7000000 sur la timeline 23
2019-10-09 16:05:54.474 UTC [12096] LOG: réplication terminée par le serveur
primaire
2019-10-09 16:05:54.474 UTC [12096] DÉTAIL: Fin du WAL atteint sur la timeline
23 à 0/7000638
2019-10-09 16:05:54.475 UTC [12092] LOG: longueur invalide de l'enregistrement
à 0/7000600 : voulait 24, a eu 0
2019-10-09 16:05:54.475 UTC [12096] FATAL: arrêt du processus walreceiver
suite à la demande de l'administrateur
2019-10-09 16:05:54.475 UTC [12092] LOG: la nouvelle timeline cible est 24
2019-10-09 16:05:54.475 UTC [12092] LOG: longueur invalide de l'enregistrement
à 0/7000600 : voulait 24, a eu 0
2019-10-09 16:05:54.475 UTC [12092] LOG: longueur invalide de l'enregistrement
à 0/7000600 : voulait 24, a eu 0
2019-10-09 16:05:54.475 UTC [12092] LOG: longueur invalide de l'enregistrement
à 0/7000600 : voulait 24, a eu 0
10.0.3.141:5432 - accepting connections
10.0.3.141:5432 - accepting connections
2019-10-09 16:05:55,471 INFO: Lock owner: pg-2; I am pg-1
2019-10-09 16:05:55,471 INFO: does not have lock
2019-10-09 16:05:55,472 INFO: establishing a new patroni connection to the
postgres cluster
2019-10-09 16:05:55,494 INFO: no action. i am a secondary and i am following a
leader
2019-10-09 16:05:59.478 UTC [12092] LOG: longueur invalide de l'enregistrement
à 0/7000600 : voulait 24, a eu 0
2019-10-09 16:06:00,436 INFO: Lock owner: pg-2; I am pg-1
2019-10-09 16:06:00,437 INFO: does not have lock
2019-10-09 16:06:00,454 INFO: no action. i am a secondary and i am following a
leader
Il se resynchronise avec le nouveau primaire et devient un secondaire.
sur pg_patroni3
$ sudo apt update
$ sudo apt upgrade -y
$ sudo apt install -y vim sudo curl wget gpg
$ sudo vim /etc/apt/source.list.d/pgpdg.list
ajout du dépôt pgdg deb http://apt.postgresql.org/pub/repos/apt/ stretch-pgdg main
$ curl https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
$ sudo apt update
$ sudo apt install postgresql-12
$ sudo apt install python3-pip
$ sudo apt install python3-psycopg2
$ sudo pip3 install python-etcd
$ sudo pip3 install patroni
$ sudo patroni
Usage: /usr/local/bin/patroni config.yml
Patroni may also read the configuration from the PATRONI_CONFIGURATION environment variable
Création du fichier /etc/patroni/pg3.yml, on le raccroche au cluster de configuration etcd (10.0.3.64) et on lui demande d’écouter sur 10.0.3.32:5434 (adresse affectée par lxc-create
).
scope: my-ha-cluster
name: pg-3
restapi:
listen: 0.0.0.0:8008
connect_address: 127.0.0.1:8008
etcd:
host: 10.0.3.64:2379
bootstrap:
dcs:
ttl: 30
loop_wait: 10
retry_timeout: 10
maximum_lag_on_failover: 1048576
postgresql:
use_pg_rewind: true
use_slots: true
parameters:
wal_level: replica
hot_standby: "on"
wal_keep_segment: 8
max_wal_senders: 5
max_relication_slots: 5
checkpoint_timeout: 30
initdb: UTF8
pg_hba:
- host all dba all md5
- host replication repl all md5
users:
dba:
password: secret
options:
- createrole
- createdb
repl:
password: secret
options:
- replication:
postgresql:
listen: 10.0.3.68:5434
connect_address: 10.0.3.68:5434
data_dir: /var/lib/postgresql/data/main
bin_dir: /usr/lib/postgresql/12/bin
authentication:
replication:
username: replication
password: secret
superuser:
username: dba
password: secret
parameters:
unix_socket_directories: '/tmp'
Au lancement de Patroni, pg_patroni3
récupère l’instance sur le leader et se raccroche à la timeline de celui-ci.
Il devient un nouveau secondaire.
$ patroni /etc/patroni/pg3.yml
2019-10-09 16:37:14,848 INFO: Failed to import patroni.dcs.consul
2019-10-09 16:37:14,859 INFO: Selected new etcd server http://10.0.3.64:2379
2019-10-09 16:37:14,866 INFO: No PostgreSQL configuration items changed, nothing to reload.
2019-10-09 16:37:14,885 INFO: Lock owner: pg-2; I am pg-3
2019-10-09 16:37:14,890 INFO: trying to bootstrap from leader 'pg-2'
2019-10-09 16:37:16,088 INFO: replica has been created using basebackup
2019-10-09 16:37:16,090 INFO: bootstrapped from leader 'pg-2'
2019-10-09 16:37:16,117 INFO: postmaster pid=10448 10.0.3.68:5432 - no response
2019-10-09 16:37:16.134 UTC [10448] LOG: starting PostgreSQL 12.0 (Debian 12.0-
1.pgdg90+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 6.3.0-18+deb9u1) 6.3.0 20170516, 64-bit
2019-10-09 16:37:16.134 UTC [10448] LOG: en écoute sur IPv4, adresse «
10.0.3.68 », port 5432
2019-10-09 16:37:16.137 UTC [10448] LOG: écoute sur la socket Unix « /tmp/.s.PGSQL.5432 »
2019-10-09 16:37:16.152 UTC [10450] LOG: le système de bases de données a été
interrompu ; dernier lancement connu à 2019-10-09 16:37:15 UTC
2019-10-09 16:37:16.220 UTC [10450] LOG: entre en mode standby
2019-10-09 16:37:16.224 UTC [10450] LOG: la ré-exécution commence à 0/8000028
2019-10-09 16:37:16.225 UTC [10450] LOG: état de restauration cohérent atteint à 0/8000100
2019-10-09 16:37:16.226 UTC [10448] LOG: le système de bases de données est
prêt pour accepter les connexions en lecture seule
2019-10-09 16:37:16.231 UTC [10454] FATAL: n'a pas pu démarrer l'envoi des WAL
: ERREUR: le slot de réplication « pg_3 » n'existe pas
2019-10-09 16:37:16.236 UTC [10455] FATAL: n'a pas pu démarrer l'envoi des WAL
: ERREUR: le slot de réplication « pg_3 » n'existe pas
10.0.3.68:5432 - accepting connections
10.0.3.68:5432 - accepting connections
2019-10-09 16:37:17,163 INFO: Lock owner: pg-2; I am pg-3
2019-10-09 16:37:17,164 INFO: does not have lock
2019-10-09 16:37:17,164 INFO: establishing a new patroni connection to the postgres cluster
2019-10-09 16:37:17,187 INFO: no action. i am a secondary and i am following a leader
2019-10-09 16:37:20,437 INFO: Lock owner: pg-2; I am pg-3
2019-10-09 16:37:20,437 INFO: does not have lock
2019-10-09 16:37:20,442 INFO: no action. i am a secondary and i am following a leader
2019-10-09 16:37:21.248 UTC [10464] LOG: Commence le flux des journaux depuis
le principal à 0/9000000 sur la timeline 24
2019-10-09 16:37:30,425 INFO: Lock owner: pg-2; I am pg-3
2019-10-09 16:37:30,425 INFO: does not have lock
2019-10-09 16:37:30,431 INFO: no action. i am a secondary and i am following a leader
Sur chaque nœud patroni, on installe HaProxy :
Ajout dans la configuration du tableau de stats et du proxy etcd :
listen stats
bind *:7000
mode http
stats enable
stats uri /
listen etcd
bind *:2379
mode tcp
option tcp-check
balance roundrobin
default-server inter 10s downinter 5s rise 2 fall 2 slowstart 60s maxconn 250 maxqueue 256 weight 100
server etcd1 etcd1:2379 check
server etcd2 etcd2:2379 check
server etcd3 etcd3:2379 check
et on modifie la configuration de patroni, la section etcd doit désormais faire référence à la boucle locale (haproxy):
etcd:
host: 127.0.0.1:2379
Il faut lancer Ha Proxy et relancer chacune des instances patroni pour prendre en compte le “nouveau” cluster etcd.
chaque nœud communique désormais avec le proxy etcd sur sa boucle locale.
On valide que chaque nœud patroni renvoie bien vers le cluster etcd :
les urls :
doivent toutes renvoyer la liste des nœuds etcd :
http://10.0.3.32:2379, http://10.0.3.64:2379, http://10.0.3.78:2379
Pour se connecter au cluster patroni, il faut déterminer qui est le leader et qui sont les secondaires. L’API de Patroni permet de récupérer cette information en se connectant sur le port 8008 en http, le statut 200 indique que nous sommes en présence du leader.
On se propose de configurer un round-robin en lecture sur les secondaires et une redirection vers le leader en cas d’accès en écriture.
Les lectures seront effectuées sur le port 5433 et redirigées vers le port 5434 d’un des secondaires Les écritures seront effectuées sur le port 5432 et redirigées vers le port 5434 du leader
Mise en place
Toutes les instances écoutent sur le port 5434.
Sur chacun des noeuds, /etc/hosts contiendra les adresses ip des noeuds :
Sur toutes les instances Patroni, configurer HA proxy comme tel :
listen production
bind *:5432
option httpchk OPTIONS /master
http-check expect status 200
default-server inter 2s fastinter 1s rise 2 fall 1 on-marked-down shutdown-sessions
server pg-1 pg-1:5434 check port 8008
server pg-2 pg-2:5434 check port 8008
server pg-3 pg-3:5434 check port 8008
listen standby
bind *:5433
option httpchk OPTIONS /replica
http-check expect status 200
balance roundrobin
default-server inter 2s fastinter 1s rise 2 fall 1 on-marked-down shutdown-sessions
server pg-1 pg-1:5434 check port 8008
server pg-2 pg-2:5434 check port 8008
server pg-3 pg-3:5434 check port 8008
HAProxy doit être en mesure de déterminer via le http-check, qui est le leader et le rendre disponible sur le port 5432.
Les autres nœuds en lecture seule, doivent être accessibles à tour de rôle sur le port 5433. L’api de Patroni réponds avec un status 200 sur l’url /replica pour tous les secondaires que nous avons organisés en round-robin.
Pour vérifier que Haproxy est fonctionnel, les stats sont disponibles sur le port 7000 aux url :
nmap -p 5432,5433 p1 p2 p3
Starting Nmap 7.40 ( https://nmap.org ) at 2020-01-09 17:03 CET
Nmap scan report for p1 (10.0.3.141)
Host is up (0.00018s latency).
rDNS record for 10.0.3.141: pg_patroni1
PORT STATE SERVICE
5432/tcp open postgresql
5433/tcp open pyrrho
Nmap scan report for p2 (10.0.3.201)
Host is up (0.00011s latency).
rDNS record for 10.0.3.201: pg_patroni2
PORT STATE SERVICE
5432/tcp open postgresql
5433/tcp open pyrrho
Nmap scan report for p3 (10.0.3.68)
Host is up (0.000080s latency).
rDNS record for 10.0.3.68: pg_patroni3
PORT STATE SERVICE
5432/tcp open postgresql
5433/tcp open pyrrho
Vérification du roundrobin pour l’accès en lecture seule
$ while : ; do psql -P pager -h pg-2 -p 5433 -U dba -c "show primary_conninfo;" dba;
sleep 1; done
primary_conninfo
------------------------------------------------------------------------------------------
user=repl password=* host=10.0.3.141 port=5434 sslmode=prefer application_name=pg-3
(1 row)
primary_conninfo
-----------------------------------------------------------------------------------------
user=repl password=* host=10.0.3.68 port=5434 sslmode=prefer application_name=pg-1
(1 row)
primary_conninfo
------------------------------------------------------------------------------------------
user=repl password=* host=10.0.3.141 port=5434 sslmode=prefer application_name=pg-3
(1 row)
primary_conninfo
-----------------------------------------------------------------------------------------
user=repl password=* host=10.0.3.68 port=5434 sslmode=prefer application_name=pg-1
(1 row)
Répéter le test sur les 2 autres nœuds :
$ while : ; do psql -P pager -h pg-1 -p 5433 -U dba -c "show primary_conninfo;"
dba; sleep 1; done
...
$ while : ; do psql -P pager -h pg-3 -p 5433 -U dba -c "show primary_conninfo;"
dba; sleep 1; done
On doit obtenir le même résultat.
$ while : ; do psql -h p1,p2,p3 -P pager -p 5432 -U dba -c "show primary_conninfo;"
dba ; sleep 1 ;clear;done
Seul le leader doit répondre à une demande de connexion sur le port 5432.
On peut simuler une perte du leader et vérifier la bascule vers un des secondaires promu.
Enfin, pour bénéficier de la haute disponibilité du cluster, les clients doivent se connecter en mentionnant les 3 serveurs du cluster dans leur chaîne de connexion pour ainsi se connecter au premier disponible :
Connexion en écriture, port 5432
while : ; do psql -P pager -h pg-1,pg-2,pg-3 -p 5432 -U dba -c "show primary_conninfo;"
dba; sleep 1; done
Connexion en lecture, port 5433
while : ; do psql -P pager -h pg-1,pg-2,pg-3 -p 5433 -U dba -c "show primary_conninfo;"
dba; sleep 1; done
En cas de perte du nœud sur laquelle elle est connectée, l’application n’aura qu’à attendre le temps de la promotion initiée par Patroni et ré-initier la même connexion.
HaProxy basculera sur un nœud disponible, que ce soit le nouveau primaire ou un autre secondaire.
Vérifier que le secondaire descendu, disparaît du round-robin
Le nœud rétabli, n’est accessible que lorsqu’il a raccroché à la timeline du leader.
Vérifier qu’il est réinséré dans le roundrobin.
L’application doit pouvoir gérer le défaut de connexion temporaire lorsqu’un secondaire ou le primaire tombe (retry 2s plus tard pour que cela soit transparent).