La mise à jour de PostgreSQL vers la dernière version stable est devenue une tradition annuelle importante dans notre infrastructure. Notre première utilisation de PostgreSQL remonte aux premiers jours de l’association, en 2019, avec PostgreSQL 11.
Depuis, chaque année, nous préparons la mise à jour de PostgreSQL quelques semaines après la sortie de la dernière version stable.
Contrairement aux mises à jour mineures qui peuvent être appliquées par un simple redémarrage, la mise à jour vers une version majeure de PostgreSQL nécessite obligatoirement une migration complète de la base de données vers une nouvelle instance toute neuve.
La réalisation de cette migration est particulièrement délicate avec Docker, car l’un des moyens standard d’effectuer la mise à niveau est de démarrer les deux binaires de PostgreSQL simultanément (celui de l’ancienne version et de la nouvelle version), or ce n’est pas possible avec l’image Docker officielle library/postgres car chaque image Docker ne contient que le binaire correspondant à sa version.
Il existe une image Docker tianon/docker-postgres-upgrade créée par la communauté, qui fait coexister les deux binaires dans une même image et réalise la migration. Toutefois, nous avons fait le choix de ne pas l’utiliser au profit de solutions plus traditionnelles.
En effet, la documentation mentionne également la possibilité de migrer les données entre deux instances de versions différentes par le réseau, et c’est la solution que nous avons choisie.
Un grand nombre de nos services utilisent la même instance PostgreSQL (Matrix, Liens, Git, Nextcloud, Forms), ce qui a pour conséquence de rendre ces services indisponibles le temps de la mise à jour, qui peut durer de 5 minutes à une heure selon la taille de la base de données et les éventuels problèmes rencontrés (plus souvent des fautes d’inattention).
Pour éviter de déranger nos utilisateur·ice·s, nous avions pour habitude d’effectuer cette mise à jour annuelle dans la nuit (à partir de 01h00).
Il pourrait être possible de réduire le temps d’indisponibilité afin de ne pas impacter tous nos services en même temps, notamment en séparant la base de données sur plusieurs instances, mais en raison de la RAM limitée dont nous disposons, nous n’avons pas expérimenté cette solution.
Depuis PostgreSQL 17, nous expérimentons l’utilisation de pgautoupgrade pour mettre à jour facilement les instances PostgreSQL.
Si exécutée correctement et sans erreur, cette nouvelle procédure peut réduire à 2 minutes le temps d’interruption total (mesuré sur Balearica avec une base de données de 2 Go).
Veillez minutieusement à bien exécuter chaque étape dans l’ordre. Vous pouvez éventuellement préparer chaque commande avant d’arrêter le conteneur afin de gagner du temps.
Nous utiliserons $OLD pour désigner l’ancienne version majeure de Postgres, et $NEW pour désigner la nouvelle version.
./runtime.env est lu par l’hôte et contient POSTGRES_USER et POSTGRES_PASSWORD.PGAUTO_ONESHOT=yes dans ./runtime.env pour que le conteneur de mise à jour s’arrête tout seul après l’opération.buildtime.env, passez la variable VERSION à $NEW.docker-compose.yml en remplaçant l’image library/postgres:$VERSION par pgautoupgrade/pgautoupgrade:$NEW-debian.docker pull) les images pgautoupgrade/pgautoupgrade:$NEW-debian et postgres:$NEW.cp -rav $OLD $NEWpgautoupgrade devrait se lancer à la place.
postgresql.conf depuis l’ancienne version : cp $OLD/docker/postgresql.conf $NEW/docker/postgresql.confdocker-compose.yml : library/postgres:$VERSIONPGAUTO_ONESHOT=yes dans ./runtime.env.Vérifiez que la mise à jour est effective en vous rendant sur les applications qui utilisent la base de données, et en examinant le numéro de version majeure dans les logs de PostgreSQL.
Si tout s’est bien passé, après quelques jours, vous pourrez supprimer le dossier $OLD dans le volume PostgreSQL qui contient l’ancienne base de données. Ne l’oubliez pas, car cela peut représenter un gain de plusieurs Go d’espace.