Ansible - Un premier playbook

Découverte Ansible - Partie 3

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 ou failed 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.