Loading search data...

Ansible - Maîtriser les inventaires statiques

Publié le : 7 janvier 2021 | Mis à jour le : 27 juin 2023

logo ansible

Continuons notre formation Ansible avec au menu aujourd’hui les inventaires Ansible.

Un inventaire est une source de données listant les machines cibles devant être gérés par Ansible qui peuvent être organisée en groupes. On distingue deux type d’inventaire :

  • les inventaires statiques constitué d’un fichier décrivant la hiérarchie des serveurs
  • les inventaires dynamiques fourni par un système centralisé recensant tous les serveurs de l’infrastructure (ex aws).

Dans ce billet je vais vous donner quelques conseils pour gérer plus facilement vos variables définis dans vos inventaires.

Configurer la source de votre inventaire

Ansible utilise des plugins pour gérer les différentes source d’inventaires. Il existe toute une série de plugins et pour les lister il suffit de lancer la commande:

ansible-doc -t inventory -l

Les plugins d’inventaires sont gérés au niveau de la configuration d’ansible via le fichier ansible.cfg, que je vous conseille de mettre dans le répertoire de votre projet, car le plus souvent il sera fonction de celui-ci. Si vous n’en avez pas vous pouvez le générer :

ansible-config init --disabled > ansible.cfg

Par défaut, Ansible propose les suivants :

  • host_list: une simple liste d’hôte
  • script: un script qui construit un inventaire dynamique à partir d’une source de données
  • yaml: l’inventaire en format de fichier de données YAML
  • toml: l’inventaire en format de fichier de données TOML
  • ini: l’inventaire en format de fichier de données INI
  • auto:

Pour modifier cette liste il faut se rendre dans la section inventories et éditer enable_plugins

[inventory]
enable_plugins = host_list, script, auto, yaml, ini, toml

Ici nous allons laisser ceux définis par défaut et nous utiliserons le format le plus courant qui est host_list.

Constitution de votre fichier hosts

Je vous conseille de garder ce nom !

Organisation d’un inventaire statique

Ce que l’on rencontre le plus souvent dans les bonnes pratiques Ansible pour créer un inventaire statique est l’utilisation de la structure suivante:

inventories
├── production
|   ├── hosts
│   ├── group_vars
│   │   ├── all
│   │   │   └── all.yml
│   │   ├── dbservers
│   │   │   └── all.yml
│   │   └── webservers
│   │       └── all.yml
│   └── host_vars
│       ├── prodhost1.yml
│       └── prodhost2.yml
└── staging
    ├── group_vars
    │   ├── all
    │   │   └── all.yml
    │   ├── dbservers
    │   │   └── all.yml
    │   └── webservers
    │       └── all.yml
    ├── host_vars
    │   └── stagehost1.yml
    └── hosts

En effet il faut bien séparer vos différents environnements (le cloisonnement) pour éviter de lancer des actions sur la production alors que vous vouliez le faire sur l’environnement de test. Vous avez certainement remarqué que nous retrouvons des dossiers _vars qui vont nous servir à stocker nos variables.

La priorité de lecture des variables par Ansible

Avant de vous lancer dans la création de variables il est important de connaître la priorité de lecture qu'Ansible applique lors de l’éxécution d’une commande. Ansible priorise la source des variables, de la plus petite à la plus grande dans cet ordre :

  • command line values (par exemple, -u my_user qui n’est pas une variable)
  • role defaults (défini dans role/defaults/main.yml) 1
  • inventory file or script group vars 2
  • inventory group_vars/all 3
  • playbook group_vars/all 3
  • inventory group_vars/* 3
  • playbook group_vars/* 3
  • inventory file or script host vars 2
  • inventory host_vars/* 3
  • playbook host_vars/* 3
  • host facts / cached set_facts 4
  • play vars
  • play vars_prompt
  • play vars_files
  • role vars (défini dans role/vars/main.yml)
  • block vars (seulement pour les tasks dans un block)
  • task vars (seulement pour les tasks)
  • include_vars
  • set_facts / registered vars
  • role (et include_role) params
  • include params
  • extra vars ( par exemple, -e “user=my_user”)

On voit que les variables passées dans les paramètres de la commande ansible-playbook (-e) est le plus fort, il gagne sur tout le monde.

Imaginez une infrastructure gérée avec des dizaines de playbooks, des dizaines de fichiers de variables, des dizaines de groupes, … Si on ne s’organise par correctement, le risque est de rapidement d’en perdre le contrôle !

Un peu de pratique

Dans cet exercice nous allons créer l’environnement de test, ici staging, dans lequel nous aurons 3 machines répartis dans 2 groupes: webservers et dbservers.

Commençons par créer l’arborescence ci-dessus en se limitant à un environnement staging :

mkdir -p inventories/staging/{group_vars,host_vars}\{all,webservers,dbservers}
touch inventories/staging/hosts
touch inventories/staging/group_vars/{all,webservers,dbservers}.yml
touch inventories/staging/hosts_vars/stagehost{1,2}.yml

Dans le fichier inventories/staging/hosts mettez y ceci:

stagehost1

[webservers]
stagehost2

[dbservers]
stagehost3

Lançons la commande ansible-inventory en indiquant notre inventaire :

ansible-inventory -i inventories/staging --graph --vars # pour afficher les variables

@all:
  |--@dbservers:
  |  |--stagehost3
  |--@ungrouped:
  |  |--stagehost1
  |--@webservers:
  |  |--stagehost2

Remarquez que notre machine stagehost1.yml fais partie d’aucun group (ungrouped).

Maintenant plaçons une variable dans le fichier inventories/staging/group_vars/all.yml ceci :

VARIABLE: "common"
PACKAGES_COMMON:
  - python3

Relançons la commande ci-dessus :

ansible-inventory -i inventories/staging --graph --vars

@all:
  |--@dbservers:
  |  |--stagehost3
  |  |  |--{PACKAGES_COMMON = ['python3']}
  |  |  |--{VARIABLE = common}
  |--@ungrouped:
  |  |--stagehost1
  |  |  |--{PACKAGES_COMMON = ['python3']}
  |  |  |--{VARIABLE = common}
  |--@webservers:
  |  |--stagehost2
  |  |  |--{PACKAGES_COMMON = ['python3']}
  |  |  |--{VARIABLE = common}
  |--{PACKAGES_COMMON = ['python3']}
  |--{VARIABLE = common}

On retrouve bien la variable sur l’ensemble des hosts de notre inventaire.

Dans le fichier inventories/staging/webservers.yml ajouter ceci:

VARIABLE: "webserver"
PACKAGES_GROUP:
  - nginx

Et dans le fichier inventories/staging/dbservers.yml ajouter ceci:

VARIABLE: "dbserver"
PACKAGES_GROUP:
  - mysql

Relançons la commande :

ansible-inventory -i inventories/staging --graph --vars

@all:
  |--@dbservers:
  |  |--stagehost3
  |  |  |--{PACKAGES_COMMON = ['python3']}
  |  |  |--{PACKAGES_GROUP = ['mysql']}
  |  |  |--{VARIABLE = dbserver}
  |  |--{PACKAGES_GROUP = ['mysql']}
  |  |--{VARIABLE = dbserver}
  |--@ungrouped:
  |  |--stagehost1
  |  |  |--{PACKAGES_COMMON = ['python3']}
  |  |  |--{VARIABLE = common}
  |--@webservers:
  |  |--stagehost2
  |  |  |--{PACKAGES_COMMON = ['python3']}
  |  |  |--{PACKAGES_GROUP = ['nginx']}
  |  |  |--{VARIABLE = webserver}
  |  |--{PACKAGES_GROUP = ['nginx']}
  |  |--{VARIABLE = webserver}
  |--{PACKAGES_COMMON = ['python3']}
  |--{VARIABLE = common}

Maintenant dans le fichier inventories/staging/host_vars/stagehost1.yml ajouter ceci :

PACKAGES_HOST:
  - net-tools
@all:
  |--@dbservers:
  |  |--stagehost3
  |  |  |--{PACKAGES_COMMON = ['python3']}
  |  |  |--{PACKAGES_GROUP = ['mysql']}
  |  |  |--{VARIABLE = dbserver}
  |  |--{PACKAGES_GROUP = ['mysql']}
  |  |--{VARIABLE = dbserver}
  |--@ungrouped:
  |  |--stagehost1
  |  |  |--{PACKAGES_COMMON = ['python3']}
  |  |  |--{PACKAGES_HOST = ['net-tools']}
  |  |  |--{VARIABLE = common}
  |--@webservers:
  |  |--stagehost2
  |  |  |--{PACKAGES_COMMON = ['python3']}
  |  |  |--{PACKAGES_GROUP = ['nginx']}
  |  |  |--{VARIABLE = webserver}
  |  |--{PACKAGES_GROUP = ['nginx']}
  |  |--{VARIABLE = webserver}
  |--{PACKAGES_COMMON = ['python3']}
  |--{VARIABLE = common}

Maintenant voyons comment concaténer les trois variables PACKAGES_COMMON, PACKAGES_GROUP et PACKAGES_HOST en une seule dans un playbook.

---
- hosts: all
  gather_facts: false
  tasks:
    - name: my appender
      ansible.builtin.set_fact:
        PACKAGES: '{{PACKAGES_HOST | default ([]) + PACKAGES_COMMON}}'
    - name: debug
      ansible.builtin.debug:
        var: PACKAGES, VARIABLE

Je ne demande pas la collecte des facts ici pour éviter de surcharger l’affichage.

Lançons le et comme mes hosts n’existe pas je demande une connection locale (-c local):

ansible-playbook -c local -i inventories/staging playbook.yml

On obtient bien le résultat attendu.

ok: [stagehost1] => {
    "PACKAGES,VARIABLE": "(['net-tools', 'python3'], 'common')"
}
ok: [stagehost3] => {
    "PACKAGES,VARIABLE": "(['mysql', 'python3'], 'dbserver')"
}
ok: [stagehost2] => {
    "PACKAGES,VARIABLE": "(['nginx', 'python3'], 'webserver')"
}

Astuces

On a vu comment afficher les variables avec la commande ansible-inventory mais faisons également dans le playbook. Il suffit de remplacer PACKAGES dans la partie debug par hostvars[inventory_hostname]. Relancez et vous remarquerez que des informations essentielles vous sont fournis pour comprendre le fonctionnement d’Ansible.

Pour finir ajoutez une machine stagehost4 dans le groupe [webservers] et affecter lui ceci dans son fichier host_vars:

VARIABLE: "titi"
PACKAGES_HOST:
  - htop

Vous devez obtenir ceci :

ok: [stagehost1] => {
    "PACKAGES,VARIABLE": "(['net-tools', 'python3'], 'common')"
}
ok: [stagehost2] => {
    "PACKAGES,VARIABLE": "(['nginx', 'python3'], 'webserver')"
}
ok: [stagehost4] => {
    "PACKAGES,VARIABLE": "(['htop', 'nginx', 'python3'], 'titi')"
}
ok: [stagehost3] => {
    "PACKAGES,VARIABLE": "(['mysql', 'python3'], 'dbserver')"

Passez stagehost4 dans le group et regardez les valeurs, y a rien qui vous choque ?

On retrouve la propriété PACKAGE_GROUP avec nginx !!!

C’est avec ce genre de situation qu’on perd vite le contrôle.

Que se passe t’il ? C’est la précédence qui donne ce résultat. Le fait est que ansible lit la valeur des variables de l’hôte, pas du groupe. Autrement dit, il ne peut y avoir qu’une seule valeur pour une variable sur un hôte. Si vous voulez régler ce problème il faut surcharger la variable au niveau du host.

Quelques bonnes pratiques pour garder le contrôle

Pour les variables communes :

  • Ne mettre dans l’inventaire que des variables globales propre à l’infrastructure;
  • Pour les autres variables les mettre au niveau du playbook;
  • Éviter autant que possible de mettre des machines dans plusieurs groupes en créer plutôt un troisième;
  • Utiliser la commande ansible-inventory --list pour vérifier que le résultat obtenu est conforme à l’attendu;

Plus loin

Si vous voulez plus de tutorials Ansible je vous renvoie sur le billet de l'introduction à ansible

Mots clés :

devops ansible tutorials infra as code formation ansible

Si vous avez apprécié cet article de blog, vous pouvez m'encourager à produire plus de contenu en m'offrant un café sur  Ko-Fi. Vous pouvez aussi passer votre prochaine commande sur amazon, sans que cela ne vous coûte plus cher, via  ce lien . Vous pouvez aussi partager le lien sur twitter ou Linkedin via les boutons ci-dessous. Je vous remercie pour votre soutien.

Autres Articles


Commentaires: