Aller au contenu

Mise à jour majeure avec pg_upgrade

L’outil pg_upgrade a pour but de faciliter la mise en place d’une nouvelle version majeure sans traitement des fichiers utilisateurs qui représentent habituellement la grosse majorité de la volumétrie d’une instance. Il a l’avantage d’être extrêmement rapide et simple à utiliser.

Une mise à jour majeure de PostgreSQL est une opération complexe à préparer prudemment. Elle requiert du temps et une préparation minutieuse :

  • bien lire les Release Notes ;
  • bien tester l’application avec la nouvelle version.

Cette procédure est applicable pour toutes les versions de PostgreSQL et pour les environnements Linux les plus courants (Debian, Ubuntu, CentOS, Redhat).

La procédure se décompose en 3 étapes principales

  • Installation ;
  • Migration ;
  • Finalisation.

Sauf indication contraire, toutes les commandes indiquées ci-dessous doivent être exécutées avec l’utilisateur root.

À titre d’exemple, ce qui suit décrit une migration de PostgreSQL 14 à 16. Adapter les numéros de versions en conséquence.

Création de l’instance cible

Installation de PostgreSQL

(Pour l’installation de PostgreSQL avec les paquets du PGDG, voir les howto de yum.postgresql.org ou apt.postgresql.org ou cet extrait de nos formations ; ou encore les indications officielles.)

# Pour CentOS, Red Hat, Rocky Linux
dnf install -y postgresql16-server

# Pour Debian, Ubuntu…
apt-get install postgresql-16

Création de l’instance

# pour CentOS et RedHat
PGSETUP_INITDB_OPTIONS="--data-checksums" /usr/pgsql-16/bin/postgresql-16-setup initdb

systemctl enable postgresql-16

Pour Debian, une instance doit être créée manuellement, si l’outil pg_upgradecluster n’est pas utilisé.

# pour Debian
pg_dropcluster 16 main --stop
pg_createcluster 16 main -- --data-checksums

systemctl enable postgresql@16-main

Transfert des configurations

La configuration présente dans les fichiers suivants doit être reportée en tenant compte des possibles incompatibilités entre versions majeures :

  • postgresql.conf et inclusions ;
  • pg_hba.conf ;
  • pg_ident.conf.

Migration

Test préalable

La commande suivante doit être exécutée avec l’utilisateur postgres, ou propriétaire des binaires

# Pour CentOS et RedHat
export PGDATAOLD=/var/lib/pgsql/14/data
export PGDATANEW=/var/lib/pgsql/16/data
export PGBINOLD=/usr/pgsql-14/bin
export PGBINNEW=/usr/pgsql-16/bin

/usr/pgsql-16/bin/pg_upgrade --check 

# Pour Debian
export PGDATAOLD=/var/lib/postgresql/14/main
export PGDATANEW=/var/lib/postgresql/16/main
export PGBINOLD=/usr/lib/postgresql/14/bin
export PGBINNEW=/usr/lib/postgresql/16/bin

/usr/lib/postgresql/16/bin/pg_upgrade --check \
  --old-options='--config_file=/etc/postgresql/14/main/postgresql.conf' \
  --new-options='--config_file=/etc/postgresql/16/main/postgresql.conf'
Résultat attendu :

[...]

*Clusters are compatible*

Exécution de pg_upgrade

Deux modes sont disponibles avec pg_upgrade :

  • la copie de fichiers, plus lente et nécessitant le double d’espace disque, permet de revenir en arrière sans reprise de sauvegarde en cas de problème ;
  • la création de liens physiques, plus rapide, nécessite de restaurer une sauvegarde en cas de retour en arrière (voir cet article de Florent Jardin : Les liens physiques avec pg_upgrade).

L’arrêt de l’ancien et du nouveau service sont requis.

# Pour CentOS et RedHat
systemctl stop postgresql-14
systemctl stop postgresql-16
systemctl disable postgresql-14
# Pour Debian
systemctl stop postgresql@14-main
systemctl stop postgresql@16-main
systemctl disable postgresql@14-main

Pour migrer, il suffit de reprendre la commande de l’étape de test et de retirer l’option --check

# Pour CentOS et RedHat
export PGDATAOLD=/var/lib/pgsql/14/data
export PGDATANEW=/var/lib/pgsql/16/data
export PGBINOLD=/usr/pgsql-14/bin
export PGBINNEW=/usr/pgsql-16/bin

/usr/pgsql-16/bin/pg_upgrade

La méthode de migration sans copie de fichiers nécessite l’option --link

# Pour CentOS et RedHat
export PGDATAOLD=/var/lib/pgsql/14/data
export PGDATANEW=/var/lib/pgsql/16/data
export PGBINOLD=/usr/pgsql-14/bin
export PGBINNEW=/usr/pgsql-16/bin

/usr/pgsql-16/bin/pg_upgrade --link

Exécution de pg_upgradecluster (Debian uniquement)

La communauté Debian met à disposition le wrapper pg_upgradecluster pour faciliter les opérations de mise à jour majeures, notamment :

  • détecte la dernière version disponible pour le nouveau cluster (l’option -v permet de forcer une version) ;
  • crée un nouveau cluster de toute pièce ;
  • met en place un nouveau fichier de configuration sur la base des valeurs de l’ancien cluster ;
  • bascule les ports d’écoute entre les deux clusters.

Au préalable, s’assurer que le cluster est bien détruit :

pg_dropcluster 16 main

L’activation des sommes de contrôles doit être prédéfinie dans la configuration, car pg_upgradecluster délègue la création du cluster cible au script pg_createcluster

initdb_options = '--data-checksums'

Pour Debian

export LC_CTYPE=en_US.UTF-8 
export LC_ALL=en_US.UTF-8

pg_upgradecluster -v 16 14 main --method=upgrade

# sans copie des fichier
pg_upgradecluster -v 16 14 main --method=upgrade --link

manpage pg_upgradecluster

En cas de réplication physique

Se reporter aux conseils de la documentation

Finalisation

Démarrage de l’instance cible

# Pour CentOS et RedHat
systemctl start postgresql-16

# Pour Debian
systemctl start postgresql@16-main

Mise à jour des extensions

Si l’une des bases de données contenue dans l’instance dispose d’une ou de plusieurs extensions, il est possible que des mises à jour d’extensions soient disponibles pour cette nouvelle version majeure.

Si c’est le cas, pg_upgrade crée un script update_extensions.sql dans le répertoire courant, voici un exemple de son contenu :

\CONNECT base1
ALTER EXTENSION "foo" UPDATE;
\CONNECT postgres
ALTER EXTENSION "foo" UPDATE;

Dans le cas où les fichiers de mise à jour propre à l’extension ne sont pas accessibles, il est préférable de recréer l’extension, par exemple :

ALTER EXTENSION "foo" UPDATE;
-- extension "foo" has no update path from version "0.15" to version "0.22"

DROP EXTENSION "foo";
CREATE EXTENSION "foo";

Calcul des statistiques

Même si le système est directement utilisable, l’instance cible ne dispose d’aucune statistique sur les données, ce qui risque de ralentir fortement les premières requêtes. Pour corriger cela, l’outil pg_upgrade crée un script permettant simplement de calculer les statistiques. Le script est créé dans le répertoire courant à partir duquel pg_upgrade a été exécuté.

./analyze_new_cluster.sh

Il est tout à fait possible d’exécuter la commande vacuumdb --all --analyze en remplacement du script.

# Pour CentOS et RedHat
/usr/pgsql-16/bin/vacuumdb --all --analyze

# Pour Debian
/usr/lib/postgresql/16/bin/vacuumdb --all --analyze

Suppression de l’ancienne instance

Il est préférable de ne pas laisser l’ancienne instance sur le serveur hôte.

L’outil pg_upgrade crée un script permettant de supprimer l’ancienne instance. Le script delete_old_cluster.sh est créé dans le répertoire courant à partir duquel pg_upgrade a été exécuté.

./delete_old_cluster.sh

Les paquets de l’ancienne version ne sont plus nécessaires, il est préférable les retirer pour éviter toute confusion.

# Pour CentOS et RedHat
dnf remove postgresql14-server postgresql14-contrib

# Pour Debian
apt-get autoremove --purge postgresql-14

Erreurs rencontrées

max_locks_per_transaction

Une erreur peut être rencontrée lors de l’utilisation de pg_upgrade.

pg_dump: error: query failed: ERROR: out of shared memory
HINT: You might need to increase max_locks_per_transaction.

pg_upgrade utilise pg_dump pour récupérer le schéma de chaque base de données de l’instance source, afin de l’appliquer sur l’instance cible. Pour cela pg_upgrade démarre l’instance sur le port 50432 (pour éviter d’utiliser les ports classiques) et lance la commande suivante :

command: "/usr/pgsql-xx/bin/pg_dump" --host /mnt/data/pgsql --port 50432 --username postgres --schema-only --quote-all-identifiers --binary-upgrade --format=custom --file="pg_upgrade_dump_xxxxxx.custom"

L’erreur rencontrée est due au trop grand nombre de tables/partitions à récupérer lors du pg_dump. Il faut, pour palier à cette erreur, augmenter le max_locks_per_transaction sur l’instance à migrer.

Par défaut max_locks_per_transaction est à 64. Le temps de la migration, il peut être fixé bien plus haut sans risque : par exemple 1000