Find file
Fetching contributors…
Cannot retrieve contributors at this time
552 lines (471 sloc) 21.5 KB
<?xml version="1.0" encoding="UTF-8"?>
<!-- Dernière modification
le $Date$
par $Author$
révision $Revision$ -->
<sect1 id="failover">
<title>Effectuer une bascule d'urgence avec &slony1;</title>
<indexterm>
<primary>bascule d'urgence</primary>
<secondary>reprise sur panne</secondary>
</indexterm>
<sect2>
<title>Avant-propos</title>
<para>
&slony1; est un système de réplication asynchrone. À cause de cela, il est
presque certain qu'au moment où le n&oelig;ud origine d'un ensemble de
réplication tombe en panne, la dernière transaction <quote>validée</quote>
sur l'origine ne soit pas encore propagée aux abonnés. Les systèmes qui
tombent en panne sont souvent soumis à une forte charge&nbsp;; c'est un
des corollaires de la loi de Murphy. Ainsi le but principal est
d'<emphasis>éviter</emphasis> que le serveur principal tombe en panne. La
meilleure façon d'éviter cela est d'effectuer une maintenance fréquente.
</para>
<para>
Ouvrir le capot d'un serveur à chaud n'est pas ce qu'on peut appeler une
façon <quote>professionelle</quote> d'assurer la maintenance d'un système.
En général, les utilisateurs qui ont besoin de réplication pour leur
sauvegarde ou leur plan de reprise sur panne, ont également des critères
très stricts en matière de <quote>temps d'arrêt du système</quote>. Pour
répondre à ces critères, &slony1; ne se contente pas de fournir des méthodes
de reprise sur panne, il intègre également la notion de transfert d'origine.
</para>
<para>
On suppose dans cette partie que le lecteur est familier avec l'utilitaire
<xref linkend="slonik"/> et qu'il sait comment mettre en place un système de
réplication &slony1; composé de deux n&oelig;uds.
</para>
</sect2>
<sect2>
<title>Bascule contrôlée</title>
<indexterm><primary>bascule contrôlée</primary></indexterm>
<para>
Imaginons un n&oelig;ud <quote>origine</quote>, appelé n&oelig;ud1, avec un
<quote>abonné</quote> appelé n&oelig;ud2 (l'esclave). Une application web,
placée sur un troisième serveur, accède à la base de données sur n&oelig;ud1.
Les deux bases sont actives et fonctionnelles, la réplication est est à peu
près synchronisée. On contrôle la bascule avec la commande <xref
linkend="stmtmoveset"/>.
</para>
<itemizedlist>
<listitem>
<para>
Au moment où nous écrivons ces lignes, basculer vers un autre serveur
nécessite que l'application se reconnecte à la nouvelle base de donnée.
Donc, pour éviter toute complication, nous éteignons le serveur web. Les
utilisateurs qui ont installé <application>pgpool</application> pour
gérer les connexions peuvent simplement éteindre le pool.
</para>
<para>
Les actions à mener dépendent beaucoup de la configuration des
applications qui utilisent la base de données. En général, les
applications qui étaient connectées à l'ancienne base doivent détruire
leurs connexions et en établir de nouvelles vers la base qui vient d'être
promue dans le rôle du <quote>/maître/</quote>. Il y a différentes façons
de configurer cela, et donc différentes façon d'effectuer la bascule&nbsp;:
<itemizedlist>
<listitem>
<para>
L'application stocke le nom de la base de donnée dans un fichier.
</para>
<para>
Dans ce cas, la reconfiguration nécessite de changer la valeur dans
ce fichier, d'arrêter puis de relancer l'application pour qu'elle
pointe vers le nouvel emplacement des données.
</para>
</listitem>
<listitem>
<para>
Une utilisation pertinente de DNS consiste à créer des <ulink
url="http://www.iana.org/assignments/dns-parameters">champs DNS</ulink>
CNAME qui permettent à l'application d'utiliser un nom pour
référencer le n&oelig;ud qui joue le rôle du n&oelig;ud
<quote>maître</quote>.
</para>
<para>
Dans ce cas, la reconfiguration nécessite de changer le CNAME pour
pointer vers le nouveau serveur. De plus, il faut probablement
relancer l'application pour rafraîchir les connexions à la base.
</para>
</listitem>
<listitem>
<para>
Si vous utilisez <application>pgpool</application> ou un
<quote>gestionnaire de connexions</quote> similaire, alors la
reconfiguration implique de modifier le paramètrage de cet outil,
à part cela la procédure est similaire à l'exemple DNS/CNAME
ci-dessus.
</para>
</listitem>
</itemizedlist>
</para>
<para>
La nécessité de redémarrer l'application qui se connecte à la base dépend
de la manière dont elle a été conçue et des mécanismes de gestion d'erreurs
de connexion qui ont été implémentés&nbsp;; si à la suite d'une erreur
elle tente d'ouvrir à nouveau une connexion, alors il n'est pas nécessaire
de la relancer.
</para>
</listitem>
<listitem>
<para>
Un petit script <xref linkend="slonik"/> exécute les commandes
suivantes&nbsp;:
<programlisting>lock set (id = 1, origin = 1);
wait for event (origin = 1, confirmed = 2);
move set (id = 1, old origin = 1, new origin = 2);
wait for event (origin = 1, confirmed = 2);</programlisting>
</para>
<para>
Après ces commandes, l'origine (rôle du maître) de l'ensemble de
réplication 1 est transféré sur le n&oelig;ud 2. En fait, elle n'est
pas simplement transférée&nbsp;; le n&oelig;ud1 devient un abonné
parfaitement synchronisé et actif. Autrement dit, les deux n&oelig;uds
ont échangé leurs rôles respectifs.
</para>
</listitem>
<listitem>
<para>
Après la reconfiguration de l'application web (ou de
<application><link linkend="pgpool">pgpool</link></application>) pour
qu'elle se connecte à la base du n&oelig;ud 2, le serveur web est
redémarré et reprend son activité normale.
</para>
<para>
Lorsqu'on utilise un script shell, pour stopper l'application, lancer le
script <application>slonik</application>, déplacer les fichiers de
configuration et relancer l'ensemble, toute la procédure prend en général
moins de 10 secondes.
</para>
</listitem>
</itemizedlist>
<para>
Vous pouvez désormais éteindre le serveur qui héberge le n&oelig;ud 1 et
effectuer les opérations de maintenance requise. Lorsque le démon <xref
linkend="slon"/> du n&oelig;ud 1 est redémarré, il reprend la réplication,
et rattrape son retard. Une fois synchronisé, on peut exécuter la procédure
à nouveau pour restaurer la configuration originale.
</para>
<para>
Ceci est la meilleure méthode pour ce genre d'opération de maintenance&nbsp;;
elle s'effectue rapidement, sous le contrôle d'un administrateur, et elle
n'implique aucune perte de données.
</para>
<para>
Après avoir réalisé les modifications dans la configuration, vous devriez,
comme <xref linkend="bestpractices"/>, exécuter les scripts &lteststate;
dans l'ordre pour valider que l'état du cluster reste bon.
</para>
</sect2>
<sect2>
<title>Bascule d'urgence</title>
<indexterm><primary>bascule suite à une panne du système</primary></indexterm>
<para>
Lorsque de graves problèmes apparaissent sur le serveur <quote>origine</quote>,
il est parfois nécessaire d'effectuer une bascule (<xref
linkend="stmtfailover"/>) vers le serveur de sauvegarde. Ce cas de figure n'a
rien de souhaitable car les transactions <quote>validées</quote> sur le
serveur mais pas sur les abonnés, seront perdues. Certaines de ces
transactions auront peut-être été annoncées à l'utilisateur final comme
<quote>validées</quote>. En conséquence, les bascules d'urgence doivent être
considérées comme un <emphasis>dernier recours</emphasis>. Si le serveur
origine qui subit <quote>l'avarie</quote> peut être maintenu assez longtemps,
il est <emphasis>nettement</emphasis> préférable d'effectuer une bascule
contrôlée.
</para>
<para>
&slony1; ne fournit pas de moyen pour détecter les pannes du système.
Abandonner des transactions <quote>validées</quote> est une décision
commerciale qui ne peut pas être prise par un système de gestion de base de
données. Si vous voulez placer les commandes ci-dessous dans un script exécuté
automatiquement par un système de surveillance, ce sont
<emphasis>vos</emphasis> données, et <emphasis>votre</emphasis> politique de
bascule d'urgence.
</para>
<itemizedlist>
<listitem>
<para>
La commande <xref linkend="slonik"/>
<programlisting>failover (id = 1, backup node = 2);</programlisting>
ordonne au n&oelig;ud 2 de se considérer comme le propriétaire
(l'origine) de tous les ensembles que le n&oelig;ud 1 possédait. S'il
existe des n&oelig;uds supplémentaires dans le cluster &slony1;, tous
les n&oelig;uds abonnés au n&oelig;ud 1 sont avertis du changement.
<application>Slonik</application> va aussi envoyer une requête
à chaque abonné pour déterminer le n&oelig;ud de plus haut niveau de
synchronisation (<emphasis>c'est-à-dire</emphasis> la dernière
transaction <quote>validée</quote>) pour chaque ensemble de réplication.
La configuration sera changée de façon à ce que le n&oelig;ud 2 applique
d'abord ces transactions finales, avant d'autoriser l'accès en écriture
sur les tables.
</para>
<para>
De plus, tous les n&oelig;uds qui étaient abonnés directement au n&oelig;ud
1 considèreront désormais le n&oelig;ud 2 comme leur fournisseur de données
pour cet ensemble de replication. Cela signifie qu'une fois que la commande
de bascule d'urgence est complétée, plus aucun n&oelig;ud du cluster ne
reçoit d'information de la part du n&oelig;ud 1.
</para>
<note>
<para>
Notez que, pour que le n&oelig;ud 2 soit considéré comme un candidat
pour la bascule, il doit avoir été configuré avec l'option
<command>forwarding = yes</command> de <xref linkend="stmtsubscribeset"/>,
ce qui a pour effet que les données du log de réplication sont conservées
dans &sllog1;/&sllog2; du n&oelig;ud 2. Si ces données ne sont
<emphasis>pas</emphasis> conservées, alors la bascule vers ce n&oelig;ud
n'est pas possible.
</para>
</note>
</listitem>
<listitem>
<para>
Reconfigurer et relancer l'application (ou
<application>pgpool</application>) pour qu'elle se reconnecte
au n&oelig;ud 2.
</para>
</listitem>
<listitem>
<para>
Purger le n&oelig;ud abandonné.</para>
<para>
Vous découvrirez, après la bascule, qu'il existe encore beaucoup de
références au n&oelig;ud 1 dans la table <xref linkend="table.sl-node"/>,
ainsi que ses tables associées telle que <xref
linkend="table.sl-confirm"/>&nbsp;; puisque des données sont toujours
présentes dans &sllog1;/&sllog2;, &slony1; ne peut pas
purger immédiatement le n&oelig;ud.
</para>
<para>
Une fois que la bascule est complète et que le n&oelig;ud 2 accepte
les opérations d'écriture sur les tables répliquées, il faut supprimer
toutes les informations de configuration rémanentes avec la commande
<xref linkend="stmtdropnode"/>&nbsp;:
<programlisting>drop node (id = 1, event node = 2);</programlisting>
</para>
<para>
Supposons que la panne résulte d'un problème matériel catastrophique sur
le n&oelig;ud 1, il est possible qu'il ne <quote>reste</quote> plus rien
sur le n&oelig;ud 1. Si la panne n'est pas <quote>totale</quote>, ce qui
est souvent le cas lors d'une coupure réseau, vous découvrirez que le
n&oelig;ud 1 <quote>imagine</quote> toujours que rien n'a changé et qu'il
est dans le même état qu'avant la panne. Reportez-vous à la section <xref
linkend="rebuildnode1"/> pour plus de détails sur ce que cela implique.
</para>
</listitem>
<listitem>
<para>
Après avoir réalisé les modifications dans la configuration, vous devriez,
comme <xref linkend="bestpractices"/>, exécuter les scripts &lteststate;
dans l'ordre pour valider que l'état du cluster reste bon.
</para>
</listitem>
</itemizedlist>
</sect2>
<sect2 id="complexfailover">
<title>Bascule avec un ensemble de n&oelig;uds complexe</title>
<para>
La bascule est assez <quote>simple</quote> s'il n'y a que deux
n&oelig;uds&nbsp;; si un cluster &slony1; comprends de nombreux n&oelig;uds,
exécuter une bascule propre demande une planification et une exécution très
attentives aux détails.
</para>
<para>
Considérez le diagramme suivant décrivant un ensemble de six n&oelig;uds sur
deux sites.
<inlinemediaobject>
<imageobject>
<imagedata fileref="complexenv.png"/>
</imageobject>
<textobject>
<phrase>Multisites symétriques</phrase>
</textobject>
</inlinemediaobject>
</para>
<para>
Supposons que les n&oelig;uds 1, 2 et 3 résident à un point central et que
nous ayons besoin d'effectuer une bascule à cause d'un problème sur ce site
complet. Les causes peuvent aller d'une perte persistente de communications
à la destruction physique de ce site. La cause n'a pas d'importance en soi
car ce qui nous intéresse est de savoir comment réaluser une bascule propre
de &slony1; vers le nouveau site.
</para>
<para>
Nous supposerons maintenant que le n&oelig;ud 5 doit devenir la nouvelle
origine après bascule.
</para>
<para>
La séquence de reconfiguration de &slony1; requiert d'exécuter les étapes
suivantes pour une bascule propre de la configuration des n&oelig;uds&nbsp;:
</para>
<itemizedlist>
<listitem>
<para>
Réabonner chaque n&oelig;ud (en utilisant <xref linkend="stmtsubscribeset"/>
nécessaire à la reformation d'un cluster qui n'est pas encore abonné au
futur fournisseur de données.
</para>
<para>
Dans notre cluster d'exemple, cela signifie que nous voulons réabonner
les n&oelig;uds 4 et 6 , qui doivent tous les deux pointer vers 5.
</para>
<programlisting>include &lt;/tmp/failover-preamble.slonik&gt;;
subscribe set (id = 1, provider = 5, receiver = 4);
subscribe set (id = 1, provider = 5, receiver = 4);</programlisting>
</listitem>
<listitem>
<para>
Supprimer tous les n&oelig;uds inutiles, en commençant par les derniers
esclaves.
</para>
<para>
Comme les n&oelig;uds 1, 2 et 3 sont inaccessibles, nous devons indiquer
<envar>EVENT NODE</envar> pour que chaque événement atteigne les portions
toujours vivantes du cluster.
</para>
<programlisting>include &lt;/tmp/failover-preamble.slonik&gt;;
drop node (id=2, event node = 4);
drop node (id=3, event node = 4);</programlisting>
</listitem>
<listitem>
<para>
Maintenant exécuter la bascule avec <command>FAILOVER</command>.
</para>
<programlisting>include &lt;/tmp/failover-preamble.slonik&gt;;
failover (id = 1, backup node = 5);</programlisting>
</listitem>
<listitem>
<para>
Enfin, supprimez l'ancienne origine du cluster.
</para>
<programlisting>include &lt;/tmp/failover-preamble.slonik&gt;;
drop node (id=1, event node = 4);</programlisting>
</listitem>
</itemizedlist>
</sect2>
<sect2>
<title>Automatisation de la commande <command>FAIL OVER</command></title>
<indexterm><primary>automatisation des bascules d'urgence</primary></indexterm>
<para>
Si vous choisissez d'automatiser la commande <command>FAIL OVER </command>,
il est important de le faire <emphasis>avec soin</emphasis>. Vous devez être
sur que le n&oelig;ud en panne est réellement en panne, et vous devez être
capable de vous assurer que le n&oelig;ud en panne ne redémarre pas, ce qui
entraînerait un conflit entre deux n&oelig;uds capables de jouer le rôle du
<quote>maître</quote>.
</para>
<note>
<para>
Le fait de <quote>tirer une balle dans la tête du serveur en panne</quote>
ne pose pas directement de problème à la réplication ou à &slony1;&nbsp;;
&slony1; supporte cela de manière assez tranquillement car, une fois qu'un
n&oelig;ud est marqué comme étant en panne, les autres n&oelig;uds
<quote>l'oublient</quote> et l'ignorent. Le problème se situe plutôt au
niveau de <emphasis>votre application</emphasis>. Supposons que le
n&oelig;ud en panne soit capable de répondre aux requêtes de votre
application, <emphasis>cela</emphasis> va certainement poser un problème
qui n'a rien à voir avec &slony1;. Le problème est que deux bases de
données sont en mesure de répondre en tant que <quote>maître</quote>.
</para>
</note>
<para>
Lorsqu'une bascule d'urgence est effectuée, il faut un mécanisme pour dégager
de force le n&oelig;ud en panne hors du réseau afin d'éviter toute confusion
au niveau des applications. Cela peut être fait via une interface SNMP qui
effectue une partie des opérations suivantes&nbsp;:
</para>
<itemizedlist>
<listitem>
<para>Éteindre l'alimentation du serveur en panne.</para>
<para>
Si l'on ne fait pas attention, le serveur peut réapparaître dans le
système de réplication si un administrateur le rallume.
</para>
</listitem>
<listitem>
<para>
Modifier les règles du pare-feu ou d'autres configurations pour exclure
du réseau l'adresse IP du serveur en panne.
</para>
<para>
Si le serveur a de multiples interfaces, et donc de multiple adresses IP,
cette approche permet de supprimer/désactiver les adresses utilisées
par l'application, tout en conservant les adresses
<quote>administratives</quote> afin que le serveur reste accessible par
les administrateurs systèmes.
</para>
</listitem>
</itemizedlist>
</sect2>
<sect2 id="rebuildnode1">
<title>Après la bascule d'urgence, reconfiguration de l'ancienne origine</title>
<indexterm><primary>reconstruction après une bascule d'urgence</primary></indexterm>
<para>
Ce qui arrive au n&oelig;ud en panne dépend beaucoup de la nature de la
catastrophe qui a conduit à la bascule d'urgence vers un autre n&oelig;ud.
Si le n&oelig;ud a été abandonné à cause de la destruction physique des
disques de stockage, il n'y a plus grand-chose à faire. D'un autre coté,
si un n&oelig;ud a été abandonné à cause d'une coupure réseau, qui n'a pas
perturbé le fonctionnement normal du n&oelig;ud <quote>fournisseur</quote>.
Toutefois, une fois que les communications sont restaurées, le fait est que
la commande <command>FAIL OVER</command> rend obligatoire l'abandon du
n&oelig;ud qui était en panne.
</para>
<para>
Après ce genre de bascule d'urgence, les données stockées sur le n&oelig;ud 1
seront rapidement et de plus en plus désynchronisées par rapport aux autres
n&oelig;uds. Elles doivent être considérées comme corrompues. Ainsi le seul
moyen pour que le n&oelig;ud 1 retourne dans le cluster de réplication et
qu'il redevienne le n&oelig;ud origine est de le reconstruire à partir de
zéro comme un abonné, de le laisser rattraper son retard, puis d'effectuer
la procédure de bascule contrôlée.
</para>
<para>
Une bonne raison de <emphasis>ne pas</emphasis> faire cela automatiquement
est que d'importantes mises à jour (d'un point de vue
<emphasis>commercial</emphasis>) ont pu être <quote>validées</quote> sur le
système en panne. Vous souhaiterez probablement analyser les dernières
transactions que le n&oelig;ud a réalisé avant de tomber en panne, afin de
voir si certaines doivent être ré-appliquer sur le cluster
<quote>actif</quote>. Par exemple, si quelqu'un réalisait des opérations
bancaires impactant des comptes clients au moment de la panne, il est
souhaitable de ne pas perdre cette information.
</para>
<warning>
<para>
On a observé certains résultats étranges lorsqu'un n&oelig;ud <quote>tombe
en panne</quote> à cause d'une coupure réseau persistante, par opposition
aux pannes du système de stockage. Dans de tels scénarios, le n&oelig;ud
<quote>en panne</quote> dispose d'une base de données en parfait état de
marche&nbsp;; le fait est qu'ayant été coupé des autres n&oelig;uds, il
<quote>crie en silence</quote>.
</para>
<para>
Lorsque la connexion réseau est réparée, ce n&oelig;ud peut réapparaître et
conformément à <emphasis>sa</emphasis> configuration, il va communiquer
avec les autres n&oelig;uds du cluster &slony1;.
</para>
<para>
En <emphasis>fait</emphasis>, la confusion se trouve uniquement sur ce
n&oelig;ud. Les autres n&oelig;ud du cluster ne sont pas du tout
perturbés&nbsp;; ils savent que ce n&oelig;ud est <quote>mort</quote> et
qu'ils doivent l'ignorer. Mais il est impossible de savoir cela en
regardant le n&oelig;ud qui était <quote>en panne</quote>.
</para>
<para>
Ceci nous ramène au fait que &slony1; n'est pas un outil de surveillance de
réseau. Vous devez avoir des méthodes claires pour signaler aux
applications et aux utilisateurs les bases de données à utiliser. En
l'absence de telles méthodes, la réplication ne fera qu'empirer le potentiel
de confusion, et les bascules d'urgence seront un énorme potentiel de
confusion.
</para>
</warning>
<para>
Si la base de données est très volumineuse, la construction du n&oelig;ud 1
peut prendre plusieurs heures&nbsp;; ceci est une autre raison de considérer
les bascules d'urgence comme un <quote>dernier recours</quote> non
souhaitable.
</para>
</sect2>
</sect1>