Introduction
De nombreux articles traitent de la promotion d’un serveur de secours PostgreSQL en serveur principal avec la commande pg_ctl promote
.
Beaucoup d’entre eux concluent que le serveur de secours doit être reconstruit lorsque le serveur principal est de retour.
Qu’en est-il d’une maintenance programmée, maintenance incluant des arrêts, sur le serveur primaire ? Doit-on reconstruire le système primaire/standby ? Évidemment la réponse est : NON.
En supposant qu’il y a suffisamment d’espace disque pour conserver les fichiers WAL pendant la tâche de maintenance, en gérant prudemment le point
de réplication (replication slot) et l’option de récupération recovery_timeline_target
durant les procédures failover/failback, il n’y a
pas besoin de reconstruire le système standby impliquant des sauvegardes/restaurations de bases de données et/ou d’utiliser pg_rewind
.
Pour plus d’informations sur l’installation et la configuration d’un serveur standby PostgreSQL avec la réplication en continu (streaming replication) : Réplication en continu avec PostgreSQL 9.6, 10 et 11 (Streaming replication) - Bases de données PostgreSQL Standby
Les applications clientes se connectent à la base de données avec le nom de serveur logique LSRVSQLPAC
(adresse IP virtuelle) sur le port 30001.
Le serveur DNS résout la bonne adresse physique en fonction du contexte (failover, failback…).
Avant de réaliser une bascule, les serveurs doivent être vérifiés et préparés.
Voyons comment faire correctement les bascules failover/failback d’un serveur PostgreSQL lors d’une maintenance planifiée sur le serveur primaire sans perdre de transactions et sans avoir besoin de reconstuire quoi que ce soit.
Sur chaque serveur, le contexte est le suivant :
Système d’exploitation : Ubuntu 18.04
Distribution (PostgreSQL 9.6.15) : /opt/postgres/pgsql-9.6/bin
$PATH : /opt/postgres/pgsql-9.6/bin:$PATH
$PGLIB : /opt/postgres/pgsql-9.6/lib
Port : 30001
$PGDATA : /sqlpac/postgres/srvpg1
$CFG : /opt/postgres/dba/srvpg1/cfg
Fichiers de configuration :
$CFG/postgresql.conf
$CFG/pg_hba.conf
$CFG/pg_ident.conf
Contrôle du serveur PostgreSQL :
Compte PostgreSQL de la réplication (role) : repmgr
Nom du point de réplication (Replication slot name) : standby1 |
La procédure ci-dessous n’est valable que pour les versions 9, 10 et 11 de PostgreSQL. Pour les versions 12 et supérieures, la procédure est légèrement différente et non abordée ici.
Préparer le serveur primaire VPSFRSQLPAC1 à devenir un serveur standby
En avance, sur le serveur primaire, le paramètre hot_standby
est défini à on
(ce paramètre est ignoré quand il s’agit d’un serveur primaire).
VPSFRSQLPAC1 : postgresql.conf
hot_standby = on
Préparer le serveur de standby VPSFRSQLPAC2 à devenir un serveur primaire
Sur le serveur de standby, vérifier que les paramètres nécessaires à un serveur primaire sont définis de manière appropriée (valeurs minimales…) :
VPSFRSQLPAC2 : postgresql.conf
listen_addresses = '*'
wal_level = replica
max_wal_senders = 3
max_replication_slots = 3
Si des paramètres statiques doivent être ajustés, modifier les valeurs et éventuellement redémarrer le serveur de standby (optionnel) :
postgres@vpsfrsqlpac2$ pg_ctl restart -D $CFG
Vérifier que le compte PostgreSQL de la réplication est défini dans le fichier d’authentification pg_hba.conf
du serveur de standby
avec l’adresse IP du serveur primaire actuel, cette entrée autorisera les connexions depuis VPSFRSQLPAC1 lorsque celui-ci deviendra le serveur
de standby dans une prochaine étape.
VPSFRSQLPAC2 : pg_hba.conf
host replication repmgr 79.xxx.xxx.xxx/32 md5
Recharger la configuration si l’entrée a dû être ajoutée :
postgres@vpsfrsqlpac2$ pg_ctl reload -D $CFG
Failover - Bascule sur le site de secours
Failover - Étape 1 : Stopper les applications, sauvegarder la base puis finalement éteindre la base sur le serveur primaire VPSFRSQLPAC1
postgres@vpsfrsqlpac1$ pg_ctl stop -D $CFG
À ce stade, désactiver tous les scripts qui pourraient redémarrer la base sur VPSFRSQLPAC1 (scripts shell de démarrage…). La maintenance peut à présent démarrer sur le serveur VPSFRSQLPAC1.
Failover - Étape 2 : Promotion du serveur de standby en serveur primaire
Lancer pg_ctl promote
pour promouvoir le serveur de standby sur VPSFRSQLPAC2 en serveur primaire.
postgres@vpsfrsqlpac2$ pg_ctl promote -D $CFG
server promoting
Vérifier le fichier de log :
2019-11-22 16:23:20.394 CET LOG: received promote request
2019-11-22 16:23:20.394 CET LOG: redo done at 0/3016198
2019-11-22 16:23:20.394 CET LOG: last completed transaction was at log time 2019-11-22 16:22:12.212852+01
2019-11-22 16:23:20.407 CET LOG: selected new timeline ID: 2
2019-11-22 16:23:20.600 CET LOG: archive recovery complete
2019-11-22 16:23:20.620 CET LOG: MultiXact member wraparound protections are now enabled
2019-11-22 16:23:20.623 CET LOG: database system is ready to accept connections
2019-11-22 16:23:20.623 CET LOG: autovacuum launcher started
Un nouvel identifiant de chronologie (timeline id) est appliqué lors de la promotion. "selected new timeline ID: 2
" dans le fichier de log.
postgres@vpsfrsqlpac2$ psql -p 30001
select substr(pg_xlogfile_name(pg_current_xlog_location()), 1, 8);
substr ---------- 00000002
Le fichier recovery.conf
dans le répertoire $PGDATA
est renommé en recovery.done
lorsque la commande pg_ctl promote
a été exécutée.
Lorsque la bascule vers VPSFRSQLPAC2 est réalisée pour la première fois, le point de réplication standby1
n’existe pas ($PGDATA/pg_replslot
).
select slot_name, restart_lsn from pg_replication_slots
slot_name | restart_lsn -----------+------------- (0 rows)
Dans ce contexte, créer et activer immédiatement le point de réplication standby1
sur le nouveau serveur primaire :
postgres@vpsfrsqlpac2$ psql -p 30001
select * from pg_create_physical_replication_slot('standby1',true);
slot_name | xlog_position -----------+--------------- standby1 |0/3016318
L’ancien serveur de standby est à présent le serveur primaire.
Failover - Étape 3 : Bascule des applications sur le nouveau serveur primaire
L’adresse logique LSRVSQLPAC bascule sur l’adresse physique de VPSFRSQLPAC2 dans le serveur DNS. Les applications peuvent se connecter.
Failover - Étape 4 : Conversion de l’ancien serveur primaire en serveur de standby
L’ancien serveur primaire VPSFRSQLPAC1 est à présent de retour et disponible, la tâche de maintenance est terminée.
À cette étape, ne pas démarrer la base de données, une configuration doit être réalisée !
Sur l’ancien serveur primaire, créer le fichier recovery.conf
dans le répertoire $PGDATA
.
VPSFRSQLPAC1 : $PGDATA/recovery.conf
standby_mode = 'on'
primary_conninfo = 'user=repmgr host=vpsfrsqlpac2 port=30001 password=***********!'
primary_slot_name = 'standby1'
recovery_target_timeline = 'latest'
- Le mode standby est défini à
on
. - Les informations de connexion au serveur primaire VPSFRSQLPAC2 sont stockées dans le paramètre
primary_conninfo
. - Le nom du point de réplication précédemment (re)créé sur VPSFRSQLPAC2 est évidemment spécifié (
primary_slot_name
). - Le paramètre
recovery_target_timeline
est défini àlatest
: cette information est importante car l’id de chronologie (timeline id) est passé de 1 à 2 sur le nouveau serveur primaire. Si il n’est pas défini àlatest
, le recovery au démarrage ne bascule pas automatiquement sur le nouvel id de chronologie.
Lorsque le fichier recovery.conf
est correctement défini, démarrer l’ancien serveur primaire, vérifier le fichier de log et tester la réplication.
Au démarrage, VPSFRSQLPAC1 traite les fichiers WAL de VPSFRSQLPAC2 générés depuis sa promotion en serveur primaire. VPSFRSQLPAC1 devient le serveur de standby.
postgres@vpsfrsqlpac1$ pg_ctl start -D $CFG
2019-11-22 18:33:09.163 CET LOG: entering standby mode
2019-11-22 18:33:09.176 CET LOG: consistent recovery state reached at 0/3016208
2019-11-22 18:33:09.176 CET LOG: invalid record length at 0/3016208: wanted 24, got 0
2019-11-22 18:33:09.177 CET LOG: database system is ready to accept read only connections
2019-11-22 18:33:09.203 CET LOG: fetching timeline history file for timeline 2 from primary server
2019-11-22 18:33:09.211 CET LOG: started streaming WAL from primary at 0/3000000 on timeline 1
2019-11-22 18:33:09.215 CET LOG: replication terminated by primary server
2019-11-22 18:33:09.215 CET DETAIL: End of WAL reached on timeline 1 at 0/3016208.
2019-11-22 18:33:09.216 CET LOG: new target timeline is 2
2019-11-22 18:33:09.217 CET LOG: restarted WAL streaming at 0/3000000 on timeline 2
2019-11-22 18:33:09.579 CET LOG: redo starts at 0/3016208
Attendre que la réplication soit complètement synchronisée.
Si le paramètre recovery_target_timeline
n’est pas défini à latest
, le recovery boucle avec l’erreur ci-dessous lorsqu’il atteint
la fin de son id de chronologie, il ne bascule pas sur l’id de chronologie suivant (timeline id 2) :
LOG: started streaming WAL from primary at 0/36000000 on timeline 1
LOG: replication terminated by primary server
DETAIL: End of WAL reached on timeline 1 at 0/362E3B70.
LOG: restarted WAL streaming at 0/36000000 on timeline 1
LOG: replication terminated by primary server
DETAIL: End of WAL reached on timeline 1 at 0/362E3B70.
Dans ce cas de figure, redémarrer simplement la base avec recovery_target_timeline
correctement défini à latest
: il ne s’agit
pas ici d’une erreur irrécupérable pour la base de données.
Maintenant VPSFRSQLPAC1 est le serveur de standby. Les scripts automatiques peuvent être réactivés sur VPSFRSQLPAC1.
Retour arrière (Failback)
La procédure de retour arrière sera similaire à la procédure de bascule sur le site de secours.
Failback - Étape 1 : Stopper les applications, sauvegarder la base de données et éteindre la base sur le serveur primaire VPSFRSQLPAC2.
postgres@vpsfrsqlpac2$ pg_ctl stop -D $CFG
Désactiver tous les scripts qui pourraient redémarrer la base sur VPSFRSQLPAC2 (shells, cron…).
Failback - Étape 2 : Promotion du serveur de standby en serveur primaire
Lancer pg_ctl promote
pour promouvoir le serveur de standby VPSFRSQLPAC1 en serveur primaire.
postgres@vpsfrsqlpac1$ pg_ctl promote -D $CFG
server promoting
Vérifier le fichier de log :
2019-11-22 18:59:53.743 CET LOG: received promote request
2019-11-22 18:59:53.743 CET LOG: redo done at 0/E98A7988
2019-11-22 18:59:53.743 CET LOG: last completed transaction was at log time 2019-11-22 18:40:13.347683+01
2019-11-22 18:59:53.752 CET LOG: selected new timeline ID: 3
2019-11-22 18:59:54.074 CET LOG: archive recovery complete
2019-11-22 18:59:54.092 CET LOG: MultiXact member wraparound protections are now enabled
2019-11-22 18:59:54.122 CET LOG: database system is ready to accept connections
2019-11-22 18:59:54.123 CET LOG: autovacuum launcher started
L’id de chronologie (timeline id) est incrémenté de 1 lors de la promotion. "selected new timeline ID: 3
" dans le fichier de log.
Le fichier recovery.conf
dans le répertoire $PGDATA
est renommé en recovery.done
avec pg_ctl promote
.
Lors du retour arrière sur VPSFRSQLPAC1, un point de réplication standby1
existe déjà ($PGDATA/pg_replslot
),
mais celui-ci est obsolète car il s’agit du point de réplication existant avant la bascule sur VPSFRSQLPAC2.
select slot_name, restart_lsn from pg_replication_slots
slot_name | restart_lsn -----------+------------- standby1 | 0/3016198
Recréer et activer immédiatement le point réplication standby1
sur le nouveau serveur primaire :
postgres@vpsfrsqlpac1$ psql -p 30001
select * from pg_drop_replication_slot('standby1'); select * from pg_create_physical_replication_slot('standby1',true);
slot_name | xlog_position -----------+--------------- standby1 | 0/E98A7B08
VPSFRSQLPAC1 est de nouveau le serveur primaire.
Failback - Étape 3 : Bascule des applications sur le nouveau serveur primaire
L’adresse logique LSRVSQLPAC bascule sur l’adresse physique de VPSFRSQLPAC1 dans le serveur DNS. Les applications peuvent se connecter.
Failback - Étape 4 : Conversion de VPSFRSQLPAC2 en serveur de standby
Maintenant, VPSFRSQLPAC2 redevient le serveur de standby.
À cette étape, ne pas démarrer la base sur VPSFRSQLPAC2.
Sur VPSFRSQLPAC2, renommer le fichier recovery.done
en recovery.conf
dans le répertoire $PGDATA
.
Ajouter le paramètre recovery_target_timeline
défini à latest
si ce paramètre n’était pas existant
dans le fichier d’origine recovery.conf
avant bascule, dans le cas contraire la bascule sur l’id de chronologie 3
(timeline ID 3) ne sera pas automatiquement réalisée durant le recovery.
VPSFRSQLPAC2 : $PGDATA/recovery.conf
standby_mode = 'on'
primary_conninfo = 'user=repmgr host=vpsfrsqlpac1 port=30001 password=***********!'
primary_slot_name = 'standby1'
recovery_target_timeline = 'latest'
Lorsque le fichier recovery.conf
est correctement paramétré, démarrer la base sur VPSFRSQLPAC2, vérifier le fichier de log
et tester la réplication.
Au démarrage, VPSFRSQLPAC2 traite les fichiers WAL de VPSFRSQLPAC1 générés depuis sa promotion en serveur principal et VPSFRSQLPAC2 redevient le serveur de standby.
postgres@vpsfrsqlpac2$ pg_ctl start -D $CFG
2019-11-22 19:05:17.799 CET LOG: entering standby mode
2019-11-22 19:05:17.811 CET LOG: consistent recovery state reached at 0/E98A79F8
2019-11-22 19:05:17.812 CET LOG: database system is ready to accept read only connections
2019-11-22 19:05:17.812 CET LOG: invalid record length at 0/E98A79F8: wanted 24, got 0
2019-11-22 19:05:17.824 CET LOG: fetching timeline history file for timeline 3 from primary server
2019-11-25 19:05:17.835 CET LOG: started streaming WAL from primary at 0/E9000000 on timeline 2
2019-11-22 19:05:18.568 CET LOG: replication terminated by primary server
2019-11-22 19:05:18.568 CET DETAIL: End of WAL reached on timeline 2 at 0/E98A79F8.
2019-11-22 19:05:18.570 CET LOG: new target timeline is 3
2019-11-22 19:05:18.571 CET LOG: restarted WAL streaming at 0/E9000000 on timeline 3
2019-11-22 19:05:19.524 CET LOG: redo starts at 0/E98A79F8
Les scripts de maintenance et de démarrage automatiques peuvent être réactivés sur VPSFRSQLPAC2.
La situation est revenue à la normale.
Conclusion
Les procédures programmées de failover/failback avec la réplication en continu PostgreSQL sont très simples.
En supposant qu’il y a assez d’espace disque pour la rétention des fichiers WAL durant les tâches de failover/failback,
une bonne gestion des étapes et du point de réplication garantit la consistence, aucune perte de données et surtout il n’y a pas
besoin de reconstruction complète incluant des backup/restore de bases de données ou autres méthodes
douloureuses (pg_rewind
…)
Avec ce cas pratique, il semble que cela soit une bonne pratique de (re)créer un point de réplication actif dès qu’un serveur de secours est promu en en serveur principal.
Si il n’y a pas assez d’espace disque pour la rétention des fichiers WAL durant la tâche de maintenance, la procédure est un tout petit peu différente et non abordée ici mais en quelques mots : le mode archive log doit être actif et chaque serveur de standby PostgreSQL doit pouvoir lire les archive logs générés par son serveur primaire.