AccueilTechniqueMaintenance → Mise à jour de PostgreSQL

    Mise à jour de PostgreSQL

    Historique🔗

    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.

    Contraintes techniques🔗

    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.

    Enjeux🔗

    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.

    Mise à niveau🔗

    Cette étape se précède par une annonce d’interruption de service sur les réseaux sociaux.

    Création d’une nouvelle instance🔗

    Il faut d’abord créer une instance toute neuve avec la nouvelle version de PostgreSQL que nous allons mettre en réseau avec l’autre.

    1. Copier les fichiers de déploiement de l’instance dans le dossier core/services/ : cp -r postgres-db postgres-db-2
    2. Modifier les variables dans buildtime.env:
      • UNIT pour lui donner une IP différente de l’instance principale
      • PG_VERSION avec la nouvelle version majeure de PostgreSQL
      • CONTAINER_NAME pour lui donner un nom différent de l’instance principale.
    3. Mettre à jour le nom du volume monté avec le numéro de la nouvelle version de PostgreSQL.
    4. Retirer l’allocation du port WireGuard utilisé pour la replica dans la configuration copiée.
    5. Ajouter un fichier runtime.env pour définir les variables POSTGRES_USER et POSTGRES_PASSWORD. C’est également le moment idéal pour faire tourner les identifiants du compte administrateur postgres.
    6. Créer le nouveau volume spécifié à l’étape 3 avec docker volume create.

    Démarrer l’instance.

    Configuration de la nouvelle instance🔗

    Les fichiers de configuration postgresql.conf et pg_hba.conf doivent être mis à jour dans le volume de la nouvelle instance.

    Utilisez vimdiff pour copier les paramètres des anciens fichiers de configuration vers les nouveaux. Attention : n’appliquez pas immédiatement le paramètre listen_addresses, car la nouvelle instance ne pourra pas prendre les adresses utilisées par l’instance actuelle pendant la mise à jour.

    Redémarrez l’instance pour appliquer les changements.

    Configuration de l’ancienne instance🔗

    Activez l’entrée dédiée aux mises à niveau dans pg_hba.conf. Cette entrée est présente dans les fichiers de configuration; elle doit être restreinte à l’IP de la nouvelle instance et utiliser la méthode d’authentification trust à titre exceptionnel.

    Exécutez SELECT pg_reload_conf(); sur l’ancienne instance pour recharger les changements sans redémarrage.

    Test de connexion🔗

    Avec l’ancienne instance🔗

    Ouvrez une shell sur le conteneur de la nouvelle instance et essayez d’établir une connexion à l’ancienne instance :

    psql -U <POSTGRES_USER> -p <PORT> -h <OLD_POSTGRES_IP>
    

    La connexion doit s’ouvrir sans vous demander le mot de passe de l’utilisateur postgres.

    Avec la nouvelle instance🔗

    De la même manière, avec la même shell, essayez d’établir une connexion à la nouvelle instance :

    psql -U <POSTGRES_USER> -p <PORT>
    

    Le serveur PostgreSQL devrait vous demander votre mot de passe lors de la connexion.

    Préparation de la configuration du service🔗

    Le dossier de configuration postgres-db-2 dans core/services/ étant temporaire, il est nécessaire de reporter certains changements dans le dossier original postgres-db pour appliquer la mise à niveau.

    Il est préférable de préparer ces changements dans les fichiers avant l’interruption des services afin de minimiser le temps d’interruption.

    • Modifiez PG_VERSION avec la nouvelle version.
    • Modifiez le nom du volume avec la nouvelle version.
    • Reprenez le paramètre listen_addresses de l’ancienne instance dans postgresql.conf.

    Ne redémarrez pas le conteneur postgres-db et ne rechargez pas sa configuration : elle sera directement appliquée lors du redémarrage de fin de maintenance.

    Migration des données🔗

    1. Interrompez le replica PostgreSQL.

    2. Interrompez tous les services qui utilisent PostgreSQL et annoncez le début de la maintenance planifiée.

    3. Exécutez la commande suivante sur la nouvelle instance :

      pg_dumpall -U <POSTGRES_USER> -p <PORT> -h <OLD_POSTGRES_IP> | psql -U <POSTGRES_USER> -p <PORT>
      

      Cette étape peut durer quelques minutes.

    4. Croisez les doigts. Si aucune erreur ne s’affiche, stoppez le conteneur postgres-db-2 et redémarrez le conteneur postgres-db, qui devrait charger directement la configuration que vous avez préparée à l’étape précédente.

    Nettoyage🔗

    1. Vérifiez dans les logs du conteneur que l’instance a bien démarré avec la nouvelle version de PostgreSQL.
    2. Démarrez tous les services, vérifiez leurs logs et annoncez la fin de la maintenance.
    3. Supprimez le dossier postgres-db-2.
    4. Une fois avoir bien vérifié que toutes les données ont été migrées correctement, supprimez le volume PostgreSQL de l’ancienne instance.
    5. Suivez le guide pour reconstruire la replica PostgreSQL avec la nouvelle version.