Utiliser la notion de role dans Ansible est indispensable pour exploiter toute la puissance de l’outil.

Dans un article précédent, nous avons présenté Ansible et vu son utilisation basique. Cet article portera sur la modularité que peut apporter le rôle Ansible.

Pré-requis

Dans cet article, je suppose que vous savez utiliser Anisble et/ou que vous avez suivi l’article précédent : Ansible : l’outil de déploiement incontournable.

Je rappelle simplement les commandes pour bootstraper le répertoire de travail (CentOS 7).

yum install -y epel-release
yum install -y ansible
mkdir -p ansible-playbooks/{inventories,roles,{host,group}_vars}
cd ansible-playbooks/

Nous allons directement partir au coeur de notre sujet : les rôles Ansible.

Mise en situation

Partons sur la création d’un rôles qui installera le middleware Apache ainsi que des VirtualHost. L’ajout et la suppression des VirtualHost ne devront pas supposer la modification rôle.

Challenge accepted !

L’inventaire de toute une vie

L’inventaire Ansible sur lequel se basera cet article :

vi inventories/reverse_proxy
# Les serveurs
centos01 ansible_host=centos01.ineat-conseil.fr

# Les groupes
[centos]
centos01

[linux:children]
centos

Le Role Apache

Ecrire un playbook est une chose, mais ce n’est pas pérenne quand on commence à utiliser et réutiliser les taches et les playbooks. C’est ici qu’interviennent les rôles ansible.

Dans l’éco-système Ansible, les rôles sont le pivot qui permettent la modularisation des taches et ainsi faciliter le séquencement des actions sur les serveurs. Ils sont en général constitués des éléments suivants :

  • Un répertoire “tasks” qui contient les taches à appliquer sous forme de fichiers .yaml
  • Un répertoire “defaults” qui contient les variables par défaut du rôle
  • Un répertorie “files” qui contient les fichiers à copier sur le serveur cible
  • Un répertoire “templates” qui contient les fichiers variabilisés à copier sur le serveur cible
  • Un répertoire “handlers” qui contient les actions à déclencher

A minima, un rôle doit contenir la structure suivante pour fonctionner :

ansible-playbooks/
└── roles/
    └── tasks/
        └── main.yaml

Le rôle se base sur le fichier tasks/main.yaml pour exécuter les taches. Libre à vous de créer d’autres fichiers de taches et de les importer dans le main.yaml.

Documentation officielle sur le role

Ecrivons la structure de notre premier rôle appelé “httpd” :

mkdir -p roles/httpd/{defaults,tasks,files,templates,handlers}
touch roles/httpd/{defaults,tasks,handlers}/main.yaml
ansible-playbooks/
└── roles/
    └── httpd/
        ├── defaults/
        │   └── main.yaml
        ├── tasks/
        │   └── main.yaml
        ├── files/
        ├── templates/
        └── handlers/
            └─── main.yaml

Parcourons ensemble les différents répertoires. Chacun d’entre-eux nous apportent leurs lots de fonctionnalités.

Définir les déclencheurs du rôle

Commençons par les handlers, particularité de Ansible, ce sont des taches qui seront déclenchées par les taches principales.

Partons pour notre première tache déclenchable sur le redémarrage du service Apache. On utilise le module systemd pour réaliser le redémarage d’Apache.

vi roles/httpd/handlers/main.yaml
---
- name: Reload apache service
  systemd:
    name: httpd
    state: restarted

L’attribut name est très important, car c’est lui qui va définir l’identifiant du déclencheur.

Les taches du rôle

Définissons les taches du rôles dans le fichier tasks/main.yaml. Rendons notre rôle simple à lire.

Le fichiers de taches principal ordonnance les groupes de taches rangés par fichiers :

vi roles/httpd/tasks/main.yaml
---
- include_tasks: install.yaml

Et le fichier d’installation apache :

vi roles/httpd/tasks/install.yaml
---
- name: Install package httpd
  yum:
    name: httpd
state: installed

Les variables du rôle

Les variables du rôle ont pour portée… le rôle ! =D

Rien de plus simple que de limiter des variables à un rôle et qu’elles soient  surchargées par les group_vars du playbook. On peut ainsi rendre le rôle non bloquant (sur le plan des variables) tout en le laissant modulable.

Documentation officielle sur les variables

Définissons une variable contenant une liste de VirtualHost pour configurer le middleware Apache.

vi roles/httpd/defaults/main.yaml
---
apache_virtualhosts:
  - servername: website.ineat-conseil.fr
    documentroot: /var/www/html

On pourra ainsi rendre la configuration des VirtualHost Apache facile à modifier et à surcharger. Cette pratique permet de conserver un rôle dans un état figé, et uniquement modifier ses paramètres : vive la modularité !

Copier les fichiers du rôle

Créons notre superbe site internet à base de “Hello World” :

vi roles/httpd/files/index.html
Hello World !

On peut maintenant faire appel à ce fichier et le copier sur le serveur distant dans le répertoire /var/www/html.

C’est le module copy qui se charge de copier des fichiers d’un serveur à l’autre. Il prend en paramètre le fichier source (celui à copier) et le fichier cible (le chemin complet du fichier à copier sur le serveur distant).

Documentation officielle sur le module copy

Le paramètre src peut être référencé avec un nom de fichier, le chemin relatif du fichier ou le chemin absolu du fichier sur le serveur.
En utilisant le chemin absolu, pas de mystère le fichier se trouve pile à cet endroit sur le serveur.
En utilisant le nom du fichier (ou le chemin relatif), la recherche se fait nativement à plusieurs niveaux :

  • Dans le répertoire files du rôle,
  • Le répertoire courant du fichier de tâches du rôle,
  • Le répertoire courant du playbook qui lance le rôle.
vi roles/httpd/tasks/website.yaml
- name: Copy website index
  become: true
  copy:
    src: index.html
    dest: /var/www/html/index.html
    owner: apache
    group: apache

Le paramètre src fait ici référence au fichier créé à l’étape précédente dans le répertoire roles/httpd/files/virtualhost.conf.

Vous l’aurez remarqué, l’instruction become s’est ajouté au sein de la tâche, elle permet d’élever les droits sur les instructions demandées. Remarque : il est nécessaire d’être root pour attribuer des droits (utilisateur et permissions) au travers des tâches Ansible.

N’oublions pas de mettre à jour le fichier de taches principal :

vi roles/httpd/tasks/main.yaml
---
- include_tasks: install.yaml
- include_tasks: website.yaml

Utiliser les templates du rôle

Il nous fat maintenant un VirtualHost à copier sur le serveur distant.

Copier un fichier statique est une bonne chose, mais nous souhaitons aller un cran plus loin avec notre configuration Apache. C’est ici qu’est introduit la notion de Template avec Ansible.

Un Template Ansible est un fichier qui utilise les variables contextualisées.
Variables contextualisées…?? Un bien grand mot qui fait référence à l’ensemble des variables collectées, assemblées, agrégées au lancement du playbook et du rôle.

Le template s’utilise comme le fichier statique, on manipule un fichier du rôle qui se trouve cette fois dans le répertoire templates du rôle. L’import des variables se fait à l’aide de la syntaxe Jinja2. On peut aller très loin dans la récupération des variables Jinja en leur appliquant des transformation, des filtres, des pipelines d’agrégation, …

Documentation officielle sur le module template

Commençons simple avec l’import du fichier 🙂
Créons un VirtualHost qui va récupérer le dictionnaire apache_virtualhosts.

Les exemples qui vont suivre suivent un cheminement pour permettre de mieux comprendre la manipulation des variables Jinja2 dans les templates.

Approche 1 : Itération dans le template

Première approche : itérer sur le dictionnaire directement dans le template du virtualhost.

vi roles/httpd/tasks/virtualhosts.yaml
- name: Copy the Apache VirtualHost template
  become: true
  template:
    src: virtualhost.conf.j2
    dest: /etc/httpd/conf.d/virtualhost.conf
    onwer: root
    group: root

Créons le template :

vi roles/httpd/templates/virtualhost.conf.j2
{% for vhost in apache_virtualhosts.items() %}
<VirtualHost *:80>
  Servername {{ vhost.servername }}
  DocumentRoot {{ vhost.documentroot }}
</VirtualHost>
{% endfor %}

Lançons la tache pour tester et s’assurer du rendu final. Le résultat semble correct, le templating se fait correctement. Première approche pas trop mal, mais qui apporte beaucoup de complexité au sein même du template.

Approche 2 : Itération sur la tâche

Seconde approche : itérer sur la tache qui applique le template du virtualhost.

vi roles/httpd/tasks/virtualhosts.yaml
- name: Copy the Apache VirtualHost template
  become: true
  template:
    src: virtualhost.conf.j2
    dest: "/etc/httpd/conf.d/{{ item.servername }}.conf"
    owner: root
    group: root
  loop: "{{ apache_virtualhosts }}"

Créons le template :

vi roles/httpd/templates/virtualhost.conf.j2
<VirtualHost *:80>
  Servername {{ item.servername }} 
  DocumentRoot {{ item.documentroot }}
/VirtualHost>

Lançons la tache pour tester et s’assurer du rendu final. Le résultat semble correct, le templating se fait correctement.

Approche totalement différente de la précédente, ici on démultiplie les VirtualHost par fichier, c’est l’application de la boucle avec loop: qui change la nature du fichier final.
Cette seconde approche apporte beaucoup de clarté dans le template Ansible et séquence la résultante de la tache par une multitude de fichiers. Dans notre cas, c’est l’approche que je conseille pour Apache notamment.

N’oublions pas de mettre à jour le fichier de taches principal

vi roles/httpd/tasks/main.yaml
---
- include_tasks: install.yaml
- include_tasks: website.yaml
- include_tasks: virtualhosts.yaml

Appliquer les déclencheurs du rôle

Vous vous souvenez des handlers déclarés précédemment, nous allons maintenant les appeler pour agrémenter notre rôle.

Sauf que les handlers se déclenchent sur la résultante [changed] d’une tache. Il faut donc appliquer un changement à toute tache qui apporte des modifications nécessitant un redémarrage d’Apache :

  • Les fichiers de configuration

Modifions donc notre module template précédemment créé.

vi roles/httpd/tasks/virtualhosts.yaml
- name: Copy the Apache VirtualHost template
  become: true
  template:
    src: virtualhost.conf.j2
    dest: "/etc/httpd/conf.d/{{ item.servername }}.conf"
    onwer: root
    group: root
  loop: "{{ apache_virtualhosts }}"
  notify:
    - Reload apache service

L’appel au déclencheur se fait sur base du paramètre notify et prend en valeur le nom du ou des actions à déclencher, ici Reload apache service.

Construction du playbook

Maintenant que tous les ingrédients sont réunis, nous pouvons construire le playbook qui va appeler toute cette magie !

Documentation officielle sur la construction d’un playbook role

Oulalah !! Ca va être compliqué d’appeler un rôle… je laisse juger par vous-même.

vi install_httpd.yaml
---
- hosts: [centos]
  roles:
    - httpd

Et c’est tout !!! Rapide, n’est-ce pas ?!

A présent, il ne reste plus qu’à lancer le tout et attendre que la magie opère…

ansible-playbook -i inventories/reverse_proxy install_httpd.yaml

Conclusion

Dans ce “rapide” tutoriel, nous avons vu les points suivants :

  • Création d’un Role
    • Notification avec les Handlers
    • Utilisation des Variables
    • Utilisation des Templates
  • Séquencement avec les Modules
    • Gestion des paquets avec yum
    • Services systèmes avec systemd
    • Transfère des fichiers avec copy
    • Variabilisation des fichiers avec template
  • Création d’un playbook de rôles

Nous avons vu les différentes applications des Templates dans Ansible et du niveau de boucle associé.
J’apporterai très prochainement des sources GitHub qui compileront les exercices réalisés dans ce tutoriel.

Vous avez maintenant les armes nécessaire pour bien aborder Ansible, ses modules, ses rôles et ses playbooks sereinement avec quelques bonnes pratiques d’emblée.

Dans un très prochain article, nous passerons en revue les filtres Jinja2 applicables dans les Template Ansible.