diff --git a/HowtoAnsible.md b/HowtoAnsible.md index 48029827..972a5862 100644 --- a/HowtoAnsible.md +++ b/HowtoAnsible.md @@ -24,17 +24,19 @@ Nous utilisons actuellement Ansible 2.0.2 (disponible via > ~/.ansible/hosts -$ ssh-copy-id HOSTNAME +Exemples d'utilisation basique : -$ ansible HOSTNAME -i $HOME/.ansible/hosts -m ping -HOSTNAME | SUCCESS => { - "changed": false, +~~~ +$ ansible localhost --module ping +localhost | SUCCESS => { + "changed": false, "ping": "pong" } -$ ansible HOSTNAME -a "date" +$ echo HOSTNAME >> ~/.ansible/hosts && ssh-copy-id HOSTNAME && ansible HOSTNAME -i $HOME/.ansible/hosts --module ping --one-line +HOSTNAME | SUCCESS => {"changed": false, "ping": "pong"} + +$ ansible HOSTNAME --module command --args "date" HOSTNAME | SUCCESS | rc=0 >> jeudi 26 mai 2016, 23:16:01 (UTC+0200) @@ -70,106 +76,24 @@ Options utiles pour [ansible-playbook](https://manpages.debian.org/cgi-bin/man.c * `-l` / `--limit HOSTNAME` : limite la connexion à un ou plusieurs serveurs (attention, par défaut c'est *all*, cf `/etc/ansible/hosts`) * `-f` / `--forks N` : nombre de process lancés en parallèle (par défaut 5)… peut être utile de mettre à 1 pour ne pas paralléliser + ## Les éléments d'Ansible -L'utilisation d'Ansible se fait principalement avec un **playbook**. C'est l'élément clé, qui peut suffire à réaliser des tâches utiles. Il s'agit d'un fichier qui décrit la succession d'une ou plusieurs séries d'actions (des *plays*). Chaque **play** indique quelques réglages généraux comme le groupe de serveurs concernés, la manière de devenir administrateur, quels fichiers de variables charger et surtout des actions. +L'élément de base d'Ansible est le [module](#modules) : on peut exécuter une tâche (installation de paquets, copie de fichiers, etc.) en exécutant simplement `ansible --module`. +Pour regrouper plusieurs tâches, on utilise un [playbook](#playbook) : un fichier en syntaxe YAML qui va lister une succession de modules avec des arguments. +Au sein d'un playbook, on dispose d'options pratiques comme les [handlers](#handlers) : ils permettent le déclenchement d'une commande sous certaines conditions (redémarrage d'un service par exemple). +Si l'on veut organiser de façon poussée les différentes tâches, on utilisera des [roles](#roles) : il s'agit simplement d'inclure dans un playbook des fichiers YAML respectant une structure conventionnelle. +Enfin, pour s'exécuter sur un ensemble de machines, Ansible a besoin d'un [inventory](#inventory) : c'est la liste des serveurs potentiellement concernés. -Un playbook va ensuite dérouler des _actions_ qui seront organisées en _roles_, _tasks_ et _handlers_. - -Une **action** est l'invocation d'une fonctionnalité particulière fournie par un module. Exemple, pour s'assurer de l'absence d'un fichier : - -~~~ -- file: path=/etc/cron.daily/apticron state=absent -~~~ - -Les actions peuvent être listées dans la partie **tasks** du playbook et peuvent être aussi déportées dans un fichier et inclus dans le playbook. - -Les **handlers** sont des actions qui ne sont exécutées que si une autre action en a fait la demande, via l'attribut `notify`. - -Lorsqu'on a besoin d'utiliser des fichiers ou _templates_ à copier, des variables avec des valeurs par défaut, des handlers… on peut organiser tout cela dans un **role** en respectant la structure conventionnelle suivante : - -~~~ -foo -├── defaults -│   └── main.yml -├── files -├── handlers -│   └── main.yml -├── meta -│   └── main.yml -├── README.md -├── tasks -│   └── main.yml -├── templates -├── tests -│   ├── inventory -│   └── test.yml -└── vars - └── main.yml -~~~ - -La partie **inventory** correspond à la description de l'inventaire des serveurs à configurer et inclus un mécanisme de configuration individuelle et par groupe. Nous y revenons plus loin. - -## Howto Playbook - -Exemple de playbook très simple : - -~~~{.yaml} ---- -- hosts: all - - tasks: - - shell: echo hello World - -# vim:ft=ansible: -~~~ - -Un playbook plus complexe (avec un seul "play") : - -~~~{.yaml} ---- -- hosts: all - gather_facts: yes - become: yes - - vars_files: - - 'vars/main.yml' - - vars: - external_roles: ~/GIT/ansible-roles - external_tasks: ~/GIT/ansible-public/tasks - - pre_tasks: - - name: Minifirewall is stopped (temporary) - service: - name: minifirewall - state: stopped - - roles: - - "{{ external_roles }}/minifirewall" - - post_tasks: - - include: "{{ external_tasks }}/commit_etc_git.yml" - vars: - commit_message: "Ansible run firewall.yml" - - handlers: - - name: restart minifirewall - service: - name: minifirewall - state: restarted - -# vim:ft=ansible: -~~~ - - -### Modules pour tasks +### Modules -Pour avoir la liste des modules utilisables dans *tasks* : `ansible-doc -l` +Le module est le garant de l'idempotence : Ansible gérera le succès de l'exécution et indiquera si une vérification ou action concrète a été effectuée. (TODO: à enrichir). -Voici quelques exemples : +Pour avoir la liste des modules utilisables : `ansible-doc -l` + +Voici quelques exemples de modules que nous utilisons : * Module [command](http://docs.ansible.com/ansible/command_module.html) : @@ -334,7 +258,166 @@ Le fonctionnement exacte de `replace` et de `lineinfile` est potentiellement dé * avec le module `lineinfile`, si l'on veut utiliser une référence (`\1`) dans `line`, ça donne une erreur, il faut utiliser `replace` * avec le module `lineinfile`, l'argument `backrefs: yes` sert à utiliser une référence au sein de l'argument `regexp` (et non pas au sein de l'argument `line`). -### vars +### playbook + +Un playbook va ensuite dérouler des _actions_ qui seront organisées en _roles_, _tasks_ et _handlers_. + +Exemple de playbook très simple : + +~~~{.yaml} +--- +- hosts: all + + tasks: + - shell: echo hello World + +# vim:ft=ansible: +~~~ + +Un playbook plus complexe : + +~~~{.yaml} +--- +- hosts: all + gather_facts: yes + become: yes + + vars_files: + - 'vars/main.yml' + + vars: + external_roles: ~/GIT/ansible-roles + external_tasks: ~/GIT/ansible-public/tasks + + pre_tasks: + - name: Minifirewall is stopped (temporary) + service: + name: minifirewall + state: stopped + + roles: + - "{{ external_roles }}/minifirewall" + + post_tasks: + - include: "{{ external_tasks }}/commit_etc_git.yml" + vars: + commit_message: "Ansible run firewall.yml" + + handlers: + - name: restart minifirewall + service: + name: minifirewall + state: restarted + +# vim:ft=ansible: +~~~ + +### handlers + +Les **handlers** ne sont exécutées que si une action a fait la demande, via la directive `notify` disponible pour tous les modules. + +L'enregistrement du handler se fait seulement lorsque l'action a provoqué un changement. +L'exécution effective du handler se fait **une seule fois** en fin de "play", quel que soit le nombre de fois où il a été demandé pendant l'exécution. + +Les handlers servent le plus souvent à redémarrer des services. Exemple : + +~~~{.yaml} +tasks: +- name: copy Apache configuration + copy: (…) + notify: Restart Apache + +handlers: +- name: Restart Apache + service: + name: apache2 + state: restarted +~~~ + +Dans des rôles longs, nous conseillons de purger les handlers de temps en temps (en fin de groupe d'action). En effet, si un playbook est interrompu les handlers ne sont pas forcément exécutés alors que l'action qui les a déclenchés a bien eu lieu. On insère alors l'action suivante : + +~~~{.yaml} +- meta: flush_handlers +~~~ + +### roles + +Lorsqu'on a besoin d'utiliser des fichiers ou _templates_ à copier, des variables avec des valeurs par défaut, des handlers… on peut organiser tout cela dans un **role** en respectant la structure conventionnelle suivante : + +~~~ +foo +├── defaults +│   └── main.yml +├── files +├── handlers +│   └── main.yml +├── meta +│   └── main.yml +├── README.md +├── tasks +│   └── main.yml +├── templates +├── tests +│   ├── inventory +│   └── test.yml +└── vars + └── main.yml +~~~ + +### inventory + + + +La partie **inventory** correspond à la description de l'inventaire des serveurs à configurer et inclus un mécanisme de configuration individuelle et par groupe. + +Permet d'indiquer la liste des machines concernées par Ansible (peut être limité lors de l'exécution de la commande par l'option `-l`) et de pouvoir les regrouper dans des groupes. + +Exemple: + +~~~ +hostname.internal + +[httpservers] +machine[01:57].example.com +http.example.com:2222 + +[dbservers] +machine12.example.com +machine50.example.com +m[a:o]chine52.example.com +alias ansible_port=2222 ansible_host=192.168.1.50 + +[client] +host1 http_port=80 maxRequestsPerChild=808 # des variables qui seront automatiquement auto-completées liées à cet host + +[commercant] +mercerie +chapeautier + +[commercant:vars] +ntp_server=ntp.mercerie.example.com +proxy=proxy.mercerie.example.com +~~~ + +* `hostname.internal` : serveur présent dans aucun groupe +* `[httpservers]` : le nom du groupe (pour les serveurs http). Les noms de hosts qui suivent appartiendront à ce groupe +* `machine[01:57].evolix.net` : on peut indiquer une [pseudo-]expression régulière - ici ajoutera les machines _machine01.evolix.net_, _machine02.evolix.net_, _machine03.evolix.net_… _machine57.evolix.net_ +* `http.evolix.net:2222` : ansible se connecte par ssh, et _http.evolix.net_ a un port SSH d'écoute différent qui est 2222 +* `[dbservers]` : groupe pour les serveurs de base de données +* `machine50.example.com` : cette machine est déjà présente dans le groupe _httpservers_, mais sera aussi accessible à partir du groupe _dbservers_ +* `alias ansible_port=2222 ansible_host=192.168.1.50` : la machine _alias_ n'a pas un vrai FQDN mais pointera vers _192.168.1.50_ car on a indiqué des variables propres à Ansible. Il est existe aussi `ansible_connection` (local ou ssh) ou `ansible_user` (le nom de l'utilisateur de la machine distante avec lequel Ansible se connectera en ssh) +* `host1 http_port=80 maxRequestsPerChild=808` : des variables qui seront automatiquement disponibles pour les actions sur _host1_ +* `[commercant:vars]` : des variables qui seront liées au groupe _commercant_. + +On peut aussi créer des groupes de groupes en utilisant `:children` + +On peut aussi découper le fichier "inventory" selon les groupes et les variables : + +Les variables propres à Ansible : + + + +### variables Les variables sont un élément clé de la configuration des playbooks et roles. Exemple : @@ -380,34 +463,6 @@ Pour gérer de nombreuses variables dans un projet, on peut stocker toutes celle Les groupes sont définis dans le fichier d'[inventaire](http://docs.ansible.com/ansible/intro_inventory.html). -### Handlers - -La directive `notify`, disponible pour tous les modules, permet de déclencher un `handler`. - -L'enregistrement du handler se fait seulement lorsque l'action a provoqué un changement. -L'exécution effective du handler se fait **une seule fois** en fin de "play", quel que soit le nombre de fois où il a été demandé pendant l'exécution. - -Les handlers servent le plus souvent à redémarrer des services. Exemple : - -~~~{.yaml} -tasks: -- name: copy Apache configuration - copy: (…) - notify: Restart Apache - -handlers: -- name: Restart Apache - service: - name: apache2 - state: restarted -~~~ - -Dans des rôles longs, il est conseillé de purger les handlers de temps en temps (en fin de groupe d'action). En effet, si un playbook est interrompu les handlers ne sont pas forcément exécutés alors que l'action qui les a déclenchés a bien eu lieu. On insère alors l'action suivante : - -~~~{.yaml} -- meta: flush_handlers -~~~ - ### Tags @@ -457,6 +512,7 @@ Il est possible de consulter le contenu détaillé de la variable avec `debug` : Pour certains modules, `register` est presque un passage obligatoire pour une utilisation cohérente des éléments (stat…). + ## Configuration @@ -468,56 +524,6 @@ La configuration est lue dans l'ordre suivant : * `~/.ansible.cfg` * `/etc/ansible/ansible.cfg` - -### Fichier "inventory" - - - -Permet d'indiquer la liste des machines concernées par Ansible (peut être limité lors de l'exécution de la commande par l'option `-l`) et de pouvoir les regrouper dans des groupes. - -Exemple: - -~~~ -hostname.internal - -[httpservers] -machine[01:57].example.com -http.example.com:2222 - -[dbservers] -machine12.example.com -machine50.example.com -m[a:o]chine52.example.com -alias ansible_port=2222 ansible_host=192.168.1.50 - -[client] -host1 http_port=80 maxRequestsPerChild=808 # des variables qui seront automatiquement auto-completées liées à cet host - -[commercant] -mercerie -chapeautier - -[commercant:vars] -ntp_server=ntp.mercerie.example.com -proxy=proxy.mercerie.example.com -~~~ - -* `hostname.internal` : serveur présent dans aucun groupe -* `[httpservers]` : le nom du groupe (pour les serveurs http). Les noms de hosts qui suivent appartiendront à ce groupe -* `machine[01:57].evolix.net` : on peut indiquer une [pseudo-]expression régulière - ici ajoutera les machines _machine01.evolix.net_, _machine02.evolix.net_, _machine03.evolix.net_… _machine57.evolix.net_ -* `http.evolix.net:2222` : ansible se connecte par ssh, et _http.evolix.net_ a un port SSH d'écoute différent qui est 2222 -* `[dbservers]` : groupe pour les serveurs de base de données -* `machine50.example.com` : cette machine est déjà présente dans le groupe _httpservers_, mais sera aussi accessible à partir du groupe _dbservers_ -* `alias ansible_port=2222 ansible_host=192.168.1.50` : la machine _alias_ n'a pas un vrai FQDN mais pointera vers _192.168.1.50_ car on a indiqué des variables propres à Ansible. Il est existe aussi `ansible_connection` (local ou ssh) ou `ansible_user` (le nom de l'utilisateur de la machine distante avec lequel Ansible se connectera en ssh) -* `host1 http_port=80 maxRequestsPerChild=808` : des variables qui seront automatiquement disponibles pour les actions sur _host1_ -* `[commercant:vars]` : des variables qui seront liées au groupe _commercant_. - -On peut aussi créer des groupes de groupes en utilisant `:children` - -On peut aussi découper le fichier "inventory" selon les groupes et les variables : - -Les variables propres à Ansible : - ### ansible.cfg Options utiles (TODO : à revoir) :