+++ title = "Ansible et la hiérarchie des groupes" date = 2022-11-28 [taxonomies] tags = [ "ansible", "système" ] +++ > Le mauvais groupe, il est hiérarchique… le bon groupe… il est hiérarchique, mais c’est pas pareil. Le bon programmeur, apparemment… Je suis récemment tombé sur une petite particularité (certains diront un *quirk*) d’Ansible qui m’a paru suffisamment intéressant pour tenter de l’expliquer, ou au moins d’en expliquer ma compréhension. # Et là, la marmotte d’Ansible, elle met le chocolat de l’héritage des groupes dans le papier du `group_vars` Notre histoire commence un beau matin de printemps avec un playbook Ansible de bon alois qui se baladait innocemment dans la forêt enchantée du système d’information. Ce playbook contenait de nombreuses variables et de nombreux hôtes. Toutes ces variables et ces hôtes formaient une hiérarchie complexe où beaucoup de variables se surchargeaient les unes les autres dans un ballet ininterrompu et franchement difficilement compréhensible. Du point de vue du contrôleur (donc la machine qui fait tourner Ansible), les groupes en question servent essentiellement à organiser de manière logique les choses. Mais du point de vue de la machine que se passe-t-il ? Et du point de vue de l’hôte, comment c’est perçu ? Tu vas pouvoir constater que la réponse n’est pas si simple que ça. # Vu de l’hôte, les groupes, on s’en tape Du point de vue l’hôte (donc la machine sur laquelle se connecte le contrôleur Ansible), les groupes n’ont que peu d’importance : ils sont vus ni plus ni moins que comme des tags et rien de plus. D’ailleurs, à ma connaissance, ils ne sont accessibles qu’à travers la variable Ansible `group_names` qui contient simplement une liste, à plat donc, de tous les groupes auquel appartient l’hôte. On peut très facilement récupèrer cette liste comme suit : ``` --- - hosts: all gather_facts: false tasks: - name: tamerelol debug: msg: "{{ group_names }}" ``` Ce qui donne ce résultat : ``` ok: [HP-elite-book-cuisine.buttse.cx] => { "msg": [ "borg_client", "borgbackup", "webservers", "disabled_syslog" ] } ``` Cela peut être pratique dans certains cas : * mettre à jour seulement les machines dans le groupe X * exécuter une commande sur l’ensemble des machines dans le groupe Y * filtrer dans un template Jinja2 Mais le fait est : du point de vue de l’hôte, c’est une inforamtion plate et absolument pas hiérarchisée. **Donc si quelqu’un vous dit que les groupes Ansible sont juste des tags**, il a basiquement raison, mais seulement du point de vue de l’hôte. Le contrôlleur, c’est une toute autre histoire… # Vu du contrôleur, les groupes, c’est la vie Du point de vue de contrôleur en revanche, les groupes ont une très grande importance : c’est eux qui déterminent la façon dont les variables vont être déterminées et appliquées aux hôtes. En gros, du point de vue des `group_vars` de l’inventaire (et seulement de l’inventaire, je vous rappelle qu’il y a d’autres priorités dans Ansible, en particulier concernant les variables d’hôtes, les `group_vars` des playbooks, j’en passe et des meilleurs, la résolution est la suivante : * on commence par appliquer les variables qui sont dans le `all` (celui-ci a la priorité la plus basse donc) ; * si elles sont surchargées par des variables dans le premier niveau de l’inventaire, ces dernières prennent la priorité (et donc surcharge logiquement le `all`) * si des variables sont définies plus bas dans la hiérarchie des groupes, elles surchargeront logiquement le niveau de leur parent * et seront surchargées par les variables définies au niveau de leurs enfants Il n’y a qu’une petite exception à cette règle, qui peut s’avérer très trompeuse dans certains cas, ce sont les variables qui sont définies au même niveau dans la hiérarchie des groupes. Dans ce cas, Ansible fait une résolution dans l’ordre alphabétique de définition des groupes (donc les groupes en `a*` seront surchargés par les groupes en `b*`, etc…). Concrètement, si vous avez une hiérarchie de groupes qui ressemblent à ça : ``` > ansible-inventory -i production.yml --graph borgbackup @borgbackup: |--@borg_client: | |--macbook-pro-chiottes.buttse.cx | |--HP-elite-book-cuisine.buttse.cx |--@borg_server: | |--meinbackup.buttse.cx ``` Vous pouvez des variables « globales » (au niveau du groupe `borgbackup` donc) et les surcharger éventuellement au niveau du dessous. Petite illustration. Admettons que je définisse une variable `borg_vars` dans `group_vars/borgbackup.yml` comme suit : ``` > cat group_vars/borgbackup.yml --- borg_vars: "je suis le niveau le plus en haut" ``` Et que je définisse la même variable un peu plus « bas » dans `group_vars/borg_client.yml` : ``` > cat group_vars/borg_client.yml --- borg_vars: "je suis le niveau du client et je surcharge le serveur" ``` On va tenter de voir comment c’est défini derrière : ``` > ansible-playbook -u root -i production.yml test.yml PLAY [all] ******************************************************************************************************************** TASK [tamerelol] ************************************************************************************************************** lundi 28 novembre 2022 10:03:20 +0100 (0:00:00.032) 0:00:00.032 ******** ok: [meinbackup.buttse.cx] => { "msg": "je suis le niveau le plus en haut" } ok: [HP-elite-book-cuisine.buttse.cx] => { "msg": "je suis le niveau du client et je surcharge le serveur" } ok: [macbook-pro-chiottes.buttse.cx] => { "msg": "je suis le niveau du client et je surcharge le serveur" } ``` Comme tu peux le constater, la hiérarchie des groupes a bien été respectée pour surcharger la variable `borg_vars`. Évidemment, s’il y avait eu un groupe en dessous de `borg_client`, on aurait aussi pu surchager la variable en question dedans aussi et ça aurait fait à peu près le même effet. Ce dont il faut se méfier le plus, c’est si des serveurs sont définis en même temps sur plusieurs niveaux. Dans ce cas, on revient au cas initiale (résolution dans l’ordre alphabéique). On peut le démontrer assez facilement dans notre cas en ajoutant l’hôte `meinbackup.buttse.cx` directement dans le groupe `borg_client`. En faisant cela, on obtient bien : ``` ok: [meinbackup.buttse.cx] => { "msg": "je suis le niveau du client et je surcharge le serveur" } ``` Parce que, du point de vue d’Ansible, `borg_client` est alphabétiquement avant `borgbackup`. D’ailleurs, `borg_server` étant juste après `borg_client` mais avant `borgbackup`, si j’ajoute cette variable dans `group_vars/borg_server.yml`, elle surcharge bien celle de `group_vars/borg_client.yml`, on constate bien que c’est le cas : ``` > cat group_vars/borg_server.yml --- borg_vars: "normalement, je devrais avoir la priorité pour borg_server" > ansible-playbook -u root -i production.yml test.yml […] TASK [tamerelol] ************************************************************************************************************** lundi 28 novembre 2022 10:03:20 +0100 (0:00:00.032) 0:00:00.032 ******** ok: [meinbackup.buttse.cx] => { "msg": "normalement, je devrais avoir la priorité pour borg_server" } ok: [HP-elite-book-cuisine.buttse.cx] => { "msg": "je suis le niveau du client et je surcharge le serveur" } ok: [macbook-pro-chiottes.buttse.cx] => { "msg": "je suis le niveau du client et je surcharge le serveur" } […] ``` **Donc si quelqu’un vous dit que les groupes Ansible sont effectivement hiérarchiques**, il a basiquement raison, mais seulement du point de vue du contrôleur et de la définition de la précédence des variables. # Conclusage C’est un beau bordel mine de rien ces histoires de variables Ansible. Déjà que la précédence n’est pas toujours simple (je vous mets au défi de me dire de tête qui est plus prioritaire entre une variable d’inventaire, une variable de playbook et une variable de groupes), Ansible en rajoute une couche avec cette histoire de précédence dans les hiérarchies de groupes et de précédence dans les noms de groupe, histoire de bien simplifier les choses pour le quidam moyen. Bref, j’espère que ça t’aura permis de mieux comprendre la chose et de ne pas te faire surprendre. Et surtout, on n’oublie pas : quand deux personnes affirment des choses contradictoires, elles ne parlent peut-être simplement pas de la même chose ou du même point de vue.