Dans les précédents articles sur le thème clusters/Pacemaker, nous avons aperçu rapidement une solution qui permet de regrouper les ressources sur un seul et même nœud (une VIP et son service par exemple) : l’utilisation de la commande group. J’avais alors évoqué la problématique liée à son utilisation et le fait qu’il était parfois préférable d’utiliser une solution à base de colocation/order. Le but étant d’éviter l’arrêt en cascade des ressources groupées. Je vais essayer ici de détailler la problématique et d’y apporter une solution.

Pourquoi grouper les ressources

Si vous ne groupez pas vos ressources, elles vont être lancées sur des nœuds différents (c’est le fonctionnement par défaut de Pacemaker qui va chercher à optimiser votre cluster). Si je reprends l’exemple de la VIP et de son service et que je ne groupe pas les ressources, il y a de fortes chances pour que la VIP se retrouve sur un nœud et le service sur l’autre. C’est ce qu’il se passe avec cet exemple : je déclare deux ressources VIP et SERVICE (deux ressources qui ne font rien, c’est juste pour tester le comportement du cluster).

La configuration :

property no-quorum-policy="ignore"
property stonith-enabled="false"
primitive VIP ocf:heartbeat:Dummy
primitive SERVICE ocf:heartbeat:Dummy

On obtient ce résultat visible via crm_mon :

============
Last updated: Wed Apr 17 11:15:25 2012
Stack: openais
Current DC: test-ha1 - partition with quorum
Version: 1.0.9
2 Nodes configured, 2 expected votes
2 Resources configured.
============

Online: [ test-ha1 test-ha2 ]

VIP       (ocf::heartbeat:Dummy): Started test-ha1
SERVICE   (ocf::heartbeat:Dummy): Started test-ha2

La VIP et le SERVICE sont sur deux nœuds différents. Pour corriger ce comportement, on peut :

  • Utiliser la commande group : les ressources doivent fonctionner ensemble.
  • Utiliser des contraintes sur les ressources avec colocation et order : on lie une ressource à une autre.

Grouper les ressources

Une première solution est de grouper les ressources avec la commande group (attention l’ordre des ressources est important) :

group NOM_DU_GROUPE VIP SERVICE

Quand je groupe des ressources, ici la VIP et le SERVICE, je signifie que :

  • La VIP et le SERVICE doivent fonctionner ensemble sur le même nœud.
  • Dans cet exemple la VIP doit démarrer avant le SERVICE.
  • Le SERVICE à besoin de la VIP pour fonctionner.
  • Si la VIP est arrêtée (volontairement ou pas), le SERVICE s’arrête.
  • L’inverse n’est pas vrai : la VIP peut fonctionner sans le SERVICE.

Je groupe ma VIP et mon SERVICE :

crm(live)configure# group MON_SERVICE_HA VIP SERVICE
crm(live)configure# commit

Et j’obtiens dans mon crm_mon :

============
Last updated: Wed Apr 17 12:52:28 2012
Stack: openais
Current DC: test-ha1 - partition with quorum
Version: 1.0.9
2 Nodes configured, 2 expected votes
1 Resources configured.
============

Online: [ test-ha1 test-ha2 ]

 Resource Group: MON_SERVICE_HA
     VIP        (ocf::heartbeat:Dummy): Started test-ha1
     SERVICE    (ocf::heartbeat:Dummy): Started test-ha1

J’ai le comportement recherché. Les deux ressources fonctionnent sur le même noeud. On va tester l’arrêt du SERVICE, la VIP ne doit pas s’arrêter :

root@test-ha1:~# crm resource stop SERVICE
root@test-ha1:~# crm_mon -1
============
Last updated: Wed Apr 17 12:56:47 2012
Stack: openais
Current DC: test-ha1 - partition with quorum
Version: 1.0.9
2 Nodes configured, 2 expected votes
1 Resources configured.
============

Online: [ test-ha1 test-ha2 ]

 Resource Group: MON_SERVICE_HA
     VIP	(ocf::heartbeat:Dummy):	Started test-ha1
     SERVICE	(ocf::heartbeat:Dummy):	Stopped

La VIP fonctionne toujours… Je redémarre le SERVICE et je coupe cette fois la VIP :

root@test-ha1:~# crm resource stop VIP
root@test-ha1:~# crm_mon -1
============
Last updated: Wed Apr 17 12:57:54 2012
Stack: openais
Current DC: test-ha1 - partition with quorum
Version: 1.0.9
2 Nodes configured, 2 expected votes
1 Resources configured.
============

Online: [ test-ha1 test-ha2 ]

Tout a été arrêté. En effet, le SERVICE a besoin de la VIP pour fonctionner.

Pour illustrer ce problème, je reprends ma configuration précédente et je rajoute une VIP :

crm(live)configure# property no-quorum-policy="ignore"
crm(live)configure# property stonith-enabled="false"
crm(live)configure# primitive VIP1 ocf:heartbeat:Dummy
crm(live)configure# primitive VIP2 ocf:heartbeat:Dummy
crm(live)configure# primitive SERVICE ocf:heartbeat:Dummy
crm(live)configure# group MON_SERVICE_HA VIP1 VIP2 SERVICE
crm(live)configure# commit

La sortie du crm_mon :

============
Last updated: Wed Apr 17 13:10:55 2012
Stack: openais
Current DC: test-ha1 - partition with quorum
Version: 1.0.9
2 Nodes configured, 2 expected votes
1 Resources configured.
============

Online: [ test-ha1 test-ha2 ]

 Resource Group: MON_SERVICE_HA
     VIP1       (ocf::heartbeat:Dummy): Started test-ha1
     VIP2       (ocf::heartbeat:Dummy): Started test-ha1
     SERVICE    (ocf::heartbeat:Dummy): Started test-ha1

J’ai une intervention à faire sur la VIP2. Je souhaite l’arrêter et dans l’idéal ne pas impacter mon SERVICE qui utilise aussi la VIP1 :

root@test-ha1:~# crm resource stop VIP2

root@test-ha1:~# crm_mon -1
============
Last updated: Wed Apr 17 13:12:49 2012
Stack: openais
Current DC: test-ha1 - partition with quorum
Version: 1.0.9
2 Nodes configured, 2 expected votes
1 Resources configured.
============

Online: [ test-ha1 test-ha2 ]

 Resource Group: MON_SERVICE_HA
     VIP1	(ocf::heartbeat:Dummy):	Started test-ha1
     VIP2	(ocf::heartbeat:Dummy):	Stopped
     SERVICE	(ocf::heartbeat:Dummy):	Stopped

Conclusion : ce n’est pas possible avec cette configuration. Elle est à privilégier dans le cas d’un cluster qui utilise des relations simples entre ses ressources. Dans le cas contraire, ici dans mon exemple, il est préférable d’utiliser des contraintes sur les ressources avec colocation.

Lier les ressources avec colocation

L’alternative à la commande group est la contrainte colocation : le principe est le même, on va lier des ressources entre elles. A la différence que nous allons pouvoir ici utiliser la notion de “score” pour décrire plus précisément l’emplacement des ressources entre elles. La syntaxe de la commande :

crm(live)configure# help colocation

This constraint expresses the placement relation between two
or more resources. If there are more than two resources, then the
constraint is called a resource set. Collocation resource sets have
an extra attribute to allow for sets of resources which don't depend
on each other in terms of state. The shell syntax for such sets is
to put resources in parentheses.

Usage:
...............
        colocation <id> <score>: <rsc>[:<role>] <rsc>[:<role>] ...
...............
Example:
...............
        colocation dummy_and_apache -inf: apache dummy
        colocation c1 inf: A ( B C )
...............

Si je reprends mon exemple de configuration avec mes deux VIPs, la configuration devient :

crm(live)configure# property no-quorum-policy="ignore"
crm(live)configure# property stonith-enabled="false"
crm(live)configure# primitive VIP1 ocf:heartbeat:Dummy
crm(live)configure# primitive VIP2 ocf:heartbeat:Dummy
crm(live)configure# primitive SERVICE ocf:heartbeat:Dummy
crm(live)configure# colocation SERVICE-avec-VIP inf: VIP1 VIP2 SERVICE
crm(live)configure# commit

Quelques explications sur la syntaxe de colocation et sur la gestion du score :

  • SERVICE-avec-VIP est l’id de ma contrainte.
  • Le score mandatory va décrire le fonctionnement de notre contrainte : mandatory est équivalent à INFINITY (traduit par inf si vous faites un crm configure show). Cela signifie que vos ressources fonctionneront toujours ensemble. A l’inverse, si j’utilise le mot clé -INFINITY, les ressources ne seront jamais ensemble. Le mot clé advisory est lui équivalent à un score de 0 et peut se traduire par les ressources devraient fonctionner ensemble. Ce cas-là sera moins utilisé en tout cas pas seul et fera intervenir d’autres contraintes ou éléments de la configuration.
  • Enfin la liste des ressources à lier… Encore une fois l’ordre est important. Dans cet exemple, je me retrouve avec le même fonctionnement que précédemment à savoir :
  • Si je coupe VIP1, tout s’arrête.
  • Si je coupe VIP2, SERVICE s’arrête et VIP1 fonctionne toujours.
  • Si je coupe SERVICE, VIP1 et VIP2 fonctionnent toujours.

Arrêt VIP1

root@test-ha1:~# crm resource stop VIP1

root@test-ha1:~# crm_mon -1
============
Last updated: Thu Apr 18 15:23:35 2012
Stack: openais
Current DC: test-ha1 - partition with quorum
Version: 1.0.9
2 Nodes configured, 2 expected votes
3 Resources configured.
============

Online: [ test-ha1 test-ha2 ]

Arrêt VIP2

root@test-ha1:~# crm resource stop VIP2

root@test-ha1:~# crm_mon -1
============
Last updated: Thu Apr 18 15:24:55 2012
Stack: openais
Current DC: test-ha1 - partition with quorum
Version: 1.0.9
2 Nodes configured, 2 expected votes
3 Resources configured.
============

Online: [ test-ha1 test-ha2 ]

 VIP1	(ocf::heartbeat:Dummy):	Started test-ha1

Arrêt SERVICE

root@test-ha1:~# crm resource stop SERVICE

root@test-ha1:~# crm_mon -1
============
Last updated: Thu Apr 18 15:25:22 2012
Stack: openais
Current DC: test-ha1 - partition with quorum
Version: 1.0.9
2 Nodes configured, 2 expected votes
3 Resources configured.
============

Online: [ test-ha1 test-ha2 ]

 VIP1	(ocf::heartbeat:Dummy):	Started test-ha1
 VIP2	(ocf::heartbeat:Dummy):	Started test-ha1

Pour y parvenir nous utilisons les parenthèses pour indiquer les ressources qui n’ont pas de dépendances entre elles. Exemple :

colocation SERVICE-avec-VIP inf: ( VIP1 VIP2 ) SERVICE

Je précise donc que :

  • Je peux couper indépendamment la VIP1 et/ou la VIP2 sans impacter le SERVICE
  • Si je coupe le SERVICE, les deux VIPs s’arrêtent.

Arrêt VIP1

root@test-ha1:~# crm resource stop  VIP1

root@test-ha1:~# crm_mon -1
============
Last updated: Thu Apr 18 15:36:14 2012
Stack: openais
Current DC: test-ha1 - partition with quorum
Version: 1.0.9
2 Nodes configured, 2 expected votes
3 Resources configured.
============

Online: [ test-ha1 test-ha2 ]

VIP2    (ocf::heartbeat:Dummy): Started test-ha1
SERVICE (ocf::heartbeat:Dummy): Started test-ha1

Arrêt VIP2

root@test-ha1:~# crm resource stop  VIP2

root@test-ha1:~# crm_mon -1
============
Last updated: Thu Apr 18 15:36:45 2012
Stack: openais
Current DC: test-ha1 - partition with quorum
Version: 1.0.9
2 Nodes configured, 2 expected votes
3 Resources configured.
============

Online: [ test-ha1 test-ha2 ]

VIP1    (ocf::heartbeat:Dummy): Started test-ha1
SERVICE (ocf::heartbeat:Dummy): Started test-ha1

Arrêt SERVICE

root@test-ha1:~# crm resource stop  SERVICE

root@test-ha1:~# crm_mon -1
============
Last updated: Thu Apr 18 15:37:36 2012
Stack: openais
Current DC: test-ha1 - partition with quorum
Version: 1.0.9
2 Nodes configured, 2 expected votes
3 Resources configured.
============

Online: [ test-ha1 test-ha2 ]

Donner un ordre de démarrage aux ressources

Pour s’assurer qu’un service démarre avant un autre, il faut utiliser la contrainte order. Comme nous l’avons vu précédemment, contrairement à la commande group, la contrainte colocation ne définit par d’ordre de démarrage (et d’arrêt) de vos ressources. Je reprends l’exemple précédent du SERVICE qui a besoin de ses deux VIPs pour fonctionner et je rajoute qu’il faut si possible (la nuance est importante) démarrer les VIPs avant le SERVICE :

order SERVICE-apres-VIPs advisory: ( VIP1 VIP2 ) SERVICE

La syntaxe est semblable à celle de colocation modulo l’interprétation des parenthèses : dans cet exemple, on indique que VIP1 et VIP2 peuvent démarrer en parallèle. En résumé :

  • Je rajoute une contrainte order avec “SERVICE-apres-VIPs” comme id.
  • Le SERVICE démarrera, de préférence, après les VIP1 et VIP2.
  • Les VIP1 et VIP2 peuvent démarrer en parallèle.

Avec un score infini, on indique que la VIP doit absolument démarrer avant le SERVICE : très bien c’est ce qu’on cherche. Mais cela implique que le SERVICE devra être arrêté avant de couper la VIP. On retombe sur un des problèmes précédents que l’on cherche à éviter.

Avec un score mandatory (0), Pacemaker essaye de démarrer la VIP avant le SERVICE et de stopper la VIP après le SERVICE à condition de ne pas impacter la ressource suivante dans la contrainte définie. Clairement, en cas d’arrêt d’une des VIPs, il ne coupera pas le SERVICE.

Pour finir, la configuration finale de ce cluster :

crm(live)configure# property no-quorum-policy="ignore"
crm(live)configure# property stonith-enabled="false"
crm(live)configure# primitive VIP1 ocf:heartbeat:Dummy
crm(live)configure# primitive VIP2 ocf:heartbeat:Dummy
crm(live)configure# primitive SERVICE ocf:heartbeat:Dummy
crm(live)configure# colocation SERVICE-avec-VIP inf: ( VIP1 VIP2 ) SERVICE
crm(live)configure# order SERVICE-apres-VIP 0: ( VIP1 VIP2 ) SERVICE
crm(live)configure# commit

Je vérifie l’ordre effectif de démarrage des mes ressources :

root@test-ha1:~# grep "Initiating action" /var/log/daemon.log
Apr 19 14:01:55 test-ha1 crmd: [789]: info: te_rsc_command: Initiating action 13: start VIP1_start_0 on test-ha1 (local)
Apr 19 14:01:55 test-ha1 crmd: [789]: info: te_rsc_command: Initiating action 14: start VIP2_start_0 on test-ha1 (local)
Apr 19 14:01:55 test-ha1 crmd: [789]: info: te_rsc_command: Initiating action 15: start SERVICE_start_0 on test-ha1 (local)

Si je coupe la VIP2 par exemple, je ne dois pas impacter mon SERVICE :

root@test-ha1:~# crm resource stop VIP2

root@test-ha1:~# crm_mon -1
============
Last updated: Mon Apr 19 14:08:57 2012
Stack: openais
Current DC: test-ha1 - partition with quorum
Version: 1.0.9
2 Nodes configured, 2 expected votes
3 Resources configured.
============

Online: [ test-ha1 test-ha2 ]

 VIP1	  (ocf::heartbeat:Dummy):	Started test-ha1
 SERVICE  (ocf::heartbeat:Dummy):	Started test-ha1

Quand je relance la VIP2, rien n’est impacté…

Si je coupe le SERVICE, dans ce cas là, tout s’arrête :

root@test-ha1:~# crm resource stop SERVICE

root@test-ha1:~# crm_mon -1
============
Last updated: Mon Apr 19 14:11:42 2012
Stack: openais
Current DC: test-ha1 - partition with quorum
Version: 1.0.9
2 Nodes configured, 2 expected votes
3 Resources configured.
============

Online: [ test-ha1 test-ha2 ]

Conclusion

J’espère avoir éclairci un peu ces notions… On peut essayer de résumer en se disant qu’il est préférable d’utiliser le couple colocation/order à la place de la commande group. Une utilisation généraliste dans le cas d’un SERVICE et de ses n VIPs serait :

colocation SERVICE-avec-VIP inf: ( VIP1 VIP2 VIPn) SERVICE
order SERVICE-apres-VIP 0: ( VIP1 VIP2 VIPn) SERVICE

Il faudra se poser la question de qui doit démarrer avant qui mais également l’impact que peut avoir l’arrêt puis la relance d’une ressource sur la ressource qui suit dans la contrainte de colocation/order. Dans l’exemple de cet article, il faudrait se demander comment se comportera le SERVICE qui a été démarré avec n VIPs si on en coupe une ? Le SERVICE devient HS (il a perdu une VIP sur laquelle il est en écoute) ? Et quand la VIP est redémarrée, le SERVICE se remet en écoute sur cette adresse ?

Bons tests !