Module I4
Dalibo SCOP
26.09
10 septembre 2026
| Formation | Module I4 |
| Titre | Sauvegarde PITR avec pgBackRest |
| Révision | 26.09 |
| https://dali.bo/i4_pdf | |
| EPUB | https://dali.bo/i4_epub |
| HTML | https://dali.bo/i4_html |
| Slides | https://dali.bo/i4_slides |
| TP | https://dali.bo/i4_tp |
| TP (solutions) | https://dali.bo/i4_solutions |
Cette formation est sous licence CC-BY-NC-SA. Vous êtes libre de la redistribuer et/ou modifier aux conditions suivantes :
PostgreSQL® Postgres® et le logo Slonik sont des marques déposées par PostgreSQL Community Association of Canada.
Ce document ne couvre que les versions supportées de PostgreSQL au moment de sa rédaction, soit les versions 14 à 18.
pg_create_restore_point())pgbackrestUsage:
pgbackrest [options] [command]
Commands:
archive-get Get a WAL segment from the archive
archive-push Push a WAL segment to the archive
backup Backup a database cluster
check Check the configuration
expire Expire backups that exceed retention
info Retrieve information about backups
repo-get Get a file from a repository
restore Restore a database cluster
server pgBackRest server
stanza-create Create the required stanza data
stanza-delete Delete a stanza
stanza-upgrade Upgrade a stanza
start Allow pgBackRest processes to run
stop Stop pgBackRest processes from running
verify Verify contents of the repository.
version Get version.
…erp_prod, dwh
/etc/pgbackrest/pgbackrest.conf
/etc/pgbackrest.confpgbackrest --config=…/etc/pgbackrest/conf.d/*.confpgbackrest --config-include-path=…[global][global:archive-push],
[global:archive-get][nomstanza1][nomstanza2]…--nom-option[global]
repo1-path=/mnt/NFS/backups
repo1-retention-full=5
repo1-retention-diff=3
repo2-host-type=ssh
repo2-host=backup_serveur
repo2-host-user=postgres
repo2-path=/srv/depot2
repo2-retention-full=2
repo2-retention-diff=1
repo3-type=s3
repo3-path=/repo
repo3-s3-endpoint=s3.backup.local
repo3-s3-bucket=pgbackrest
repo3-s3-verify-tls=n
repo3-s3-key=XXXX
repo3-s3-key-secret=XXXXXXX
repo3-retention-full=10
repo3-retention-diff=1Échange des clés SSH entre instances (postgres)
& le dépôt
Accès depuis serveurs PostgreSQL au serveur de sauvegarde :
Accès depuis le serveur de sauvegardes aux instances :
Alternative au SSH :
{repo1|pg1}-host-type = tls--repo1-option=… , appel avec
--repo=1Parallélisation de l’archivage/restauration :
archive-async=y
spool-path=/var/spool/pgbackrest
# spool pour la restauration
archive-get-queue-max=4GB
[global:archive-push]
# restauration : pas trop de processus
process-max=4
[global:archive-get]
# restauration : pas trop de processus
process-max=2--delta (gros gain de temps parfois)--db-exclude/--db-include (ne restaure pas
tout !)--archive_mode=off (sécurité)--target / --typepg_upgrade)
pgbackrest --stanza=… stanza-upgrade/var/log/pgbackrest : par stanza+commandes
NOMSTANZA-archive-push-async.logNOMSTANZA-backup.logNOMSTANZA-expire.log/var/log/pgbackrest/all-server.loglog-level-console, log-level-fileNB : Ce TP a été mis à jour pour PostgreSQL 17. Adapter le numéro de version dans les chemins au besoin.
Installer pgBackRest à partir des paquets du PGDG.
L’installation du paquet est triviale avec les paquets du PGDG :
En vous aidant de https://pgbackrest.org/user-guide.html#quickstart :
- configurer pgBackRest pour sauvegarder le serveur PostgreSQL en local dans
/var/lib/pgsql/backups;- le nom de la stanza sera instance_dev ;
- prévoir de ne conserver qu’une seule sauvegarde complète.
Le ficher de configuration de pgBackRest est
/etc/pgbackrest.conf. Le modifier ainsi :
[global]
repo1-path=/var/lib/pgsql/backups
repo1-retention-full=1
[instance_dev]
# chemin de l'instance PostgreSQL
pg1-path=/var/lib/pgsql/17/data(Les chemins ci-dessus sont ceux par défaut des paquets RPM du PGDG.
Sous Debian/Ubuntu, les données sont dans
/var/lib/postgresql/17/main. Adapter les autres chemins en
fonction.)
Configurer l’archivage des journaux de transactions de PostgreSQL avec pgBackRest.
Le fichier de configuration de PostgreSQL doit être modifié au besoin ainsi.
wal_level = replica
archive_mode = on
archive_command = 'pgbackrest --stanza=instance_dev archive-push %p'Redémarrer PostgreSQL :
Initialiser le répertoire de stockage des sauvegardes et vérifier la configuration de l’archivage.
Sous l’utilisateur postgres :
… P00 INFO: stanza-create command begin 2.54.1: --exec-id=116151-5ba090e6 --log-level-console=info --pg1-path=/var/lib/pgsql/17/data --repo1-path=/var/lib/pgsql/backups --stanza=instance_dev
… P00 INFO: stanza-create for stanza 'instance_dev' on repo1
… P00 INFO: stanza-create command end: completed successfully (56ms)Vérifier la configuration de pgBackRest et de l’archivage :
pgBackRest force ainsi un archivage :
… P00 INFO: check command begin 2.54.1: --exec-id=116153-45ee6160 --log-level-console=info --pg1-path=/var/lib/pgsql/17/data --repo1-path=/var/lib/pgsql/backups --stanza=instance_dev
… P00 INFO: check repo1 configuration (primary)
… P00 INFO: check repo1 archive for WAL (primary)
… P00 INFO: WAL segment 0000000200000000000000D8 successfully archived to '/var/lib/pgsql/backups/archive/instance_dev/17-1/0000000200000000/0000000200000000000000D8-81ecb9751dd627ba196fca377e9e6d0a2aa6fd05.gz' on repo1
… P00 INFO: check command end: completed successfully (409ms)Vérifier que l’archivage fonctionne en vérifiant que ce répertoire n’est pas vide :
On peut le vérifier aussi du côté PostgreSQL :
-[ RECORD 1 ]------+------------------------------
archived_count | 4
last_archived_wal | 0000000200000000000000D8
last_archived_time | 2025-01-13 19:03:13.400874+01
failed_count | 0
last_failed_wal |
last_failed_time |
stats_reset | 2025-01-13 18:47:37.39799+01Autre méthode, regarder le nom du processus archiver,
qui contient le nom du dernier journal archivé :
$ ps faux|grep archiver
…
postgres 211745 0.0 0.1 502568 7124 ? Ss 14:14 0:00 \_ postgres: archiver last was 0000000200000000000000D8
Lancer une sauvegarde complète. Afficher les détails de cette sauvegarde.
Noter le soin avec lequel pgBackRest vérifie que l’archivage est fonctionnel avant la sauvegarde, et l’attente du dernier journal avant d’assurer que la sauvegarde est terminée :
… P00 INFO: backup command begin 2.54.1: --exec-id=116270-de7f5e35 --log-level-console=info --pg1-path=/var/lib/pgsql/17/data --repo1-path=/var/lib/pgsql/backups --repo1-retention-full=1 --stanza=instance_dev --type=full
… P00 INFO: execute non-exclusive backup start: backup begins after the next regular checkpoint completes
… P00 INFO: backup start archive = 0000000200000000000000DC, lsn = 0/DC000028
… P00 INFO: check archive for prior segment 0000000200000000000000DB
…
…
… P00 INFO: execute non-exclusive backup stop and wait for all WAL segments to archive
… P00 INFO: backup stop archive = 0000000200000000000000DC, lsn = 0/DC000158
… P00 INFO: check archive for segment(s) 0000000200000000000000DC:0000000200000000000000DC
… P00 INFO: new backup label = 20250113-190921F
… P00 INFO: full backup size = 3.5GB, file total = 1594
… P00 INFO: backup command end: completed successfully (74648ms)
… P00 INFO: expire command begin 2.54.1: --exec-id=116270-de7f5e35 --log-level-console=info --repo1-path=/var/lib/pgsql/backups --repo1-retention-full=1 --stanza=instance_dev
… P00 INFO: repo1: expire full backup 20250113-190649F
… P00 INFO: repo1: remove expired backup 20250113-190649F
… P00 INFO: repo1: 17-1 remove archive, start = 0000000200000000000000D8, stop = 0000000200000000000000DB
… P00 INFO: expire command end: completed successfully (109ms)Lister les sauvegardes :
stanza: instance_dev
status: ok
cipher: none
db (current)
wal archive min/max (17): 0000000200000000000000DC/0000000200000000000000DC
full backup: 20250113-190921F
timestamp start/stop: 2025-01-13 19:09:21+01 / 2025-01-13 19:10:35+01
wal start/stop: 0000000200000000000000DC / 0000000200000000000000DC
database size: 3.5GB, database backup size: 3.5GB
repo1: backup set size: 197.7MB, backup size: 197.7MBAjouter des données : \ Ajouter une table avec 1 million de lignes. \ Forcer la rotation du journal de transaction courant afin de s’assurer que les dernières modifications sont archivées. \ Vérifier que le journal concerné est bien dans les archives.
La table suivante fait 35 Mo, qui seront intégralement écrits dans les journaux :
Pour ce test, il est possible de forcer la rotation du journal avec
pg_switch_wal. Dans la vie réelle, il y a de l’activité
dans la base et le journal sera assez vite archivé. Sans cela il
pourrait ne pas être sauvegardé.
pg_switch_wal renvoie un LSN peu lisible, comme
0/E8000180, où E8 correspond à la fin du nom
du journal. On peut ajouter pg_walfile_name() pour voir
plus clairement le nom du journal à archiver :
Vérifier que le journal concerné est bien dans le répertoire de
sauvegarde des archives de pgBackRest, soit dans notre exemple
/var/lib/pgsql/backups/archive/instance_dev/17-1/. La copie
devrait être ici instantanée, mais en production ça ne ne l’est pas
forcément.
Simulation d’un incident : noter l’heure puis supprimer tout le contenu de la table.
Noter l’heure exacte avant de détruire des données :
Restaurer les données telles que juste avant l’incident à l’aide de pgBackRest. \ Avant de redémarrer PostgreSQL, consulter les fichiers que pgBackRest a créé ou modifié dans le PGDATA. \ Redémarrer.
D’abord, stopper PostgreSQL (sinon pgBackRest refusera de toucher aux données) :
En tant que postgres, lancer la commande de restauration avec une heure juste avant la destruction des données :
pgbackrest --stanza=instance_dev --log-level-console=info \
--delta \
--type=time \
--target="2025-01-13 19:21:22" \
--target-exclusive \
--target-action=promote \
restoreNoter déjà le mode « delta » pour accélérer la restauration, et le
type de restauration time avec une heure.
… P00 INFO: restore command begin 2.54.1: --delta --exec-id=116554-29ea7f39 --log-level-console=info --pg1-path=/var/lib/pgsql/17/data --repo1-path=/var/lib/pgsql/backups --stanza=instance_dev --target="2025-01-13 19:21:22" --target-action=promote --target-exclusive --type=time
… P00 INFO: repo1: restore backup set 20250113-190921F, recovery will start at 2025-01-13 19:09:21
… P00 INFO: remove invalid files/links/paths from '/var/lib/pgsql/17/data'
… P00 INFO: write updated /var/lib/pgsql/17/data/postgresql.auto.conf
… P00 INFO: restore global/pg_control (performed last to ensure aborted restores cannot be started)
… P00 INFO: restore size = 3.5GB, file total = 1594
… P00 INFO: restore command end: completed successfully (4909ms)La restauration du base backup est un succès, mais il va falloir rejouer les journaux archivés.
pgBackRest a créé ou modifié ces fichiers :
$ ls -alrt /var/lib/pgsql/17/data
…
…
-rw-------. 1 postgres postgres 353 13 janv. 19:15 postgresql.auto.conf
-rw-------. 1 postgres postgres 0 13 janv. 19:15 recovery.signalrecovery.signal signalera à PostgreSQL qu’il est en
mode restauration, et pas en redémarrage après un crash ;postgresql.auto.conf contient des paramètres qui vont
surcharger postgresql.conf :# Recovery settings generated by pgBackRest restore on 2025-01-13 19:15:22
restore_command = 'pgbackrest --stanza=instance_dev archive-get %f "%p"'
recovery_target_time = '2025-01-13 19:21:22'
recovery_target_inclusive = 'false'
recovery_target_action = 'promote'On y trouve :
restore_command pour récupérer les journaux dans le
dépôt, commande que pgBackRest a préparé en fonction de sa configuration
et des paramètres de la ligne de commande de restauration ;recovery_target_time indique l’heure cible ;recovery_target_inclusive = 'false' arrête la
restauration juste avant cette heure pour ne pas rejouer la
destruction des données (le défaut est de rejouter jusqu’à l’heure cible
incluse) ;recovery_target_action = 'promote' demande à PostgreSQL
de s’ouvrir en écriture après le rejeu.Démarrer PostgreSQL :
Attendre la fin de la restauration dans les traces :
# Attention, le nom du fichier dépend du jour
tail -n100 /var/lib/pgsql/17/data/log/postgresql-Mon.log2025-01-13 19:26:38.946 CET [116631] LOG: database system was interrupted; last known up at 2025-01-13 19:09:21 CET
2025-01-13 19:26:39.032 CET [116631] LOG: starting backup recovery with redo LSN 0/DC000028, checkpoint LSN 0/DC000080, on timeline ID 2
2025-01-13 19:26:39.108 CET [116631] LOG: restored log file "0000000200000000000000DC" from archive
2025-01-13 19:26:39.168 CET [116631] LOG: starting point-in-time recovery to 2025-01-13 19:21:22+01
2025-01-13 19:26:39.176 CET [116631] LOG: redo starts at 0/DC000028
2025-01-13 19:26:39.236 CET [116631] LOG: restored log file "0000000200000000000000DD" from archive
2025-01-13 19:26:39.298 CET [116631] LOG: completed backup recovery with redo LSN 0/DC000028 and end LSN 0/DC000158
2025-01-13 19:26:39.298 CET [116631] LOG: consistent recovery state reached at 0/DC000158
2025-01-13 19:26:39.298 CET [116626] LOG: database system is ready to accept read-only connections
2025-01-13 19:26:39.584 CET [116631] LOG: restored log file "0000000200000000000000DE" from archive
2025-01-13 19:26:39.893 CET [116631] LOG: restored log file "0000000200000000000000DF" from archive
2025-01-13 19:26:40.169 CET [116631] LOG: restored log file "0000000200000000000000E0" from archive
2025-01-13 19:26:40.458 CET [116631] LOG: restored log file "0000000200000000000000E1" from archive
2025-01-13 19:26:40.609 CET [116631] LOG: restored log file "0000000200000000000000E2" from archive
2025-01-13 19:26:40.758 CET [116631] LOG: restored log file "0000000200000000000000E3" from archive
2025-01-13 19:26:40.907 CET [116631] LOG: restored log file "0000000200000000000000E4" from archive
2025-01-13 19:26:41.029 CET [116631] LOG: restored log file "0000000200000000000000E5" from archive
2025-01-13 19:26:41.291 CET [116631] LOG: restored log file "0000000200000000000000E6" from archive
2025-01-13 19:26:41.592 CET [116631] LOG: restored log file "0000000200000000000000E7" from archive
2025-01-13 19:26:41.876 CET [116631] LOG: restored log file "0000000200000000000000E8" from archive
2025-01-13 19:26:42.238 CET [116631] LOG: restored log file "0000000200000000000000E9" from archive
2025-01-13 19:26:42.333 CET [116631] LOG: recovery stopping before commit of transaction 32096, time 2025-01-13 19:21:40.349582+01
2025-01-13 19:26:42.333 CET [116631] LOG: redo done at 0/E904E420 system usage: CPU: user: 1.18 s, system: 0.18 s, elapsed: 3.15 s
2025-01-13 19:26:42.333 CET [116631] LOG: last completed transaction was at log time 2025-01-13 19:20:51.370895+01
2025-01-13 19:26:42.403 CET [116631] LOG: restored log file "0000000200000000000000E9" from archive
2025-01-13 19:26:42.498 CET [116631] LOG: selected new timeline ID: 3
2025-01-13 19:26:42.612 CET [116631] LOG: archive recovery complete
2025-01-13 19:26:42.615 CET [116629] LOG: checkpoint starting: end-of-recovery immediate wait
2025-01-13 19:26:42.806 CET [116629] LOG: checkpoint complete: wrote 4490 buffers (27.4%); 0 WAL file(s) added, 0 removed, 13 recycled; write=0.040 s, sync=0.114 s, total=0.194 s; sync files=36, longest=0.102 s, average=0.004 s; distance=213304 kB, estimate=213304 kB; lsn=0/E904E420, redo lsn=0/E904E420
2025-01-13 19:26:42.814 CET [116626] LOG: database system is ready to accept connectionsVérifier les logs et la présence des données disparues.
La trace ci-dessus indique bien :
consistent recovery state) ;selected new timeline ID: 3), comme après toute
restauration ;last completed transaction was at …).Les lignes perdues sont bien revenues :
Remarque :
Sans spécifier de --target-action=promote, on
obtiendrait dans les traces de PostgreSQL, après restore :