15 KiB
Playbooks Ansible
Jouer et rejouer facilement un jeu de commandes prédéfini sur un ensemble de machines.
Format du Playbook
-
Il se présente sous la forme d'un simple fichier texte au format
yaml
. -
Il peut donc être facilement versionné dans un outil de gestion de version.
yaml
-
Format ouvert de représentation de données.
-
Acronyme récursif de YAML Ain't Markup Language.
-
Permet de représenter des données complexes tout en conservant une excellente lisibilité.
-
Utilisation en forte progression ces dernières années.
Ansible, GitLab, Docker Compose, Kubernetes manifests, etc... -
Site officiel : https://yaml.org/
YAML basics
https://fr.wikipedia.org/wiki/YAML
-
attention ! l'indentation se fait avec un ou plusieurs espaces, jamais avec des tabulations !
-
Les commentaires sont signalés par le signe dièse
#
et se prolongent sur toute la ligne. -
Les éléments de listes sont dénotés par le tiret
-
, suivi d'une espace, à raison d'un élément par ligne. -
Les tableaux sont de la forme
clé: valeur
, à raison d'un couple par ligne.
YAML basics
https://fr.wikipedia.org/wiki/YAML
-
Les chaînes de caractères peuvent être entourées de guillemets doubles
"
, ou simples'
, sachant qu'un guillemet s'échappe avec un antislash\
, alors qu'une apostrophe s'échappe avec une autre apostrophe. -
Les chaînes de caractères peuvent de plus être représentées par un bloc indenté avec des modificateurs facultatifs pour conserver
|
ou éliminer>
les retours à la ligne. -
Plusieurs documents rassemblés dans un seul fichier sont séparés par trois traits d'union
---
.
YAML basics
https://fr.wikipedia.org/wiki/YAML
---
receipt: Oz-Ware Purchase Invoice
date: 2012-08-06
customer:
given: Dorothy
family: Gale
items:
- part_no: A4786
descrip: Water Bucket (Filled)
price: 1.47
quantity: 4
- part_no: E1628
descrip: High Heeled "Ruby" Slippers
size: 8
price: 100.27
quantity: 1
bill-to: &id001
street: |
123 Tornado Alley
Suite 16
city: East Centerville
state: KS
ship-to: *id001
specialDelivery: >
Follow the Yellow Brick
Road to the Emerald City.
Pay no attention to the
man behind the curtain.
...
yaml - Définition d’une collection (-)
# Une liste de fruits
fruits:
- pomme
- orange
- framboise
- mangue
Forme abrégée :
# Une liste de fruits
fruits: ['pomme', 'orange', 'framboise', 'mangue']
Définition d’un dictionnaire (key: value)
# Un utilisateur
martin:
name: Martin Dupond
job: developer
skill: python
Forme abrégée :
# Un utilisateur
martin: { name: Martin Dupond, job: developer, skill: python }
Tasks
-
Les commandes d'un Playbook sont découpées en instructions unitaires appelées tâches (tasks).
-
Chaque tâche exécute un module Ansible avec des paramètres spécifiques.
-
Format d'une tâche :
- name: Description de la tâche
<nom-du-module>:
<paramètre-1>: <valeur-1>
<paramètre-2>: <valeur-2>
<paramètre-3>: <valeur-3>
Liste de tâches
-
Un playbook peut décrire une liste de plusieurs tâches.
-
Les tâches seront exécutées dans l'ordre d'apparition et de façon séquentielle sur chacune des machines cibles.
tasks: <----------------------------- liste de tâche
- name: Ma tâche 1 <--------------- tâche 1
<nom-du-module>:
<paramètre-1>: <valeur-1>
<paramètre-2>: <valeur-2>
<paramètre-3>: <valeur-3>
- name: Ma tâche 2 <--------------- tâche 2
<nom-du-module>:
<paramètre-1>: <valeur-1>
<paramètre-2>: <valeur-2>
Exemple réel
- hosts: web # exécution d'un 'Play' sur le groupe 'web'
tasks:
- name: Installation of Apache Package # tâche 1
yum:
name: httpd
state: present
update_cache: yes
- name: Ensure Apache is running (and enabled at boot) # tâche 2
service: name=httpd state=started enabled=yes
- Ici on mixe les deux types de syntaxes (normale et abrégée).
- Les bonnes pratiques préconisent l'utilisation de la syntaxe normale.
Exemple réel
avec syntaxe normale
- hosts: web
tasks:
- name: Installation of Apache Package
yum:
name: httpd
state: present
update_cache: yes
- name: Ensure Apache is running (and enabled at boot)
service:
name: httpd
state: started
enabled: yes
Notion de Play
---
- hosts: webservers # Play 1 sur le groupe webservers
tasks:
- name: My task
...
- hosts: databases # Play 2 sur le groupe databases
tasks:
- name: My task...
...
- Chaque Play contient sa propre liste de tâches et cible un ensemble spécifique de machines.
Lancer un playbook
$ ansible-playbook <fichier-playbook> -i <fichier-inventaire>
$ ansible-playbook playbook.yaml -i ./hosts
PLAY [web] *******************************************************************
TASK [setup] *****************************************************************
ok: [web1.formation.sii.fr]
ok: [web2.formation.sii.fr]
TASK [Installation du package Apache] ****************************************
changed: [web2.formation.sii.fr]
changed: [web1.formation.sii.fr]
TASK [Ensure Apache is running (and enable it at boot)] **********************
changed: [web1.formation.sii.fr]
changed: [web2.formation.sii.fr]
PLAY RECAP *******************************************************************
web1.formation.sii.fr : ok=3 changed=2 unreachable=0 failed=0
web2.formation.sii.fr : ok=3 changed=2 unreachable=0 failed=0
* Notre Playbook comportait 2 tâches seulement, nous en voyons 3 !
- Ansible a automatiquement ajouté à l'exécution une tâche nommée setup dont l'objectif est de récupérer les facts des machines cibles.
Récapitulatif du Playbook
PLAY RECAP *******************************************************************
web1.formation.sii.fr : ok=3 changed=2 unreachable=0 failed=0
web2.formation.sii.fr : ok=3 changed=2 unreachable=0 failed=0
-
Sur les 2 machines cibles :
ok
: 3 tâches ont été exécutées avec succès.changed
: 2 tâches ont modifié l'état du système.unreachable
: Toutes les machines étaient joignables.failed
: Aucune tâche n'a échoué.
Relancer un playbook
$ ansible-playbook playbook.yaml -i ./hosts --become
PLAY [web] *******************************************************************
TASK [setup] *****************************************************************
ok: [web1.formation.sii.fr]
ok: [web2.formation.sii.fr]
TASK [Installation du package Apache] ****************************************
ok: [web1.formation.sii.fr]
ok: [web2.formation.sii.fr]
TASK [Ensure Apache is running (and enable it at boot)] **********************
ok: [web1.formation.sii.fr]
ok: [web2.formation.sii.fr]
PLAY RECAP *******************************************************************
web1.formation.sii.fr : ok=3 changed=0 unreachable=0 failed=0
web2.formation.sii.fr : ok=3 changed=0 unreachable=0 failed=0
Première exécution
PLAY RECAP *******************************************************************
web1.formation.sii.fr : ok=3 changed=2 unreachable=0 failed=0
web2.formation.sii.fr : ok=3 changed=2 unreachable=0 failed=0
Seconde exécution
PLAY RECAP *******************************************************************
web1.formation.sii.fr : ok=3 changed=0 unreachable=0 failed=0
web2.formation.sii.fr : ok=3 changed=0 unreachable=0 failed=0
Ordre d'exécution
-
Il est possible de contrôler l'ordre dans lequel les machines cibles sont adressées.
-
L'ordre par défaut est l'ordre d'apparition dans l'inventaire.
- hosts: all
order: sorted # l'ordre est défini ici
gather_facts: False
tasks:
- debug:
var: inventory_hostname
Ordre | Description
-
| -
inventory
| Ordre d'apparition dans l'inventaire. C'est le choix par défaut.
reverse_inventory
| Ordre inverse d'apparition dans l'inventaire.
sorted
| Ordre alphabétique des noms de machines.
reverse_sorted
| Ordre alphabétique inverse des noms de machines.
shuffle
| Ordre aléatoire.
Démarrer l'exécution à un endroit précis
$ ansible-playbook playbook.yaml --start-at-task="my task"
Cette commande démarre l'exécution du playbook à partir de la tâche nommée my task
.
https://docs.ansible.com/ansible/latest/user_guide/playbooks_startnstep.html
Ignorer le code de retour d'une commande
-
Les modules command et shell sont sensibles au code de retour des commandes.
-
Pour ignorer les erreurs sur une commande qui renvoie un code > 0 on peut utiliser
ignore_errors
. -
Ou, utiliser
failed_when
. -
De même,
changed_when
peut être utilisé.
tasks:
- name: run this command and ignore the result
shell: /usr/bin/somecommand
ignore_errors: True
Déclaration de variables
- hosts: web
vars:
- app_directory: /var/www/html
- app_user: apache
- app_group: apache
tasks:
- name: Modify permission on {{ app_directory }}
file:
dest: '{{ app_directory }}'
mode: 0755
owner: '{{ app_user }}'
group: '{{ app_group }}'
recurse: yes
Les variables sont déclarées dans vars
et résolues avec {{ }}
.
$ ansible-playbook playbook.yaml -i ./hosts
PLAY [web] ****************************************************************
TASK [setup] **************************************************************
ok: [web1.formation.sii.fr]
ok: [web2.formation.sii.fr]
TASK [Modify permission of directory /var/www/html] ***********************
changed: [web1.formation.sii.fr]
changed: [web2.formation.sii.fr]
PLAY RECAP ****************************************************************
web1.formation.sii.fr : ok=2 changed=1 unreachable=0 failed=0
web2.formation.sii.fr : ok=2 changed=1 unreachable=0 failed=0
Les handlers et les notify
- hosts: web
vars:
- apache_listen_port: 8080
tasks:
- name: Modify Apache configuration
lineinfile:
dest: /etc/httpd/conf/httpd.conf
regexp: '^Listen '
line: 'Listen {{ apache_listen_port }}'
notify: Reload Apache # Signale que la configuration
# d'Apache doit être rechargée
handlers:
- name: Reload Apache # Recharge la configuration
service:
name: httpd
state: reloaded
$ ansible-playbook playbook.yaml -i ./hosts
PLAY [web] *******************************************************************
TASK [setup] *****************************************************************
ok: [web1.formation.sii.fr]
ok: [web2.formation.sii.fr]
TASK [Modify Apache configuration] *******************************************
changed: [web1.formation.sii.fr]
changed: [web2.formation.sii.fr]
RUNNING HANDLER [Reload Apache] *********************************************
changed: [web1.formation.sii.fr]
changed: [web2.formation.sii.fr]
PLAY RECAP *******************************************************************
web1.formation.sii.fr : ok=3 changed=2 unreachable=0 failed=0
web2.formation.sii.fr : ok=3 changed=2 unreachable=0 failed=0
Idempotence
-
L'idempotence signifie qu'une opération a le même effet qu'on l'applique une ou plusieurs fois.
-
Les commandes d'un Playbook doivent être écrites de manière à produire le même résultat quel que soit le nombre de fois où elles sont exécutées sur une même cible.
Travaux pratiques
Variables sur la ligne de commande
Il est possible d'initialiser des variables directement sur la ligne de commande avec l'option --extra-vars
(ou -e
).
-
Ces variables peuvent être définies sous la forme :
- chaîne de caractères
$ ansible-playbook playbook.yaml --extra-vars "my_var_1=foo my_var_2=bar"
- json
$ ansible-playbook playbook.yaml --extra-vars '{"my_var_1":"foo","my_var_2":"bar"}'
$ ansible-playbook playbook.yaml --extra-vars '{"my_var":"foo","my_list":["foo","bar"]}'
-
Utilisez le fomat json si vous voulez passer autre chose que des strings :
- booleans
- integers
- floats
- lists
- ...
Découper un Playbook
-
Un Playbook peut se présenter sous la forme d'un fichier unique.
-
Toutefois il est possible de le découper en plusieurs fichiers séparés afin de mieux organiser et favoriser la ré-utilisation de certaines parties.
-
Il existe plusieurs manières de découper un Playbook :
les includes, les imports, et les roles.
https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse.html
Includes et Imports
-
Disponibles à partir de Ansible v2.4.
-
Permettent le découpage des tâches d'un gros Playbook en fichiers plus petits.
-
Ces fichiers peuvent ensuite être appelés :
-
depuis un ou plusieurs Playbooks
-
plusieurs fois dans un même Playbook.
-
Import dynamique vs statique
-
Les commandes
import
permettent
un chargement statique.
(import_playbook, import_tasks, etc.) -
Les commandes
include
permettent
un chargement dynamique.
(include_tasks, include_role, etc.)
Statique vs Dynamique
-
Statique
Ansible traite les imports statique au moment de l'analyse du Playbook (avant l'exécution). -
Dynamique
Ansible traite les imports dynamiques au fur et à mesure durant l'exécution du Playbook. -
Les imports statiques et dynamiques peuvent être mixés, toutefois cela n'est pas recommandé car cela rend le debug des Playbooks plus complexe.
Import de Playbooks
- Il est possible d'importer un ou plusieurs Playbooks à l'intérieur d'un Playbook maître, avec
import_playbook
.
Roles
-
Plus puissants que les includes et les imports.
-
Permettent d'empaqueter un ensemble de tâches ainsi que les variables, handlers et autres autres éléments associés.
-
Les roles peuvent être facilement ré-utilisés et partagés.