Dalibo SCOP
Formation | Module R50 |
Titre | Généralités sur la Haute Disponibilité |
Révision | 24.09 |
https://dali.bo/r50_pdf | |
EPUB | https://dali.bo/r50_epub |
HTML | https://dali.bo/r50_html |
Slides | https://dali.bo/r50_slides |
Vous trouverez en ligne les différentes versions complètes de ce document.
Cette formation est sous licence CC-BY-NC-SA. Vous êtes libre de la redistribuer et/ou modifier aux conditions suivantes :
Vous n’avez pas le droit d’utiliser cette création à des fins commerciales.
Si vous modifiez, transformez ou adaptez cette création, vous n’avez le droit de distribuer la création qui en résulte que sous un contrat identique à celui-ci.
Vous devez citer le nom de l’auteur original de la manière indiquée par l’auteur de l’œuvre ou le titulaire des droits qui vous confère cette autorisation (mais pas d’une manière qui suggérerait qu’ils vous soutiennent ou approuvent votre utilisation de l’œuvre). À chaque réutilisation ou distribution de cette création, vous devez faire apparaître clairement au public les conditions contractuelles de sa mise à disposition. La meilleure manière de les indiquer est un lien vers cette page web. Chacune de ces conditions peut être levée si vous obtenez l’autorisation du titulaire des droits sur cette œuvre. Rien dans ce contrat ne diminue ou ne restreint le droit moral de l’auteur ou des auteurs.
Le texte complet de la licence est disponible sur http://creativecommons.org/licenses/by-nc-sa/2.0/fr/legalcode
Cela inclut les diapositives, les manuels eux-mêmes et les travaux pratiques. Cette formation peut également contenir quelques images et schémas dont la redistribution est soumise à des licences différentes qui sont alors précisées.
PostgreSQL® Postgres® et le logo Slonik sont des marques déposées par PostgreSQL Community Association of Canada.
Ce document ne couvre que les versions supportées de PostgreSQL au moment de sa rédaction, soit les versions 12 à 16.
Sur les versions précédentes susceptibles d’être encore rencontrées en production, seuls quelques points très importants sont évoqués, en plus éventuellement de quelques éléments historiques.
Sauf précision contraire, le système d’exploitation utilisé est Linux.
La haute disponibilité est un sujet complexe. Plusieurs outils libres coexistent au sein de l’écosystème PostgreSQL, chacun abordant le sujet d’une façon différente.
Ce document clarifie la définition de « haute disponibilité », des méthodes existantes et des contraintes à considérer. L’objectif est d’aider à la prise de décision et le choix de la solution.
Deux critères essentiels permettent de contraindre le choix d’une solution : le RTO (recovery time objectives) et le RPO (recovery point objective).
Le RTO représente la durée maximale d’interruption de service admissible, depuis la coupure de service jusqu’à son rétablissement. Il inclut le délai de détection de l’incident, le délai de prise en charge et le temps de mise en œuvre des actions correctives. Un RTO peut tendre vers zéro mais ne l’atteint jamais parfaitement. Une coupure de service est le plus souvent inévitable, aussi courte soit-elle.
Le RPO représente la durée maximale d’activité de production déjà réalisée que l’on s’autorise à perdre en cas d’incident. Contrairement au RTO, le RPO peut atteindre l’objectif de zéro perte.
Les deux critères sont complémentaires. Ils ont une influence importante sur le choix d’une solution et sur son coût total. Plus les RTO et RPO sont courts, plus la solution est complexe. Cette complexité se répercute directement sur le coût de mise en œuvre, de formation et de maintenance.
Le coût d’une architecture est exponentiel par rapport à sa disponibilité.
La Haute Disponibilité de service définit les moyens techniques mis en œuvre pour garantir une continuité d’activité suite à un incident sur un service.
La haute disponibilité de service nécessite de redonder tous les éléments nécessaires à l’activité du service : l’alimentation électrique, ses accès réseaux, le réseau lui-même, les serveurs, le stockage, les administrateurs, etc.
En plus de cette redondance, une technique de réplication synchrone ou asynchrone est souvent mise en œuvre afin de maintenir à l’identique ou presque les serveurs redondés.
Pour que la disponibilité ne soit pas affectée par le temps de réaction des humains, on peut rechercher à automatiser les bascules vers un serveur sain en cas de problème.
La Haute disponibilité des données définit les moyens techniques mis en œuvre pour garantir une perte faible voire nulle de données en cas d’incident. Ce niveau de disponibilité des données est assuré en redondant les données sur plusieurs systèmes physiques distincts et en assurant que chaque écriture est bien réalisée sur plusieurs d’entre eux.
Dans le cas d’une réplication synchrone entre les systèmes, les écritures sont suspendues tant qu’elles ne peuvent être validées de façon fiable sur au moins deux systèmes.
Autrement dit, la haute disponibilité des données et la haute disponibilité de service sont contradictoires, le premier nécessitant d’interrompre le service en écriture si l’ensemble ne repose que sur un seul système.
Par exemple, un RAID 1 fonctionnant sur un seul disque suite à un incident n’est PAS un environnement à haute disponibilité des données, mais à haute disponibilité de service.
La position du curseur entre la haute disponibilité de service et la haute disponibilité de données guide aussi le choix de la solution. S’il est possible d’atteindre le double objectif, l’impact sur les solutions possibles et le coût est une fois de plus important.
Les différentes méthodes de sauvegardes de PostgreSQL (logique avec
pg_dump
, ou PITR avec pgBackRest ou d’autres outils) sont
souvent sous-estimées. Elles sont pourtant un élément essentiel de toute
architecture qui est souvent déjà présent. On n’oubliera d’ailleurs pas
de tester
régulièrement ses sauvegardes.
Investir dans l’optimisation des sauvegardes peut déjà assurer un certain niveau de disponibilité de votre service, à moindre coût.
Quoi qu’il en soit, la sauvegarde est un élément crucial de toute architecture. Ce sujet doit toujours faire partie de la réflexion autour de la disponibilité d’un service.
La sauvegarde PITR (Point In Time Recovery) est une méthode permettant de restaurer une instance PostgreSQL à n’importe quel instant durant la fenêtre de rétention définie, par exemple les dernières 24 heures. Le temps de restauration (RTO) dépend de deux variables : le volume de l’instance et son volume d’écriture.
Avec le bon matériel, les bonnes pratiques et une politique de sauvegarde adaptée, il est possible d’atteindre un RTO de quelques minutes, dans la plupart des cas.
La maîtrise du RPO (perte de données) repose sur la fréquence
d’archivage des journaux de transactions. Un RPO d’une minute est tout à
fait envisageable. En-dessous, nous entrons dans le domaine de la
réplication en streaming, soit vers une instance secondaire,
soit avec l’outil pg_receivewal
(pour la sauvegarde
uniquement). Nous abordons ce sujet dans un futur chapitre.
Il est possible d’utiliser les journaux de transactions archivés dans le cadre de la réplication physique. Ces archives deviennent alors un second canal d’échange entre l’instance primaire et ses secondaires, apportant une redondance à la réplication elle-même.
Parmi les outils existants et éprouvés au sein de la communauté, nous pouvons citer les deux ci-dessus.
Le principal point faible de la sauvegarde PITR est le temps de prise en compte de l’incident et donc d’intervention d’un administrateur.
Enfin, la sauvegarde PITR doit être surveillée de très près par les équipes d’administration au travers d’une supervision adaptée.
La réplication physique interne de PostgreSQL réplique le contenu des journaux de transactions. Les instances secondaires sont considérées comme des « clones » de l’instance primaire.
Avec peu de configuration préalable, il est possible de créer des instances secondaires directement à partir de l’instance primaire ou en restaurant une sauvegarde PITR.
La mécanique de réplication est très efficace, car elle ne réplique que les modifications binaires effectuées dans les tables et les index.
Cette étape assure déjà une haute disponibilité de données, ces dernières étant présentes sur plusieurs serveurs distincts.
La réplication permet d’atteindre un RPO et un RTO plus faibles que celui d’une simple sauvegarde PITR, au prix d’un investissement plus important (matériel redondant), d’une prix d’investissement plus important (redondance du matériel), d’une complexification de l’architecture et de sa maintenance. Le RTO deviendra également plus lié au temps de réaction qu’à la bascule technique.
PostgreSQL supporte la réplication asynchrone ou synchrone.
La réplication asynchrone autorise un retard entre
l’instance primaire et ses secondaires, ce qui implique un RPO supérieur
à zéro. Ce retard dépend directement du volume d’écriture envoyé par le
primaire et de la capacité du réseau à diffuser ce volume, donc son
débit. Une utilisation OLTP a un retard typique inférieur à la seconde.
Ce retard peut cependant être plus important lors des périodes de
maintenance (VACUUM
, REINDEX
) ou lors
d’écritures en masse de données.
La réplication synchrone s’assure que chaque écriture soit présente sur au moins deux instances avant de valider une transaction. Ce mode permet d’atteindre un RPO de zéro, mais impose d’avoir au minimum trois nœuds dans le cluster, autorisant ainsi la perte complète d’un serveur sans bloquer les écritures. En effet, avec deux nœuds seulement, la disponibilité de données n’est plus assurée : la perte d’un nouveau serveur entraînerait le blocage des écritures qui ne pourraient plus être synchrones.
De plus, le nombre de transactions par seconde dépend directement de la latence du réseau : chaque transaction doit attendre la propagation vers un secondaire et le retour de sa validation.
La réplication seule n’assure pas de disponibilité de service en cas d’incident.
Comme pour les sauvegardes PITR, le RTO dépend principalement du temps de prise en charge et d’analyse de l’incident par un opérateur. Une fois la décision prise, la promotion d’un serveur secondaire en production ne nécessite qu’une commande et ne prend typiquement que quelques secondes.
Reste ensuite à faire converger les connexions applicatives vers la nouvelle instance primaire, ce qui est facilement automatisé.
La réplication nécessite donc au minimum deux serveurs, voire trois en cas de réplication synchrone. À ce coût s’ajoutent plusieurs autres plus ou moins cachés :
Une bascule automatique lors d’un incident permet de réduire le temps d’indisponibilité d’un service au plus bas, assurant ainsi une haute disponibilité de service.
Néanmoins, automatiser la détection d’incident et la prise de décision de basculer un service est un sujet très complexe, difficile à bien appréhender et maintenir, d’autant plus dans le domaine des SGBD.
Quelle que soit la solution choisie pour détecter les anomalies et déclencher une bascule, celle-ci est toujours très naïve. Contrairement à un opérateur humain, la solution n’a pas de capacité d’analyse et n’a pas accès aux mêmes informations. En cas de non-réponse d’un élément du cluster, il lui est impossible de déterminer dans quel état il se trouve précisément. Sous une charge importante ? Serveur arrêté brutalement ou non ? Réseau coupé ?
Il y a une forte probabilité de split-brain si le cluster se contente d’effectuer une bascule sans se préoccuper de l’ancien primaire. Dans cette situation, deux serveurs se partagent la même ressource (IP ou disque ou SGBD) sans le savoir. Corriger le problème et reconsolider les données est fastidieux et entraîne une indisponibilité plus importante qu’une simple bascule manuelle avec analyse et prise de décision humaine.
Quatre mécaniques permettent de se prémunir plus ou moins d’un split-brain : le fencing, le quorum, le watchdog et le SBD (Storage Based Death). La plupart doivent être combinées pour fonctionner de façon optimale.
Le fencing (clôture) isole un serveur ou une ressource de façon active. Suite à une anomalie, et avant la bascule vers le secours prévu, le composant fautif est isolé afin qu’il ne puisse plus interférer avec la production.
Il existe au moins deux anomalies où le fencing est incontournable. La première concerne le cas d’un serveur qui ne répond plus au cluster. Il est alors impossible de définir quelle est la situation sur le serveur. Est-il encore vivant ? Les ressources sont-elles encore actives ? Ont-elles encore un comportement normal ? Ont-elles encore accès à l’éventuel disque partagé ? Dans cette situation, la seule façon de répondre avec certitude à ces questions est d’éteindre le serveur. L’action définit avec certitude que les ressources y sont toutes inactives.
La seconde anomalie où le fencing est essentiel concerne l’arrêt des ressources. Si le serveur est disponible, communique, mais n’arrive pas à éteindre une ressource (problème technique ou timeout), le fencing permet « d’escalader » l’extinction de la ressource en extinction du serveur complet.
Il est aussi possible d’isoler un serveur d’une ressource. Le serveur n’est pas éteint, mais son accès à certaines ressources cruciales est coupé, l’empêchant ainsi de corrompre le cluster. L’isolation peut concerner l’accès au réseau Ethernet ou à un disque partagé par exemple.
Il existe donc plusieurs techniques pour un fencing, mais il doit toujours être rapide et efficace. Pas de demi-mesures ! Les méthodes les plus connues soit coupent le courant, donc agissent sur l’UPS, ou le PDU ; soit éteignent la machine au niveau matériel via l’IPMI ; soit éteignent la machine virtuelle virtuelle brusquement via son hyperviseur ; soit coupent l’accès au réseau, SAN ou Ethernet.
Par conséquent, cette mécanique nécessite souvent de pouvoir gérer finement les droits d’accès à des opérations d’administration lourdes. C’est le cas par exemple au travers des communautés du protocole SNMP, ou la gestion de droits dans les ESX VMware, les accès au PDU, etc.
La mécanique du quorum attribue à chaque nœud un (ou plusieurs) vote. Le cluster n’a le droit d’héberger des ressources que s’il possède la majorité absolue des voix. Par exemple, un cluster à 3 nœuds requiert 2 votes pour pouvoir démarrer les ressources, 3 pour un cluster à 5 nœuds, etc.
Lorsque qu’un ou plusieurs nœuds perdent le quorum, ceux-ci doivent arrêter les ressources qu’ils hébergent.
Il est conseillé de maintenir un nombre de nœuds impair au sein du cluster, mais plusieurs solutions existent en cas d’égalité (par exemple par ordre d’identifiant, par poids, serveurs « témoins » ou arbitre, etc).
Le quorum permet principalement de gérer les incidents liés au réseau, quand « tout va bien » sur les serveurs eux-mêmes et qu’ils peuvent éteindre leurs ressources sans problème, à la demande.
Dans le cadre de PostgreSQL, il faut porter une attention particulière au moment où des serveurs isolés rejoignent de nouveau le cluster. Si l’instance primaire a été arrêtée par manque de quorum, elle pourrait ne pas se raccrocher correctement au nouveau primaire, voire corrompre ses propres fichiers de données. En effet, il est impossible de déterminer quelles écritures ont eu lieu sur cet ancien primaire entre sa déconnexion du reste du cluster et son arrêt total.
Pacemaker intègre la gestion du quorum et peut aussi utiliser un
serveur de gestion de vote appelé corosync-qnetd
. Ce
dernier est utile en tant que tiers pour gérer le quorum de plusieurs
clusters Pacemaker à deux nœuds par exemple.
Patroni repose sur un DCS1 extérieur, par exemple etcd, pour stocker l’état du serveur et prendre ses décisions. La responsabilité de la gestion du quorum est donc déléguée au DCS, dont l’architecture robuste est conçue pour toujours présenter des données fiables et de référence à ses clients (ici Patroni).
Tous les ordinateurs sont désormais équipés d’un watchdog. Par exemple, sur un ordinateur portable Dell Latitude, nous trouvons :
iTCO_wdt : Intel TCO WatchDog Timer Driver v1.11
Sur un Raspberry Pi modèle B :
bcm2835-wdt 20100000.watchdog : Broadcom BCM2835 watchdog timer
Au besoin, il est aussi possible d’ajouter plusieurs autres watchdog grâce à des cartes PCI par exemple, bien que ce ne soit pas nécessaire dans notre cas.
Concernant les machines virtuelles, une configuration supplémentaire est souvent nécessaire pour avoir accès à un watchdog virtualisé.
En dernier recours, il est possible de demander au noyau Linux
lui-même de jouer le rôle de watchdog grâce au module
softdog
. Néanmoins, cette méthode est moins fiable qu’un
watchdog matériel car il nécessite que le système
d’exploitation fonctionne toujours correctement et qu’au moins un des
CPU soit disponible. Cet article
entre plus en détails.
Le principe du watchdog peut être résumé par : « nourris le chien de garde avant qu’il ait faim et te mange ». En pratique, un watchdog est un compte à rebours avant la réinitialisation brutale du serveur. Si ce compte à rebours n’est pas régulièrement ré-armé, le serveur est alors redémarré.
Un watchdog surveille donc passivement un processus et assure que ce dernier est toujours disponible et sain. Dans le cadre d’un cluster en haute disponibilité, le processus rendant compte de sa bonne forme au watchdog est le clusterware.
Notez qu’un watchdog permet aussi de déclencher un self-fencing rapide et fiable en cas de besoin. Il permet par exemple de résoudre rapidement le cas de l’arrêt forcé d’une ressource, déjà présenté dans le chapitre consacré au fencing.
Patroni et Pacemaker sont tous deux capables d’utiliser un watchdog sur chaque nœud. Pour Patroni, il n’est armé que sur l’instance primaire. Pour Pacemaker, il est armé sur tous les nœuds.
Le Storage Base Death est une méthode assez ancienne. Elle
utilise un ou plusieurs disques partagés (pour la redondance), montés
sur tous les nœuds du cluster à la fois. L’espace nécessaire sur chaque
disque est très petit, de l’ordre de quelques mégaoctets pour plusieurs
centaines de nœuds (le démon sbd
utilise 1 à 4 Mo pour 255
nœuds). Cet espace disque est utilisé comme support de communication
entre les nœuds qui y échangent des messages.
Le clusterware peut isoler un nœud en déposant un message poison pill à son attention. Le destinataire s’auto-fence grâce à son watchdog dès qu’il lit le message. De plus, un nœud s’auto-fence aussi s’il n’accède plus au stockage et que Pacemaker ou Corosync indiquent eux aussi une anomalie. Ce comportement défensif permet de s’assurer qu’aucun ordre de self-fencing ne peut se perdre.
Grâce au SBD, le cluster est assuré que le nœud distant peut effectuer son self-fencing soit par perte de son accès au disque partagé, soit par réception du poison pill, soit à cause d’une anomalie qui a empêché le clusterware d’assumer le ré-armement du watchdog.
Un exemple détaillé de mise en œuvre avec sdb
est
disponible dans
la documentation de Suse.
Pacemaker supporte ce type d’architecture. Patroni ne supporte pas SBD mais a un comportement similaire vis-à-vis du DCS. D’une part les nœuds Patroni s’échangent des messages au travers du DCS. De plus, Patroni doit attendre l’expiration du verrou leader avant de pouvoir effectuer une bascule, ce qui est similaire au temps de réaction d’une architecture SBD. Mais surtout, l’instance PostgreSQL est déchue en cas de perte de communication avec le DCS, tout le serveur peut même être éteint si le watchdog est actif et que l’opération est trop longue.
Le fencing seul est suffisant pour mettre en œuvre un cluster fiable, même avec deux nœuds. Sans quorum, il est néanmoins nécessaire de désactiver le service au démarrage du cluster, afin d’éviter qu’un nœud isolé ne redémarre ses ressources locales sans l’aval du reste du cluster.
Notez que plusieurs algorithmes existent pour résoudre ce cas, hors
quorum, (par exemple les paramètres two_node
,
wait_for_all
et d’autres de Corosync).
Néanmoins, dans le cadre de PostgreSQL, il n’est jamais très prudent de laisser une ancienne instance primaire au sein d’un cluster sans validation préliminaire de son état. Nous conseillons donc toujours de désactiver le service au démarrage, quelle que soit la configuration du cluster.
L’utilisation d’un SBD est une alternative intéressante et fiable pour la création d’un cluster à deux nœuds sans fencing actif. Le stockage y joue un peu le rôle du tiers au sein du cluster pour départager quel nœud conserve les ressources en cas de partition réseau. Le seul défaut de SBD par rapport au fencing est le temps d’attente supplémentaire avant de pouvoir considérer que le nœud distant est bien hors service. Attention aussi au stockage partagé sur le même réseau que le cluster. En cas d’incident réseau généralisé, comme chaque machine perd son accès au disque ET aux autres machines via Pacemaker, toutes vont s’éteindre.
Une autre architecture possible est le cumul d’un quorum et du
watchdog. Avec une telle configuration, en cas de partition
réseau, la partition détenant le quorum attend alors la durée théorique
du watchdog (plus une marge) avant de démarrer les ressources
perdues. Théoriquement, les nœuds de la partition du cluster perdue sont
alors soit redémarrés par leur watchdog, soit sains et ont pu
arrêter les ressources normalement. Ce type d’architecture nécessite à
minima trois nœuds dans le cluster, ou de mettre en place un nœud
témoin, utilisé dans le cadre du quorum uniquement (par exemple
corosync QNetd
).
Le cluster idéal cumule les avantages du fencing, du quorum et des watchdogs.
Comme nous l’avons vu, Pacemaker dispose de toutes les solutions
connues. Reste à trouver la bonne combinaison en fonction des
contraintes de l’architecture. Patroni, quant à lui, a une architecture
similaire au SBD, mais ne force pas à utiliser le watchdog sur
les nœuds. Pour avoir une architecture aussi fiable que possible, il est
recommandé de toujours activer le watchdog sur tous les nœuds,
au strict minima via softdog
.
L’ajout d’un mécanisme de bascule automatique implique quelques contraintes qu’il est important de prendre en compte lors de la prise de décision.
En premier lieu, l’automate chargé d’effectuer la bascule automatique a tout pouvoir sur vos instances PostgreSQL. Toute opération concernant vos instances de près ou de loin doit passer par lui. Il est vital que toutes les équipes soient informées de sa présence afin que toute intervention pouvant impacter le service en tienne compte (mise à jour SAN, coupure, réseau, mise à jour applicative, etc).
Ensuite, il est essentiel de construire une architecture aussi simple que possible.La complexification multiplie les chances de défaillance ou d’erreur humaine. Il est fréquent d’observer plus d’erreurs humaines sur un cluster complexe que sur une architecture sans bascule automatique !
Pour pallier ces erreurs humaines, la formation d’une équipe est vitale. La connaissance concernant le cluster doit être partagée par plusieurs personnes afin de toujours être en capacité d’agir en cas d’incident. Notez que même si la bascule automatique fonctionne convenablement, il est fréquent de devoir intervenir dessus dans un second temps afin de revenir à un état nominal (en reconstruisant un nœud, par exemple).
À ce propos, la documentation de l’ensemble des procédures est essentielle. En cas de maintenance planifiée ou d’incident, il faut être capable de réagir vite avec le moins d’improvisation possible. Quelle que soit la solution choisie, assurez-vous d’allouer suffisamment de temps au projet pour expérimenter, tester le cluster et le documenter.
Patroni dispose de plusieurs mécanismes pour reconstruire les instances de manière automatique :
pg_rewind
: qui permet de ramener une instance ayant
divergé à un point précédent la promotion.pg_rewind
échoue ou qu’une instance reste bloquée
sur une ancienne timeline. Cette suppression va provoquer une
réinitialisation de l’instance. Elle empêche l’utilisation d’une
restauration par delta, ce qui peut provoquer une longue indisponibilité
sur des volumétries importantes.La perspective d’un mécanisme entièrement automatisé qui bascule et reconstruit les instances automatiquement peut être séduisante. Cependant, nous ne préconisons pas l’automatisation de la remise en état du cluster après un failover ayant provoqué une divergence. En effet, ce genre de pratique risque de supprimer des éléments nécessaires à l’analyse du problème et peut empêcher de capitaliser sur les leçons de l’incident. De plus, en cas de problème, cela peut donner lieu à un sur-incident.
Distributed Control System↩︎