ANSIBLE INVENTORY - Du Statique au Dynamique

Ansible permet à l’heure actuelle de manipuler des inventaires statiques et des inventaires dynamiques. Le modèle statique fonctionne bien avec des environnements figés, n’évoluant pas souvent. Par contre il devient vite limitant dans le cadre d’environnements en cours de développement sujets à des reconstructions ou en évolutions permanentes.

Au travers des inventaires dynamiques, Ansible est capable de générer automatiquement son inventaire à partir des APIs d’un cloud provider ou de services tiers, pour pouvoir ensuite l’utiliser.

Tout au long de cet article, nous utiliserons un exemple sur AWS. Puis nous verrons comment passer d’un cloud provider à un autre facilement avec les inventaires dynamiques. La méthode reste la même avec n’importe lequel des autres cloud providers (GCP, Azure, Openstack, …)

Ansible avec de l’infrastructure figée

Pour appliquer ses playbooks, Ansible a besoin de connaître la liste des instances sur lesquelles il effectuera des changements. Pour cela, il utilise un système d’inventaire. Dans un inventaire, on trouve :

  • La liste des hôtes avec leurs informations de connection (DNS, ip, utilisateur, …)
  • Des groupes fonctionnels regroupant des instances (app, webserver, bdd, …)

Les inventaires Ansible peuvent contenir des variables relatives aux instances mais pour gagner en clarté, les variables sont placées dans des group_vars et des host_vars comme décrit dans les bonnes pratiques Ansible.

Exemple d’un inventaire avec des groups vars :

# production.ini
[base:children]
webserver
appserver
bdderver

[webserver]
front1 ansible_hosts=10.0.0.34
front2 ansible_hosts=10.0.0.74

[appserver]
app1 ansible_hosts=10.0.0.18
app2 ansible_hosts=10.0.0.49

[bdderver]
bdd1 ansible_hosts=10.0.1.20

Dans ce contexte, l’équipe a récupéré manuellement toutes les adresses IP des machines à contacter et a construit son inventaire.

À partir de cet inventaire, Ansible est capable de contacter les machines distantes et de jouer son playbook dessus. Pour cela, il suffit d’indiquer à la commande `ansible-playbook` l’inventaire à utiliser :

$ ansible-playbook -i inventories/production.ini playbooks/webserver.yml

Les inventaires statiques marchent bien mais obligent un maintien des inventaires lorsque de nouvelles machines sont ajoutées, détruites ou reconstruites. Ce modèle ne supporte donc pas une mise à l’échelle automatique car il oblige un changement de code pour chaque modification de l’infrastructure.

Les Inventaires dynamiques (Présentation)

Pour pallier ça, Ansible a introduit le concept d’inventaires dynamiques.

Depuis la version 2.0, deux nouvelles fonctionnalitées ont facilité l’utilisation de ces inventaires:

  • Le chargement des inventaires depuis un script (fonctionnalité existante avant la 2.0)
  • Des scripts communautaires pour les principaux cloud providers (AWS, Azure, Openstack, Digitalocean , GCP, …)

La base des inventaires dynamiques provenant de scripts communautaires, nous allons dans un premier temps nous intéresser à ces derniers. L’intégralité des scripts est disponible sur le GitHub d’Ansible dans le dossier contrib/inventory (ici pour ceux de la version 2.7).

Dans le cadre de notre exemple, nous récupérons ceux associés au service IaaS d’AWS, EC2 (Elastic Compute Cloud), le fichier ec2.py et le fichier ec2.ini pour les placer dans notre répertoire d’inventaires.

Le script Python ec2.py s’occupe d’interroger les APIs d’AWS pour récupérer la liste des instances et différentes variables fournies par AWS (l’état de l’instance, son IP privée et publique, la plateforme, …).

Le fichier de configuration ec2.ini est consommé par le fichier ec2.py et permet d’adapter les sorties du script à ses besoins, par exemple:

  • Utiliser les IP privées au lieu des IP publiques
  • Ne récupérer que les instances d’une certaine région
  • Regrouper les instances en fonction des tags
  • etc …

exemple de fichier de configuration ec2.ini:


# Ansible EC2 external inventory script settings
#
[ec2]
regions = eu-west-1
destination_variable = public_dns_name
vpc_destination_variable = ip_address

# By default, only EC2 instances in the 'running' state are returned. Set
all_instances = False

# Replace - tags when creating groups to avoid issues with ansible
replace_dash_in_groups = True

group_by_availability_zone = False
group_by_platform = False
group_by_tag_keys = True

… 

La configuration terminée, nous pouvons maintenant utiliser le script ec2.py pour récupérer l’ensemble des machines qui constitueront l’inventaire.

Pour interroger les API d’AWS, le script a besoin de credentials. Pour plus d’informations, voir la documentation AWS sur le sujet. S’il n’y a pas d’instance en état running sur le compte AWS et dans la région cible, la sortie du script sera vide.

Regardons ensemble la sortie du script :


$ export AWS_ACCESS_KEY_ID = 'AK*****'
$ export AWS_SECRET_ACCESS_KEY= 'l7d****'
$ ./inventories/ec2.py

Le script nous retourne un objet JSON contenant plusieurs sections :

  • une section "_meta : hostvars” qui va contenir des informations permettant à Ansible d’établir une connexion aux instances, comme par exemple leur adresse IP.
  • une section par groupe Ansible formés par le script grâce aux différents tags des instances.

Pour regrouper efficacement les instances (web, app, bdd), il est donc primordial de poser des tags qui permettent d’identifier les groupes de machines.

Maintenant que nous avons vu comment fonctionne le script de la communauté, nous allons l’intégrer dans notre projet Ansible actuel.

Structure du projet:

Ansible est capable de prendre en entrée un inventaire sous forme de script, il suffit de lui donner le chemin du fichier et il s’occupera de l'exécuter et de parser sa sortie. En cas d’erreur lors de l'exécution du script, Ansible aura un inventaire vide.

Exemple d’utilisation :


$ ansible-playbook -i inventories/ec2.py playbooks/webserver.yml

Travailler avec les inventaires dynamiques

Comme on l’a vu, les inventaires dynamiques permettent de récupérer la liste des machines et de les grouper en fonction d’éléments discriminants : Tags, Région, etc…

Ces regroupements sont différents suivants le cloud provider utilisé pour collecter l’inventaire. Nous allons donc voir comment organiser ces inventaires dynamiques pour s’abstraire de la provenance de l’inventaire et gagner en lisibilité.

Il est possible dans Ansible de créer un groupe à partir d’un autre en utilisant des children. Grâce à ça nous allons pouvoir “renommer” les groupes fournis par les inventaires dynamiques afin de leur donner un nom plus parlant. En utilisant ce nouveau nom de groupe dans les différents playbooks à jouer, on s’abstrait aussi de la source de l’inventaire dynamique.

Dans notre exemple, ec2.py récupère les informations de connexion des machines sur AWS et les regroupe par tags. Imaginons que vous ayez labellisé vos instances avec le tag

ansible_group = webserver

L’inventaire dynamique d’AWS va récupérer ce tag et créer un groupe nommé ‘tag_ansible_group_webserver’. Nous pourrions utiliser ce groupe directement dans les playbooks ou les group_vars mais la lecture du code s’en trouverait alourdie. Nous allons donc créer un fichier d’inventaire, contenant la définition de notre groupe webserver, dans le répertoire d’inventaire afin qu’il soit lu par Ansible. Dans l’exemple suivant, nous avons nommé notre fichier production.ini :

# inventory/production/production.ini

[base:children]
webserver
appserver
bdderver

[webserver:children]
tag_ansible_group_webserver

[appserver:children]
tag_ansible_group_appserver

[bdderver:children]
tag_ansible_group_bddserver





Ansible étant capable de charger un répertoire d’inventaire, il suffira juste de lui donner le chemin du répertoire lors de l'exécution de notre playbook:

 $ ansible-playbook -i inventories/production/ playbooks/webserver.yml

Visualisez l’inventaire avec ansible-inventory

Avec l’utilisation des children et des inventaires dynamiques, nous pouvons donc gérer plus facilement les groupes de machines dans nos playbooks Ansible et nos group_vars. Cependant, pour débugger, nous avons parfois besoin de voir l’inventaire et les machines qui le composent.

Pour cela, nous pouvons utiliser la commande ansible-inventory. Cette commande permet d’afficher les informations présentes dans un inventaire et donc de voir les machines récupérées par l’inventaire dynamique.

L’utilisation de la commande est assez simple, il suffit de lui donner un inventaire (statique ou dynamique) et une action. Nous allons ici nous focaliser sur les actions ‘--graph’ et ‘--list’.

Afin de visualiser facilement les machines ainsi que leur appartenance à un groupe, nous allons utiliser l’option ‘--graph’.


$ ansible-inventory -i inventory/production/ --graph
@all:
  |--@ec2:
  | |--176.34.143.55
  | |--18.202.244.88
  | |--34.244.230.81
  |--@tag_ansible_group_appserver:
  | |--176.34.143.55
  |--@tag_ansible_group_bddserver:
  | |--18.202.244.88
  |--@tag_ansible_group_webserver:
  | |--34.244.230.81
  |--@ungrouped:
  |--@appserver:
  | |--@tag_ansible_group_appserver:
  | |  |--176.34.143.55
  |--@bddserver:
  | |--@tag_ansible_group_bddserver:
  | |  |--18.202.244.88
  |--@webserver:
  | |--@tag_ansible_group_webserver:
  | |  |--34.244.230.81

La sortie de la commande nous montre ainsi toutes les machines récupérées par l’inventaire dynamique. On voit que les instances sont groupées dans des groupes ‘tag_<key>_<value>’  qui sont incorporés dans les groupes que nous avons définis dans notre fichier d’inventaire. On peut donc vérifier que les machines sont organisées correctement et appartiennent aux bons groupes dans le cas de bugs.

Si nous voulons plus d’informations sur les machines de l’inventaire nous pouvons utiliser l’action ‘--list’. Cette action va nous donner la même sortie que l’inventaire dynamique que nous avons joué auparavant.


$ ansible-inventory -i inventory/production/ --list

L’action ‘--list’ permet de vérifier les variables attachées à tous les hôtes de notre inventaire. Il est parfois pratique d’obtenir la liste complète des variables chargées notamment pour du debug.

Ansible multi-cloud

À cette étape, vous êtes capables d'utiliser vos inventaires dynamiques et de les déboguer facilement. Nous allons maintenant voir dans cette partie comment gérer notre code Ansible afin de pouvoir travailler sur deux cloud providers différents en même temps et déployer notre applications sur deux plateformes.

Nous l’avons vu, travailler sur un cloud provider avec les inventaires dynamiques est simple. Il suffit d’ajouter le script fourni par la communauté dans notre dépôt ansible et de l’exécuter à l’appel d’Ansible.

Maintenant que pouvons nous faire pour pouvoir appeler l’inventaire dynamique d’un autre cloud provider ?

C’est tout simple, il suffit d’ajouter le script relatif à ce nouveau provider à côté de celui que nous utilisons déjà.

Ansible, lorsqu’on l’appelle, va lire l’ensemble des fichiers qui se trouve dans le répertoire inventory. Dans notre cas, il trouve les deux scripts pour les deux cloud providers et les exécute. Les scripts interrogent chacun leur cloud provider et retourne les informations des machines provisionnées sur celui ci. On obtient au final un inventaire complet contenant les machines des deux plateformes. Il nous suffit de changer notre inventaire de “renommage” pour incorporer les spécificités de ce nouveau cloud provider et de lancer le playbook Ansible.

Pour reprendre l’exemple de notre repository, nous aurons la structure suivante:


# inventory/production/production.ini
[base:children]
webserver
appserver
bdderver

[webserver:children]
tag_ansible_group_webserver # aws
ansible_group_webserver # azure

[appserver:children]
tag_ansible_group_appserver # aws
ansible_group_appserver # azure

[bdderver:children]
tag_ansible_group_bddserver # aws
ansible_group_bddserver # azure

Conclusion

Ansible propose plusieurs façon de gérer ses inventaires. Que ce soit de manière statique ou dynamique, ces inventaires ont pour unique but de lister et grouper les machines à configurer. L’utilisation de l’un ou l’autre des inventaires reflète souvent la façon dont nous traitons les machines qui composent l’inventaire (approche pet vs cattle).

Nous avons vu qu’il était assez simple de mettre en place des inventaires dynamiques, notamment avec les clouds providers, mais il faut tout de même garder en tête les quelques pré-requis qui peuvent augmenter le niveau de complexité:

  • Accès réseau aux apis du provider
  • Credentials permettant de récupérer les informations des machines

Les inventaires dynamiques sont très utiles pour bénéficier d’une plus grande flexibilité notamment avec plusieurs cloud providers mais ils peuvent s’avérer overkill dans un contexte où le nombre de machine et l’accès à ces dernière n’évolue pas tout au long du cycle de vie de l’infrastructure.

Les inventaires dynamique étant de plus en plus utilisés et leur support non officiel, Ansible a introduit la notion de plugins pour les inventaires. Cette nouvelle méthode de récupération d'inventaire est native et permettra de se passer des scripts communautaires à terme.

Cette implémentation est encore jeune (ansible 2.5) et supporte moins de provider que les scripts communautaires pour le moment.

Nous vous en dirons plus dans un prochain article !