PostgreSQLLa base de données la plus sophistiquée au monde.

24.4. Serveurs de secours semi-automatique (Warm Standby) pour la haute disponibilité

L'archivage continu peut être utilisé pour créer une configuration de cluster de haute disponibilité (HA) avec un (ou plusieurs) serveur(s) de secours, prêt(s) à prendre en main les opérations en cas de défaillance du serveur principal. Cette fonctionnalité est surtout connue sous le nom de Warm Standby ou Log Shipping.

Le serveur principal et le serveur de secours travaillent ensemble pour fournir cette capacité, bien que les serveurs soient très faiblement couplés. Le serveur principal opère en mode d'archivage continu alors que chaque serveur de secours opère en mode de récupération continue en lisant les fichiers WAL du serveur primaire. Aucune modification des tables de la base n'est requise pour activer cette capacité. Elle a de ce fait un coût modique en terme d'administration supplémentaire en comparaison avec d'autres approches de réplication. Cette configuration a également un impact relativement faible sur les performances du serveur principal.

Le déplacement direct des enregistrements des WAL d'une base à une autre est généralement appelé « transfert de journaux » (log shipping). Sous PostgreSQL™, c'est un transfert de fichiers, de sorte que les enregistrements WAL sont transférés fichier par fichier. Les fichiers WAL peuvent être envoyés facilement et sans surcoût quelque soit la distance, que ce soit sur un système adjacent, sur un autre système du même site ou sur un autre système de l'autre côté du globe. La bande passante requise par cette technique varie en fonction du taux de transaction du serveur principal. Le transfert de journaux par enregistrement (record-based log shipping) est également réalisable, à l'aide de procédures personnalisées, discutées dans Section 24.4.4, « Transfert de journaux d'après les enregistrements (Record-based Log Shipping) ».

L'envoi des journaux est asynchrone, ce qui signifie que les enregistrements WAL sont envoyés après validation de la transaction. De ce fait, il y a un léger risque de perte de données si le serveur principal est l'objet d'une panne catastrophique : les transactions qui ne sont pas encore transmises sont perdues. La taille de la fenêtre des données perdues peut être réduite par l'utilisation du paramètre archive_timeout. Celui-ci peut être positionné à une valeur de quelques secondes seulement, si nécessaire. Toutefois, une valeur aussi faible augmente considérablement les besoins en bande passante du transfert de fichiers. Si une valeur aussi basse est nécessaire, il peut être préférable de s'intéresser au transfert de journaux par enregistrement.

Le serveur de secours n'est pas accessible, car il est en permanence occupé par la récupération. Les performances de récupération sont suffisamment bonnes pour que l'attente soit réduite avant d'obtenir une disponibilité complète une fois le serveur de secours activé. En conséquence, on appelle cela une configuration de reprise intermédiaire ou de secours semi-automatique (warm standby), qui permet d'atteindre la haute disponibilité. La restauration d'un serveur à partir d'une sauvegarde archivée de la base et de la relecture des journaux prend considérablement plus de temps. Cette technique n'offre qu'une solution de récupération suite à une panne, mais pas de la haute disponibilité.

24.4.1. Planification

Il est généralement préférable de créer des serveurs primaire et de secours aussi semblables que possible, au moins du point de vue du serveur de bases de données. En particulier, les noms des chemins associés aux tablespaces sont passés tels quels. Les serveurs doivent donc, tous, posséder les mêmes chemins de montage des tablespaces si cette fonctionnalité est utilisée. Si CREATE TABLESPACE est exécuté sur le serveur principal, tout nouveau point de montage nécessaire doit être créé à la fois sur le serveur principal et sur tous les serveurs de secours, avant l'exécution de la commande. Le matériel ne doit pas obligatoirement être identique mais l'expérience prouve qu'il est plus facile de maintenir deux systèmes identiques sur la durée de vie de l'application et du système. Dans tous les cas, l'architecture du matériel doit être identique -- le transfert d'un système 32 bits à un 64 bits ne fonctionne pas.

En général, il n'est pas possible d'effectuer le transfert de journaux entre serveurs PostgreSQL™ de versions majeures différentes. La politique des développeurs du « PostgreSQL Global Development Group » consiste à ne pas modifier les formats disque lors de mises à jour mineures. De ce fait, il est possible d'utiliser des serveurs principal et de secours de versions mineures différentes. Toutefois, aucune garantie formelle n'est offerte à ce sujet. Il est préférable, dans la mesure du possible, de conserver les serveurs primaire et de secours au même niveau de version. Lors de la mise à jour vers une nouvelle version mineure, la meilleure politique est de mettre à jour les serveurs de secours en premier -- il y a plus de chances qu'une version mineure sache lire les fichiers WAL d'une version mineure précédente que le contraire.

Il n'y a pas de mode particulier requis pour activer un serveur de secours. Les seules opérations sur les serveurs principal et de secours sont des tâches normales d'archivage continu et de récupération. Le seul point de contact entre les deux serveurs est l'archive de fichiers WAL qu'ils partagent : le principal les écrit, le serveur de secours les lit. Il est impératif que des archives WAL de serveurs primaires différents ne soient pas mélangées. L'archive n'est pas nécessairement large, si elle ne sert qu'à l'opération de standby.

La magie qui permet à deux serveurs faiblement liés de travailler ensemble réside dans une simple restore_command utilisée sur le serveur de secours qui attend la disponibilité du prochain fichier WAL en provenance du serveur principal. restore_command est indiqué dans le fichier recovery.conf du serveur de secours. En fonctionnement normal, le processus de récupération attend un journal de transactions dans le répertoire d'archivage des WAL, remontant une erreur si le fichier est indisponible. Dans le cas du serveur de secours, il est normal que le fichier suivant n'existe pas. Il ne reste donc qu'à attendre son arrivée. Pour les fichiers se terminant en .backup ou .history, il n'est pas besoin d'attendre, et un code retour différent de zéro doit être retourné. Une restore_command d'attente peut être obtenue par l'écriture d'un script personnalisé qui boucle sur un test d'apparition du prochain fichier WAL. Il faut également définir un moyen de déclencher la bascule (failover), ce qui interrompt la restore_command, sort de la boucle et retourne une erreur au serveur de secours indiquant que le fichier n'a pas été trouvé. Cela arrête alors la récupération et le serveur de secours devient un serveur normal.

Un exemple de pseudocode de restore_command peut être :

triggered = false;
while (!NextWALFileReady() && !triggered)
{
    sleep(100000L);         /* wait for ~0.1 sec */
    if (CheckForExternalTrigger())
        triggered = true;
}
if (!triggered)
        CopyWALFileForRecovery();

Un exemple fonctionnel de restore_command d'attente est fourni dans un module contrib appelé pg_standby. Il devrait être utilisé comme référence sur la façon d'implémenter correctement la logique décrite ci-dessus. Cet exemple peut être étendu si nécessaire pour supporter des configurations ou environnements spécifiques.

PostgreSQL™ ne fournit pas de logiciel système qui permette d'identifier une panne du serveur principal et d'en notifier le système et le serveur de bases de données de secours. De nombreux outils de ce type existent et sont bien intégrés incluant d'autres aspects nécessaires à la réussite du failover, comme la migration d'adresse IP.

Le déclenchement du failover est une partie importante de la planification et de la conception. restore_command est exécutée complètement pour chaque fichier WAL. Le processus exécutant restore_command est du coup créé et meurt pour chaque fichier. Il n'y a, de ce fait, pas de démon ou de processus serveur. Il n'est donc pas possible d'utiliser les signaux et un gestionnaire de signal. Une notification plus permanente est requise pour le déclenchement du failover. Il est possible d'utiliser un simple dépassement de temps d'attente, spécialement en conjonction avec un paramètre archive_timeout défini sur le serveur principal et connu. Cela peut toutefois induire des erreurs, car un problème réseau ou l'occupation du serveur principal peuvent suffire à provoquer un failover. Un mécanisme de notification tel que la création explicite d'un fichier déclencheur, quand cela est possible, présente moins de risques d'erreur.

La taille de l'archive des journaux de transaction peut être minimisée en passant l'option %r à restore_command. Cette option indique le dernier fichier archive à conserver pour permettre un démarrage correct de la restauration. Cela peut être utilisé pour tronquer l'archive lorsque les fichiers ne sont plus nécessaires, s'il est possible d'écrire sur l'archive depuis le serveur de secours.

24.4.2. Mise en place

Une procédure courte de configuration d'un serveur de secours est indiquée dans la suite du document. Pour les détails complets de chaque étape, on peut se référer aux sections précédentes.

  1. Préaparer des configurations des serveurs principal et de secours aussi proches que possible, ce qui inclut deux copies identiques de PostgreSQL™, dans la même version.

  2. Configurer l'archivage continu sur le serveur principal vers une archive locale située dans un répertoire du serveur de secours. S'assurer que les paramètres archive_mode, archive_command et archive_timeout sont correctement configurés sur le serveur principal (voir Section 24.3.1, « Configurer l'archivage WAL »).

  3. Faire une sauvegarde des bases du serveur principal (voir Section 24.3.2, « Réaliser une sauvegarde de base ») et charger ces données sur le serveur de secours.

  4. Commencer la récupération sur le serveur de secours à partir de l'archive WAL locale en utilisant un fichier recovery.conf qui utilise une restore_command d'attente (voir Section 24.3.3, « Récupération à partir d'un archivage continu »).

La récupération traite l'archive WAL en lecture seule. Ainsi, dès lors qu'un fichier WAL est copié sur le système de secours, il peut être copié sur une cassette en même temps qu'il est lu pour la récupération. De ce fait, un serveur de secours de haute disponibilité peut tourner en parallèle du stockage des fichiers en vue de besoins de reprise sur panne à plus long terme.

Pour les tests, il est possible d'exécuter le serveur principal et celui de secours sur le même système. Cela n'apporte aucune amélioration à la robustesse du système, et ne peut pas être présenté comme de la haute disponibilité.

24.4.3. Bascule (Failover)

Si le serveur principal rencontre un problème, le serveur de secours doit commencer la procédure de failover.

Si le serveur de secours échoue, alors le failover n'est plus nécessaire. S'il peut être redémarré, même quelque temps après, alors le processus de récupération peut être immédiatement relancé en tirant parti de la restauration réactivable (Restartable Recovery). Enfin, s'il ne peut être redémarré, un nouveau serveur de secours doit être créé.

Si le serveur principal tombe et redémarre immédiatement, il faut disposer d'un mécanisme l'informant qu'il n'est plus le serveur principal. Ce mécanisme, connu sous l'acronyme STONITH (Shoot The Other Node In The Head), permet d'éviter les situations où les deux systèmes pensent qu'ils sont le serveur principal, ce qui engendrerait une certaine confusion et conduirait à des pertes irrémédiables de données.

Un grand nombre de systèmes de failover utilisent simplement deux systèmes, le principal et celui de secours, connectés par un mécanisme appelé « heartbeat » qui vérifie en permanence la connectivité entre les deux systèmes et la viabilité du principal. Il est aussi possible d'utiliser un troisième système, appelé serveur témoin pour éviter tout problème de failover inapproprié. Cependant, la complexité supplémentaire peut s'avérer inutile sauf s'il est configuré avec une attention suffisante et des tests rigoureux.

Au moment où le failover est mis en place sur le serveur de secours, un seul serveur est opérationnel. On parle alors d'état dégénéré. L'ancien serveur de secours est devenu serveur principal, mais l'ancien serveur principal est arrêté et peut le rester. Pour revenir au fonctionnement normal, il faut désormais recréer intégralement un serveur de secours, soit sur l'ancien système principal, s'il peut redémarrer, soit sur un troisième système, de préférence nouveau. Cela réalisé, on peut considérer que les deux serveurs ont échangé leurs rôles.

Certains administrateurs choisissent d'utiliser un troisième serveur pour servir de sauvegarde au nouveau serveur principal le temps que le serveur de secours devienne opérationnel. Toutefois, cela complique la configuration du système et des processus opérationnels.

La bascule entre le serveur principal et celui de secours peut être rapide, mais nécessite quelque temps pour repréparer le cluster de failover. Une bascule régulière est utile car elle permet l'arrêt régulier de chaque système pour maintenance. Cela permet également de tester les mécanismes de failover pour s'assurer que cela fonctionnera toujours en cas de besoin. Il est judicieux de rédiger ces procédures.

24.4.4. Transfert de journaux d'après les enregistrements (Record-based Log Shipping)

PostgreSQL™ supporte directement le transfert de journaux d'après fichiers (file-based log shipping) tel que décrit plus haut. Il est également possible d'implanter le transfert de journaux d'après enregistrements, mais cela demande un développement adapté.

Un programme externe peut faire appel à la fonction pg_xlogfile_name_offset() (voir à ce propos la Section 9.24, « Fonctions d'administration système ») pour trouver le nom du fichier et l'exact décalage en octets de la fin courante du WAL à l'intérieur de celui-ci. Il peut alors accéder directement au fichier WAL et copier les données depuis la dernière fin connue des WAL jusqu'à l'actuelle vers le(s) serveur(s) de secours. Par cette approche, la fenêtre de perte de données correspond au temps du cycle d'interrogation du programme de copie. Elle peut alors être très courte. Il n'y a, de plus, pas de surconsommation de la bande passante du fait de l'archivage de fichiers segment partiellement utilisés. Les scripts restore_command du serveur de secours continuent de traiter avec l'ensemble des fichiers WAL. De ce fait, les serveurs de secours ne peuvent pas, en fonctionnement normal, disposer des données copiées par incréments. Elles n'ont d'intérêt que lorsque le serveur principal subit une panne -- le dernier fichier WAL partiel est alors chargé dans le serveur de secours avant qu'il puisse démarrer. Un codage correct de cette procédure nécessite une réelle coopération entre le script de restore_command et le programme de copie des donneés.