Loading search data...

Gérer et Maitriser les inventaires Ansible

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

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

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

Dans ce billet je vais vous donner quelques tips 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 télécharger celui disponible dans le projet ansible.

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
│   ├── 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és dans les paramètres (-e) est le plus fort, il gagne sur tout le monde.

Imaginez une infrastructure gérer par des dizaines de playbooks, des dizaines de fichiers de variables, des dizaines de groupes, …. Et bein si on s’organise par correctement on en perd vite 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: no
  tasks:
    - name: my appender
      set_fact:
        PACKAGES: '{{PACKAGES_HOST | default ([]) + PACKAGES_COMMON}}'
    - name: debug
      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]. Dans cet exemple je ne demande pas la collecte des facts pour éviter de surcharger l’affichage.

Relancer et vous remarquerez que des informations essentielles vous sont fournis pour comprendre le fonctionnement d’Ansible.

Pour finir ajouter 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 precedence qui fait ceci. Le fait est que ansible lie la valeur des variables à l’hôte, pas au groupe. Autrement dit, il ne peut y avoir qu’une seule valeur pour une variable sur un hôte. Si vous voulez régler 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 propre à l’infrastructure;
  • Pour les autres variables les mettre au niveau du playbook ou des roles;
  • Éviter autant que possible de mettre des machines dans plusieurs groupes en créer plutôt un troisième;
  • Utiliser la commande ansible-inventory –graph –vars pour vérifier que c’est obtenu est conforme à l’attendu;

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


Alimenter un blog comme celui-ci est aussi passionnant que chronophage. En passant votre prochaine commande (n'importe quel autre article) via ce lien, je receverai une petite commission sans que cela ne vous coûte plus cher. Cela ne me permet pas de gagner ma vie, mais de couvrir les frais inhérents au fonctionnement du site. Merci donc à vous!

Mots clés :

devops, ansible, tutorials,

Autres Articles