Cela fait maintenant plusieurs mois que la version 16 est sortie. Il est vraiment temps de mettre à jour notre serveur en version 15 avec la version 16. Pour cela, il existe trois méthodes, et nous allons décrire chacune de ces méthodes dans cet article.
Commençons déjà par dire qu'il n'est pas nécessaire de passer par chaque version intermédiaire. Vous pouvez très bien mettre à jour une version 12 en version 16, sans avoir à faire les passages intermédiaires (versions 13, 14 et 15).
Le point important à savoir lors d'une mise à jour majeure est que les catalogues système changent d'une version majeure à une autre. De nouveaux catalogues peuvent apparaître, les anciens peuvent être modifiés. Par exemple, des colonnes peuvent leur être ajoutées ou supprimées, voire, dans certains cas, renommées ou changées de type. Ces changements dans les catalogues système nécessitent donc un travail préalable pour passer d'une version majeure à une autre, ce qui va occasionner une coupure de service pour les utilisateurs.
La méthode historique (généralement appelée dump/restore) est une méthode sûre, mais potentiellement lente. En fait, plus la volumétrie de l'instance est importante, plus la mise à jour sera longue. Deux autres méthodes (réplication logique et pg_upgrade) sont apparues au fil des années pour éviter justement une coupure importante du service, mais elles sont généralement plus complexes à mettre en place.
1. Installation de PostgreSQL 16
Quelle que soit la méthode sélectionnée, la première étape concerne l'installation de la nouvelle version de PostgreSQL. Seule la méthode pg_upgrade nécessite que la nouvelle version soit installée sur le même serveur. Cette nouvelle version peut être installée sur un nouveau serveur pour les deux autres méthodes. Dans ce cas, c'est généralement l'occasion de passer à un matériel plus performant ou tout du moins de mettre à jour le système d'exploitation.
Sur tous les exemples de cet article, je travaillerai sur le même serveur.
Voici rapidement les commandes exécutées pour l'installation de la version 16 (l'article « Installation de PostgreSQL » de cette série se trouve dans le numéro 139 de Linux Pratique et est aussi disponible sur https://connect.ed-diamond.com/linux-pratique/lp-139/installation-de-postgresql) :
Si vous suivez bien notre série sur PostgreSQL, vous avez peut-être remarqué que je n'ai pas utilisé l'option --wal-dir avec l'outil initdb. Comme je n'ai pas de partition pour les journaux de transactions de la nouvelle version, je ne déplace pas immédiatement le répertoire des journaux de transactions. Je ne le ferai qu'à la fin.
Pour la configuration du serveur, il est possible de copier les fichiers pg_hba.conf et pg_ident.conf de l'ancienne version :
Par contre, il ne faut jamais copier le fichier postgresql.conf de l'ancienne version vers la nouvelle version, car il manquerait les nouveaux paramètres et surtout les paramètres qui ont changé de nom se retrouveraient avec l'ancien nom, ce qui empêcherait le démarrage du service. Dans notre cas, notre configuration se trouve dans le fichier linuxpratique.conf et nous n'avons pas configuré de paramètres qui ont changé de nom, donc nous pouvons le copier sans souci :
Comme nous allons avoir deux services PostgreSQL démarrés sur le même serveur, leur numéro de port respectif doit être différent. Il est donc nécessaire d'éditer le fichier /srv/data16/linuxpratique.conf pour remplacer la ligne du paramètre port par la ligne suivante :
Il nous faut maintenant modifier le fichier /srv/data16/postgresql.conf pour ajouter l'inclusion de notre fichier de configuration. Cela se fait avec cette ligne :
Enfin, nous pouvons activer et démarrer le service :
2. Migration des données
2.1 Méthode dump/restore
Comme dit juste avant, cette méthode est la méthode historique. Elle peut être utilisée dans tous les cas, et elle est sûre. De plus, elle est simple à comprendre.
Elle a cependant un inconvénient de taille : un arrêt de production d'autant plus long que la volumétrie est importante (notamment lors de l'étape de restauration).
L'opération de mise à jour se passe en trois étapes :
- couper l'application ;
- sauvegarder les objets globaux et chaque base de l'ancienne version ;
- restaurer les objets globaux puis chaque base sur la nouvelle version.
Couper l'application revient à l'arrêter. En fait, il ne faut pas qu'il y ait d'écritures sur le serveur pendant les deux étapes suivantes, sinon ces nouvelles écritures ne seront pas migrées. Une solution assez simple et très sûre revient à couper les accès à la base. Cela peut se faire en configurant le fichier pg_hba.conf ou en modifiant les rôles applicatifs. Bref, il y a plusieurs solutions. Il faut juste s'assurer que les écritures ne sont pas possibles sur les bases de l'instance.
Ceci fait, nous pouvons procéder à une sauvegarde complète du serveur avec les outils pg_dump et pg_dumpall. Encore mieux, il est possible d'utiliser pg_back. C'est ce que nous allons faire maintenant :
Ceci fait, il nous faut restaurer cette sauvegarde sur la nouvelle version. Nous commençons par restaurer les objets globaux :
Cette commande va se plaindre que le rôle postgres existe déjà. C'est normal, il est créé automatiquement par l'outil initdb. Puis nous pouvons restaurer chaque base :
Comme nous pouvons le voir, cette méthode est vraiment très simple. Elle utilise des outils de sauvegarde standards normalement connus. Dans mon cas, cela a été très rapide vu que les bases ont une volumétrie minuscule. Cependant, pour des bases conséquentes, l’opération complète peut prendre de nombreuses heures pendant lesquelles les applications ne peuvent pas travailler.
2.2 Méthode réplication logique
Contrairement à la réplication physique qui ne peut pas se faire entre deux serveurs ayant une version majeure différente, la réplication logique peut se faire. Il est possible d'utiliser la solution native ou une solution externe (comme Slony par exemple), mais cet article n'abordera que la solution native.
Cette méthode nécessite de mettre en place la réplication logique sur chaque base à récupérer sur le nouveau serveur en version 16. Mais pour cela, il faut déjà configurer le serveur actuel pour que les journaux de transaction soient à un niveau compatible avec la réplication logique (valeur logical pour le paramètre wal_level). De plus, nous allons mettre en place une réplication par base. Il faut donc un bon nombre de workers et de slots de réplication. Tout cela passe par la configuration des paramètres wal_level, max_logical_replication_workers (et de ce fait max_worker_processes), et max_replication_slots dans le fichier /srv/data/linuxpratique.conf :
Pour que cette modification soit prise en compte, il faut redémarrer le serveur :
Pour rappel, le serveur en version 15 utilise le port 5432, et le serveur en version 16 utilise le port 5433.
Commençons par sauvegarder la définition des objets globaux sur le serveur en version 15 et à la restaurer sur le serveur en version 16 :
Le script suivant va récupérer la liste des bases où il est possible de se connecter et, pour chacune de ses bases, va :
- créer la base sur le serveur en version 16 ;
- sauvegarder le schéma de la base sur le serveur en version 15 ;
- restaurer ce schéma sur cette base du serveur en version 16 ;
- créer la publication de toutes les tables de la base du serveur en version 15 ;
- abonner le serveur en version 16 à la publication tout juste créée.
Ce script donnera deux erreurs sur la création des bases postgres et template1. Elles ne peuvent pas être créées étant donné qu’elles existent déjà.
À chaque création de souscription (ou abonnement), PostgreSQL va commencer par une phase de synchronisation des données entre le serveur en version 15 et le serveur en version 16. Dans mon cas, j’ai quatre bases, et donc quatre synchronisations en cours. Je dois attendre la fin de ces synchronisations, et pour cela, je dois surveiller le catalogue système pg_stat_replication :
Avec ce résultat, la deuxième colonne a la même valeur que la troisième colonne pour chaque ligne. Les quatre synchronisations sont donc terminées.
Il est néanmoins un type de données à synchroniser. Les séquences ne font pas partie des données répliquées. Il convient donc de les réinitialiser manuellement :
Maintenant, nous pouvons arrêter le serveur en version 15 :
et supprimer les souscriptions du serveur en version 16 :
Simplement supprimer la souscription ne suffit pas. En effet, à l’exécution d’un DROP SUBSCRIPTION, PostgreSQL va se connecter au serveur de publication pour supprimer le slot de réplication. Or nous avons arrêté le serveur en version 15 pour éviter de nouvelles écritures sur ce serveur. Donc nous devons supprimer l’information du slot de réplication sur la souscription (c’est le but de l’ordre ALTER SUBSCRIPTION ... SET (slot_nane=NONE), mais pour cela, nous devons tout d’abord désactiver la souscription, d’où le ALTER SUBSCRIPTION ... DISABLE.
2.3 Méthode pg_upgrade
pg_upgrade est certainement l'outil le plus récent, mais il a quand même maintenant une dizaine d'années. Autant dire que les bugs assez catastrophiques des premières années sont du passé.
Il fonctionne en deux modes : copie de fichiers et liens physiques. Le mode copie est plus long que le mode lien physique, mais il a l'avantage de permettre un retour en arrière si nécessaire, ce qui ne sera pas le cas avec le mode lien physique. Dans le cas de l'utilisation de ce deuxième mode, il convient donc de s'assurer d'avoir à sa disposition une sauvegarde complète de l'instance au cas malheureux où un retour en arrière serait nécessaire.
Les étapes de la mise à jour sont très restreintes : arrêt du serveur, lancement de pg_upgrade, démarrage du serveur. Voici un exemple concernant ma VM :
L'outil pg_upgrade indique qu’il faut exécuter une étape supplémentaire, mais cette étape est nécessaire, quelle que soit la méthode. Nous la verrons donc dans le prochain chapitre.
3. Nettoyage et remise en place des services
Il reste maintenant du nettoyage à faire pour revenir dans la situation nominale : déplacer les fichiers de données dans /srv/data et les journaux de transactions dans /srv/wal, et utiliser le port 5432 pour le service PostgreSQL en version 16.
Commençons déjà par arrêter les deux serveurs :
Nous allons maintenant déplacer les fichiers de données et les journaux de transactions de la version 15 dans un emplacement autre en attendant de les supprimer après quelques jours :
Nous pouvons maintenant déplacer les fichiers de données et les journaux de transaction de la version 16 à leur emplacement nominal :
Enfin, avant de démarrer PostgreSQL, nous devons configurer le port de connexion à 5432 :
Notez que si vous voulez toujours pouvoir démarrer la version 15 en même temps que la version 16, vous devez changer son numéro de port (par le 5433 par exemple).
Il ne reste plus qu’à modifier le répertoire de données dans le script de démarrage de la version 16 :
Il est enfin possible de démarrer PostgreSQL version 16 :
La version 15 reste éteinte, mais disponible dans le cas où des vérifications seraient à entreprendre (mais n’oubliez pas de modifier son numéro de port et son script de démarrage).
Une fois la nouvelle version en place, il est essentiel d’exécuter un VACUUM ANALYZE sur toutes les bases. C’était justement l’étape supplémentaire préconisée par pg_upgrade, mais, encore une fois, elle est valable pour toutes les méthodes. Cela peut se faire en une seule commande Unix :
Nous avons mis en place deux services de sauvegarde. Il faut s’assurer qu’ils fonctionnent avec la nouvelle version.
Ce ne sera pas le cas immédiatement pour pg_back. En effet, nous lui avons indiqué dans son fichier de configuration où se trouvaient les binaires de PostgreSQL. Il faut donc modifier sa configuration. Cela se fait ainsi :
Quant à pgbackrest, les sauvegardes précédemment effectuées sont incompatibles sur une autre version majeure. Il est donc préférable de supprimer les anciennes sauvegardes et de refaire une sauvegarde complète dès que possible :
Quant aux outils de supervision/monitoring, pgBadger n’a pas d’attachement fort à la version majeure. Profitez-en néanmoins pour le mettre à jour. PgCluu et temboard lisant les catalogues statistiques, ces derniers ont pu changer entre deux versions majeures. Il est donc essentiel d’avoir une version de ces deux outils compatible avec cette nouvelle version de PostgreSQL. Dans le doute, mettez-les à jour.
Pensez à faire de même pour tous les autres outils se connectant à PostgreSQL : outils d’administration, outils de modélisation, pilotes JDBC ou autre, etc.
4. Suppression de l’ancienne instance
Il est conseillé d’attendre au moins quelques jours, pour s’assurer que la nouvelle version fonctionne bien, que les données n’ont pas été altérées, qu’il n’y a pas eu d’erreurs commises, bref, qu’il n’y a pas de mauvaise surprise suite au passage à la nouvelle version de PostgreSQL. Évidemment, cela nécessite d’avoir suffisamment de place sur le serveur pour héberger deux versions de l’instance, mais il est toujours rassurant d’avoir l’ancienne version en place.
Si tous les voyants sont au vert, il est important de supprimer l’ancienne instance. Pour cela, il faut supprimer les données et les programmes installés. Voici comment faire en trois commandes :
5. Quelle méthode choisir ?
Maintenant que nous avons vu les trois méthodes possibles, discutons de laquelle choisir.
pg_dump/pg_restore :
+ fonctionne dans tous les cas
+ mise en place simple
+ supprime la fragmentation
- coupure de service importante
- retour en arrière possible, mais sans conservation des nouvelles données
réplication logique :
+ coupure de service très faible
+ supprime la fragmentation
+ retour en arrière possible avec conservation des nouvelles données (simple avec Slony, compliqué avec réplication native)
- peut ne pas fonctionner (voir plus bas)
- mise en place complexe
- mise en place encore plus complexe si plus d'une base utilisateur
- pas de changement de structure de la base pendant la synchronisation
- clé primaire ou contrainte d'unicité obligatoire pour toutes les tables
- pas de support des Large Objects
pg_upgrade :
+ mise en place simple
+ coupure de service faible
- peut ne pas fonctionner
- conserve la fragmentation
- retour en arrière possible si mode copie, mais sans conservation des nouvelles données
Conclusion : et les versions mineures ?
Il n'y a strictement aucune raison valable d'ignorer les versions mineures. Elles ne contiennent que des corrections de bugs, il faut donc les installer. C'est heureusement bien plus simple et bien plus rapide que les mises à jour majeures. Généralement, il suffit d'arrêter le service PostgreSQL, de mettre à jour les paquets, puis de redémarrer le service. Donc :
Il est néanmoins préférable de lire les Release Notes de la version pour savoir si des opérations supplémentaires sont à réaliser. C'est rare, mais cela peut arriver, autant le savoir.