Comment les professionnels de l'informatique utilisent Ansible

Ceci est destiné aux personnes déjà familiarisées avec Ansible et souhaitant améliorer la qualité de leurs playbooks pour une meilleure évolutivité, facilité d'utilisation, maintenabilité et sécurité.

Comment les professionnels de l'informatique utilisent Ansible

Organiser les fichiers Ansible

  • Il existe de nombreuses façons d'organiser les fichiers Ansible
  • Vous trouverez ci-dessous un exemple de structure que je recommande pour une bonne maintenabilité et une bonne évolutivité
  • N'hésitez pas à créer uniquement les répertoires et les fichiers dont vous avez besoin
ansible-project/
├── README.md
├── files
├── group_vars
│   ├── all
│   │   └── main.yml
│   ├── prod
│   │   ├── main.yml
│   │   └── secret.yml
│   └── staging
│       ├── main.yml
│       └── secret.yml
├── handlers
│   └── main.yml
├── hosts
│   ├── prod
│   └── staging
├── playbook.yml
├── roles
├── tasks
└── templates
  • files: les fichiers que nous souhaitons déployer sur les machines cibles. Ces fichiers ne contiennent pas de variables Ansible ni d'autres opérateurs Ansible comme ceux que nous pouvons utiliser dans les fichiers templates
  • group_vars: variables qui seront disponibles pour des groupes de machines spécifiques définis dans les fichiers d'inventaire des machines
  • handlers: contient des tâches qui peuvent être exécutées après des modifications spécifiques... Ex: recharger un serveur HTTP lorsqu'un fichier de configuration spécifique a changé
  • hosts: inventaire des machines (adresses IP, noms de domaine, alias...)
  • roles: un autre projet Ansible qui peut être réutilisé pour accomplir facilement des tâches spécifiques
  • tasks: fichiers de tâches organisés contenant les actions que nous voulons effectuer sur les machines définies dans les fichiers d'inventaire des machines
  • templates: fichiers de configuration des applications et des intergiciels contenant des variables Ansible qui seront remplacées dynamiquement lors du déploiement sur les machines. Les fichiers à l'intérieur de ce répertoire ont l'extension « j2 » et le moteur de création de templates utilisé s'appelle Jinja2
  • playbook.yml:fichier principal à partir duquel les tâches Ansible sont exécutées

Inventaires et variables des machines

  • Les dossiers à l'intérieur du répertoire « group_vars », à l'exception de « all », doivent correspondre à un groupe de machines déclaré dans le fichier d'inventaire des machines
  • Les variables définies dans les fichiers yaml créés dans le dossier « group_vars/hostsgroup » seront disponibles uniquement pour les machines faisant partie du groupe « hostsgroup ».
  • Voici comment nous déclarons des groupes de machines dans le fichier d’inventaire des machines Ansible :
# Defining a group named webservers
# containing webservers hosts
[webservers]
wsrv1.web.local
wsrv2 ansible_ssh_host=wsrv2.web.local # using host alias

# Defining a group named dbservers
# containing databases hosts
[dbservers]
dsrv1.db.local
dsrv2.db.local

# Defining a group of groups
# named webapp containing the
# webservers and dbservers groups
[webapp:children]
webservers
dbservers
  • En utilisant l'exemple précédent de fichier d'inventaire des machines, les variables définies dans les fichiers yaml du dossier « group_vars/webapp » seront disponibles uniquement pour les machines faisant partie du groupe « webapp », donc les machines des groupes « webservers » et « dbservers » car ces deux groupes font eux-même partie du groupe « webapp ».
  • Il est également possible de déclarer des variables directement dans le fichier d'inventaire des machines qui ne sont disponibles que pour des machines spécifiques ou un groupe de machines. Voici un exemple :
# Defining variables that are 
# available only for the hosts that
# are part of the webservers group
[webservers:vars]
domain_name=myapp.example.local
database_vip=db.example.local

# Defining variables that are available only
# for the dsrv1 and dsrv2 database hosts
[dbservers]
dsrv1.db.local master=dsrv2.db.local
dsrv2.db.local master=dsrv1.db.local

Fichiers et templates

  • Le répertoire « files » est l'endroit où nous plaçons les fichiers bruts que nous souhaitons déployer sur les machines cibles
  • Nous utilisons ensuite le module copy pour déployer les fichiers
  • Le paramètre src du module « copie » est l'endroit où nous spécifions le chemin (absolu ou relatif au répertoire « files ») des fichiers locaux que nous souhaitons déployer sur les machines cibles
  • Le répertoire « templates » est l'endroit où nous mettons les Fichiers de templates Ansible Jinja2 que nous souhaitons déployer sur les machines cibles. Ces fichiers peuvent contenir des variables Ansible, des conditions, des structures contrôles (if, else, etc) et ont l'extension 'j2'
  • Nous utilisons ensuite le module template pour déployer les fichiers
  • Le paramètre src du module « template » est l'endroit où nous spécifions le chemin (absolu ou relatif au répertoire « templates ») des fichiers de templates locaux que nous souhaitons déployer sur les machines cibles

Tâches et handlers

  • Le répertoire « tasks » est l'endroit où nous plaçons les différents fichiers de tâches appelés à partir de « plays » présents dans le fichier playbook principal (« playbook.yml »)
  • Chaque fichier de tâches a un objectif spécifique et contient la déclaration des actions que nous souhaitons effectuer ou l'état dans lequel nous souhaitons que les machines soient
  • Voici la liste des modules Ansible par catégorie, que vous pouvez utiliser lors de la création de tâches
  • Le répertoire « handlers » est l'endroit où nous plaçons les fichiers yaml contenant la déclaration des « handlers ». Les « handlers » sont utilisés pour effectuer des actions sur des modifications spécifiques (par exemple, recharger un serveur HTTP lors d'un changement de fichier de configuration)
  • Pour dire à une « tâche » d'utiliser un « handler », nous utilisons le mot-clé notify indiquant le nom du « handler » à utiliser lorsque l'état de la « tâche » est modifié
  • Les « handlers » doivent être déclarés dans les « plays » qui importent la tâche (à l'intérieur du fichier playbook principal) afin d'être reconnus
  • Les « handlers » sont par défaut exécutés à la fin de l'ensemble du « playbook ». Pour modifier ce comportement, consultez contrôler-quand-les-handlers-s'exécutent
  • Voici un exemple de « tâche » utilisant un « handler » :
# tasks/nginx_config.yml
---
- name: Deploy custom vhosts
  ansible.builtin.template:
    src: nginx/{{ item }}.j2
    dest: "{{ sites_available_dir }}/{{ item }}"
    mode: "0644"
  notify: Reload Nginx
  with_items:
    - "{{ custom_vhosts_list }}"
  when: enable_custom_vhost

# handlers/main.yml
---
- name: Reload Nginx
  ansible.builtin.systemd:
    name: nginx
    state: reloaded

# playbooks.yml
---
- name: Configure Nginx
  hosts: webservers
  become: true
  tasks:
    - ansible.builtin.import_tasks: tasks/nginx_config.yml
  handlers:
    - ansible.builtin.import_tasks: handlers/main.yml
  tags: [nginx_config, nginx]

Le fichier playbook principal

  • Le fichier « playbook.yml » est le point d'entrée pour l'exécution de nos « plays »
  • Ce fichier devrait nous donner un aperçu clair de tous les « plays » disponibles
  • Pour chaque « play » à l'intérieur de ce fichier, nous définissons :
    • un nom
    • les machines cibles
    • si nous voulons utiliser les privilèges root pour effectuer les actions
    • une liste de « tâches » importées depuis le dossier « tâches » que nous souhaitons effectuer
    • lors de l'utilisation des « rôles » d'Ansible, une liste de « rôles » à utiliser
    • une liste de fichiers « handlers » lorsque cela est nécessaire
    • une liste de « tags » qui peuvent être utilisés pour filtrer les « plays » lors de l'exécution (exécuter uniquement les pièces ayant ces tags)
  • Voici un exemple de contenu du fichier « playbook.yml » :
- name: Install Nginx
  hosts: webservers
  become: true
  tasks:
    - ansible.builtin.import_tasks: tasks/nginx_install.yml
  tags: [nginx_install, nginx]
  
- name: Configure Nginx
  hosts: webservers
  become: true
  tasks:
    - ansible.builtin.import_tasks: tasks/nginx_config.yml
  handlers:
    - ansible.builtin.import_tasks: handlers/main.yml
  tags: [nginx_config, nginx]
  
- name: Install MySQL
  hosts: dbservers
  become: true
  tasks:
    - ansible.builtin.import_tasks: tasks/mysql_install.yml
  tags: [mysql_install, mysql]
  
- name: Configure MySQL
  hosts: webservers
  become: true
  tasks:
    - ansible.builtin.import_tasks: tasks/mysql_config.yml
  handlers:
    - ansible.builtin.import_tasks: handlers/main.yml
  tags: [mysql_config, mysql]
  
- name: Servers postconf
  hosts: webservers:dbservers
  become: true
  tasks:
    - ansible.builtin.import_tasks: tasks/servers_postconf.yml
  tags: [servers_postconf] 
  • Pour une liste de tous les mots-clés que nous pouvons utiliser dans le fichier playbook principal, jetez un œil à mots-clés du playbook

Linting des playbooks Ansible

  • Le linting permet d'améliorer la qualité des plays Ansible en donnant des recommandations sur les meilleures pratiques pour l'écriture des playbooks
  • Les playbooks de haute qualité sont plus faciles à entretenir et à utiliser avec les nouvelles versions d'Ansible
  • Pour analyser les playbooks Ansible, nous pouvons utiliser Ansible Lint
  • Voici un exemple utilisant l'utilitaire de ligne de commande « ansible-lint » :
# Install ansible-lint
pip install ansible-lint

# Run
ansible-lint <ansible_files_path>

# Automatically fix some of the errors
ansible-lint --fix

Gestion des variables secrètes

  • Les variables contenant des informations sensibles doivent être chiffrées
  • L'utilitaire « ansible-vault » peut être utilisé à cette fin
  • Un mot de passe « vault » sera défini afin de chiffrer « secret.yml ». Assurez-vous de placer ce mot de passe dans un endroit sûr
  • Le mot de passe « vault » défini sera demandé pour afficher, modifier et déchiffrer le fichier « secret.yml »
  • L'option '--ask-vault-pass' doit être utilisée lors de l'exécution des playbooks afin de fournir le mot de passe 'vault' requis pour déchiffrer 'secret.yml'
  • Utilisez plutôt « --vault-password-file /path/to/vault_password_file » lorsque vous exécutez les playbooks de manière non interactive (à partir d'un pipeline CI/CD par exemple), après avoir injecté en toute sécurité le fichier de mot de passe dans l'environnement de déploiement
  • Ci-dessous les commandes à utiliser pour chiffrer, déchiffrer, visualiser et éditer 'secret.yml'
# Encrypt staging secret variables file
ansible-vault encrypt group_vars/staging/secret.yml

# Decrypt staging secret variables file
ansible-vault decrypt group_vars/staging/secret.yml

# View the content of staging secret variables file
ansible-vault view group_vars/staging/secret.yml

# Edit the content of staging secret variables file
ansible-vault edit group_vars/staging/secret.yml

Exécution des playbooks

# Staging
$ ansible-playbook -i hosts/staging playbook.yml --ask-vault-pass

# Prod
$ ansible-playbook -i hosts/prod playbook.yml --ask-vault-pass

Voici des options supplémentaires qui peuvent être utiles dans certains cas :

  • --syntax-check: vérifier uniquement la syntaxe des playbooks
  • --check: exécuter les playbooks en mode dry run (prédire les changements sans les exécuter)
  • --tags <tag_name(s)>: exécutez uniquement les « plays » à partir du fichier principal « playbook.yml », ayant les tags correspondants
  • -u <remote_user>: utiliser l'utilisateur spécifié pour la connexion SSH sur les machines
  • --ask-pass, -k: demander le mot de passe 'remote_user' pour la connexion SSH
  • --ask-become-pass, -K: demande le mot de passe d'escalade des privilèges pour 'remote_user'. Utile pour les jeux utilisant 'become: true' afin d'être exécutés avec les privilèges root
  • --vault-password-file: chemin vers le fichier contenant le mot de passe 'vault'. Remplace '--ask-vault-pass' pour les exécutions non interactives