Ansible - Un premier playbook
Troisième partie consacrée à l’outil Ansible, nous allons attaquer l’écriture et l’exécution de playbooks.
Un premier playbook
Nous avons pas mal parlé des exécutions ad hoc d’Ansible permettant de lancer unitairement un module et obtenir le résultat. Les Playbooks ont pour intérêt d’écrire un scénario complet d’exécution de plusieurs modules avec des conditions et des critères de succès/échec et des actions selon l’évènement déclenché. Les playbooks sont écrits en YAML et utilisent un langage déclaratif simple propre à Ansible.
Un playbook est constitué à minima de deux instructions : les hosts sur qui jouer et la tâche à exécuter. Les hosts sont soit un des groupes définis dans votre inventaire (comme nous avons vu dans le précédent billet), soit la totalité de vos hosts avec l’instruction all
.
Prenons un exemple simple d’un playbook installant le serveur web Apache en dernière version, déployant une configuration voulue et redémarrant le service si nécessaire. (exemple issu de la documentation officielle Ansible)
---
- hosts: webservers
vars:
http_port: 80
max_clients: 200
remote_user: root
tasks:
- name: ensure apache is at the latest version
yum:
name: httpd
state: latest
- name: write the apache config file
template:
src: /srv/httpd.j2
dest: /etc/httpd.conf
notify:
- restart apache
- name: ensure apache is running
service:
name: httpd
state: started
handlers:
- name: restart apache
service:
name: httpd
state: restarted
Un playbook peut contenir des exécutions pour différents hosts, exemple sur une installation de serveur web et base de données.
---
- hosts: webservers
remote_user: root
tasks:
- name: ensure apache is at the latest version
yum:
name: httpd
state: latest
- name: write the apache config file
template:
src: /srv/httpd.j2
dest: /etc/httpd.conf
- hosts: databases
remote_user: root
tasks:
- name: ensure postgresql is at the latest version
yum:
name: postgresql
state: latest
- name: ensure that postgresql is started
service:
name: postgresql
state: started
Un playbook vide entraînera une erreur Ansible indiquant qu’il n’a pas trouvé d’action à faire.
Détaillons les instructions.
Déclaration hosts et utilisateurs
Un playbook commence toujours par le groupe de hosts sur lequel il doit agir. Cela peut être all
si celui-ci doit attaquer la totalité des hosts de l’inventaire. On peut ensuite lui indiquer l’utilisateur distant avec lequel l’action sera déclenchée. A noter que si le remote_user
n’est pas spécifié, il se connectera avec le profil courant qui exécute la commande ansible-playbook
.
---
- hosts: webservers
remote_user: root
Dans le cas où l’utilisateur distant n’a pas assez de pouvoir, on peut lui spécifier qu’il doit faire une escalade de privilèges si cet utilisateur a les droits suffisants.
---
- hosts: webservers
remote_user: jeanmichel
become: yes
Par défaut, l’instruction become
passera en root. On peut spécifier un utilisateur particulier.
---
- hosts: webservers
remote_user: jeanmichel
become: yes
become_user: maurice
Les actions become
et become_user
peuvent être spécifiées au niveau d’une tâche pour ne s’appliquer qu’à celle-ci.
---
- hosts: webservers
remote_user: jeanmichel
tasks:
- service:
name: httpd
state: started
become: yes
Déclaration des tâches
La liste des tâches d’un playbook commence par l’instruction tasks
. Les tâches sont des appels de modules Ansible, configurés avec différentes options variant selon les modules.
Exemple de tâche démarrant Apache HTTPD.
tasks:
service:
name: httpd
state: started
Une bonne pratique est de toujours nommer ses tâches avec l’instruction name
pour avoir une meilleure lisibilité.
tasks:
- name: "Start httpd"
service:
name: httpd
state: started
Si les modules ont leur propres arguments, les tâches peuvent avoir une liste de paramètres généraux :
- tags : permet d’attribuer un libellé à une tâche pour cibler ou exclure celle-ci lors de l’exécution du playbook
- become : activer l’élévation de privilège
- become_user: exécuter en tant que le user spécifié
- ignore_errors : permet d’ignorer une éventuelle erreur
- changed_when / failed_when : permet de conditionner le changement d’état
changed
oufailed
de la tâche (pratique pour une tâche shell arbitraire par exemple) - environment : pour charger des variables d’environnement
- notify : déclenche un handler (voir après)
- …
La liste est non exhaustive, j’ai indiqué les plus courants dans mon utilisation.
Les handlers pour exécuter une action sur changement
Les handlers sont une action qui se déclenche sur un état changed
d’une tâche. Ils sont appelés par l’instruction notify
d’une tâche à laquelle on indique le name
du handler.
Exemple sur une tâche rechargeant le serveur Apache HTTPD sur mise à jour de config.
tasks:
- name: "Deploy httpd configuration"
template:
src: etc_httpd_conf_httpd.conf.j2
dest: /etc/httpd/conf/httpd.conf
notify:
- reload httpd
# handler pour prise en charge du reload
handlers:
- name: "reload httpd"
service:
name: httpd
state: reloaded
Si le template httpd.conf a modifié le fichier sur le host distant, il notifiera au handler “reload httpd” de s’exécuter. Les handlers sont exécutés après les tâches du playbook, indiqués par l’action “HANDLERS” à la fin de l’exécution généralement.
Il est possible de mettre plusieurs handlers à une tâche. A noter que ceux-ci seront exécutés dans l’ordre où sont écrits dans le playbook et non dans l’ordre où ils sont listés sur la tâche.
Bien que plusieurs tâches peuvent notifier le même handler (exemple : on déploie la configuration httpd.conf + plusieurs vhosts avec plusieurs tâches séparées), celui-ci ne sera joué qu’une seule fois.
Conclusion
Le Playbook est l’un des principaux éléments avec l’inventaire pour utiliser Ansible. Vous pouvez rédiger des playbooks faisant de nombreuses actions avec beaucoup de conditions, mais cela ne sera pas forcément la méthode la plus efficiente. Dans le prochain billet, nous verrons les rôles Ansible, un moyen d’avoir du code réutilisable et pouvant être orchestré via les Playbooks.