Ansible Best-Practices
La meilleure pratique pour utiliser Ansible est de comprendre la philosophie qui sous-tend la conception d'Ansible. Vous pouvez remarquer que presque toutes les meilleures pratiques d'Ansible ont une sorte de relation avec la philosophie qui sous-tend Ansible.
Tout d'abord, Ansible réduit la complexité grâce à la conception d'outils, et il encourage également les utilisateurs à faire de même.
La philosophie cruciale suivante de la conception d'Ansible est l'optimisation du contenu pour la lisibilité. Si le contenu d'Ansible est correctement optimisé, il pourrait également servir de documentation pour l'automatisation du flux de travail.
La recommandation la plus importante dans la philosophie d'Ansible est l'adéquation des diverses approches.
Ansible possède des capacités substantielles d'adaptation à divers environnements et flux de travail tout en assurant la gestion de tâches d'automatisation complexes. Par conséquent, les utilisateurs doivent continuer à expérimenter différentes approches pour tirer le meilleur parti de la puissance et de la simplicité d'Ansible.
Organisation du contenu
La section suivante présente l’un des nombreux moyens possibles d’organiser le contenu d’un playbook.
Un moyen crucial d’organiser le contenu de votre Playbook est la fonction d’organisation «Rôles» d’Ansible.
Vous devriez prendre le temps de lire et de comprendre la documentation sur les rôles qui est disponible ici: Rôles.
Contrôle de version
Utilisez le contrôle de version.
Conservez vos playbooks et votre fichier d’inventaire dans git (https://gitlab.accor.net), et validez-les lorsque vous les modifiez.
Ainsi, vous disposez d’un journal d’audit décrivant quand et pourquoi vous avez modifié les règles qui automatisent votre infrastructure.
Structure du répertoire
Le niveau supérieur du répertoire contiendrait des fichiers et des répertoires comme ceci :
production # inventory file for production servers
staging # inventory file for staging environment
group_vars/
group1.yml # here we assign variables to particular groups
group2.yml
host_vars/
hostname1.yml # here we assign variables to particular systems
hostname2.yml
library/ # if any custom modules, put them here (optional)
module_utils/ # if any custom module_utils to support modules, put them here (optional)
filter_plugins/ # if any custom filter plugins, put them here (optional)
site.yml # master playbook
roles/
common/ # this hierarchy represents a "role"
tasks/ #
main.yml # <-- tasks file can include smaller files if warranted
handlers/ #
main.yml # <-- handlers file
templates/ # <-- files for use with the template resource
ntp.conf.j2 # <------- templates end in .j2
files/ #
bar.txt # <-- files for use with the copy resource
foo.sh # <-- script files for use with the script resource
vars/ #
main.yml # <-- variables associated with this role
defaults/ #
main.yml # <-- default lower priority variables for this role
meta/ #
main.yml # <-- role dependencies
library/ # roles can also include custom modules
module_utils/ # roles can also include custom module_utils
lookup_plugins/ # or other types of plugins, like lookup in this case
webtier/ # same kind of structure as "common" was above, done for the webtier role
monitoring/ # ""
fooapp/ # ""
Rester simple : Keep It Simple
Lorsque vous pouvez faire quelque chose, faites-le simplement. N'utilisez pas simultanément toutes les fonctionnalités d'Ansible.
Utilisez ce qui fonctionne pour vous. Par exemple, vous n'aurez probablement pas besoin de vars, vars_files, vars_prompt et –extra-vars en même temps, tout en utilisant également un fichier d'inventaire externe.
Si quelque chose vous semble compliqué, ce qui est probablement le cas, c'est peut être une bonne occasion de simplifier les choses.
Écrire les rôles avec un sens technique
Dès qu’on commence à écrire un rôle Ansible, la tentation est grande de fourrer dedans un peu tout pour avoir une espèce de boîte à outils réutilisable.
En réalité, il faut essayer de séparer le contenu des rôles par objet technique : un rôle “MongoDB”, un rôle “HAProxy”, un rôle “Tomcat” et utiliser ensuite la glue fournie par les playbooks pour faire l’assemblage simplement et rapidement.
Cette séparation vous permet de regrouper au sein du même rôle toutes les tâches qui peuvent être effectuées pour le composant concerné : l’installation, la configuration, le démarrage, l’arrêt, la maintenance, la mise à jour… et seulement de ce composant.
Si votre code doit toucher à un autre composant technique, essayez de le mettre dans un autre rôle dès que possible. Et si vous tenez absolument à orchestrer ces tâches depuis un rôle, écrivez un rôle transverse qui va faire appel aux différentes étapes de vos rôles techniques.
L’objectif premier est de bien séparer les actions qui sont effectuées dans un rôle donné, diminuant ainsi ses dépendances, et le nombre de variables dont il a besoin. Le second objectif est bien évidemment la ré-utilisabilité, qui est primordiale, pour tout code.
Utiliser la syntaxe YAML et pas la syntaxe Ansible
Ansible vous permet d’utiliser un mélange entre deux syntaxes lorsque vous rédigez votre code. Vous pouvez soit utiliser du YAML pur :
- name: add user testuser1
user:
name: testuser1
state: present
groups: wheel
Ou vous pouvez aussi utiliser la syntaxe hybride YAML et Ansible :
- name: add user testuser1 user: name=testuser1 state=present groups=wheel
Ces deux exemples sont rigoureusement identiques en terme de description Ansible.
- Dans le premier cas, Ansible parse tout en YAML puis exécute le code.
- Dans le second cas, Ansible va en plus parser name=testuser1 state=present groups=wheel avant d’appeler la tâche user.
Cette syntaxe est pratique, mais elle oblige à une certaine gymnastique avant d’arriver à faire fonctionner une tâche, surtout quand celle-ci est complexe.
La bonne pratique consiste à toujours utiliser la syntaxe YAML pour éviter la syntaxe hybride avec les =: elle permet une détection d’erreurs un peu plus tôt, et augmente la lisibilité de votre code Ansible.
Espaces et commentaires
L’utilisation généreuse des espaces pour dissocier les choses et l’utilisation de commentaires (commençant par «#») sont encouragées ! (Vraiment !)
Toujours mentionner l'état : state
Le paramètre ‘state’ est facultatif pour beaucoup de modules. Qu'il s'agisse de ‘state=present’ ou de ‘state=absent’, il est toujours préférable de laisser ce paramètre dans vos playbooks pour le rendre clair, d'autant plus que certains modules prennent en charge des états supplémentaires.
Nommez toujours les tâches
Il est possible de ne pas indiquer le nom pour une tâche donnée, bien qu'il soit recommandé de fournir une description de la raison pour laquelle quelque chose est fait.
Ce nom est affiché lorsque le livre de lecture est exécuté.
Documenter les variables
Ansible supporte la surcharge de variables suivant l’endroit où vous les déclarez.
Dans la documentation officielle, on explique la priorité des déclarations des variables comme suit :
role defaults [1] inventory vars [2] inventory group_vars inventory host_vars playbook group_vars playbook host_vars host facts play vars play vars_prompt play vars_files registered vars set_facts role and include vars block vars (only for tasks in block) task vars (only for the task) extra vars (always win precedence)
Comme on peut le voir, la liste est particulièrement fournie et vous permet de surcharger une variable n’importe où, grosso modo.
Du coup, il faut faire attention :
- à ne pas trop disperser les déclarations de variables dans le code que vous utilisez. Évidemment, si c’est vous qui écrivez tout votre code, c’est plus facile à faire que si vous utilisez le code commis par d’autres (au hasard, en récupérant du code depuis Galaxy)
- à documenter précisément les variables que vous déclarez dans vos rôles, ou ailleurs.
Variation du système d'exploitation et de la distribution
Lorsque vous traitez avec un paramètre différent entre deux systèmes d'exploitation différents, utilisez le module group_by pour le gérer.
---
- name: talk to all hosts just so we can learn about them
hosts: all
tasks:
- name: Classify hosts depending on their OS distribution
group_by:
key: os_{{ ansible_facts['distribution'] }}
# now just on the CentOS hosts...
- hosts: os_RedHat
gather_facts: False
tasks:
- # tasks that only happen on RedHat go here
Cela renverra tous les systèmes dans un groupe dynamique basé sur le nom du système d'exploitation.
Si des paramètres spécifiques à un groupe sont nécessaires, vous pouvez également le faire.
Par exemple:
--- # file: group_vars/all asdf: 10 --- # file: group_vars/os_RedHat asdf: 42
Dans l’exemple ci-dessus, les machines RedHat obtiennent la valeur «42» pour asdf, mais les autres machines obtiennent «10». Cela peut être utilisé non seulement pour définir des variables, mais également pour appliquer certains rôles à certains systèmes uniquement.
Sinon, si seules des variables sont nécessaires:
- hosts: all
tasks:
- name: Set OS distribution dependant variables
include_vars: "os_{{ ansible_facts['distribution'] }}.yml"
- debug:
var: asdf
Cela fournira des variables basées sur le type d'OS.
Données sensibles dans une sortie accessible
L'accent mis sur les meilleures pratiques de sécurité d'Ansible est également une préoccupation importante pour les utilisateurs. Les utilisateurs ne doivent pas exposer de données sensibles dans les sorties standard. Si vous utilisez le module “template-” et que le fichier contient des mots de passe et d'autres données sensibles, vous ne voudriez pas qu'ils figurent dans la sortie.
Dans ce cas, vous pouvez utiliser l'option “no_log”. En ajoutant l'option “no_log-” à une tâche, il n'y aura pas de journal de la sortie.
Prenons par exemple le livre de jeu suivant,
- Sans le “no_log : true”, vous pouvez trouver le résultat suivant,
TASK [This fails] ********************************************** fatal: [127.0.0.1]: FAILED! => {"changed": true, "cmd": "echo This is my secret password && false", "delta": "000.003950", "end": "2019-09-27 1452.194098", "msg": "non-zero return code", "rc": 1, "start": "2019-09-27 1452.190148", "stderr": "", "stderr_lines": [], "stdout": "This is my secret password", "stdout_lines": ["This is my secret password"]}
- Si le paramètre “no_log : true” est activé, vous pouvez obtenir la sortie suivante,
TASK [This fails] *********************************************** fatal: [127.0.0.1]: FAILED! => {"censored": "the output has been hidden due to the fact that 'no_log: true' was specified for this result", "changed": true}
Vous pouvez également utiliser la “ansible-vault” pour la sécurité des données sensibles dans le playbook et les rôles.