22
0
Fork 0

HowtoAnsible: relecture rapide et corrections

This commit is contained in:
Jérémy Lecour 2017-01-13 23:24:18 +01:00
parent 379ea6fe6e
commit a81ebeccee
1 changed files with 148 additions and 110 deletions

View File

@ -7,7 +7,7 @@ title: Howto Ansible : automatisation d'infrastructure
Ansible est un outil d'automatisation de configuration et gestion de serveurs : il permet le déploiement de logiciels et l'execution de tâches via une connexion SSH. Ansible est un outil d'automatisation de configuration et gestion de serveurs : il permet le déploiement de logiciels et l'execution de tâches via une connexion SSH.
Ansible fonctionne sans agent sur les serveurs (*agent-less*) et selon le concept d'*[idempotence](https://fr.wikipedia.org/wiki/Idempotence)* : on décrit l'état d'un serveur et des actions seront executées dans le but de rendre le serveur conforme à cette description. Ansible fonctionne sans agent sur les serveurs (*agent-less*) et selon le concept d'**[idempotence](https://fr.wikipedia.org/wiki/Idempotence)** : on décrit l'état d'un serveur et des actions seront executées dans le but de rendre le serveur conforme à cette description.
On pourra relancer Ansible plusieurs fois, l'état final reste le même : seules les actions nécessaires seront exécutées. On pourra relancer Ansible plusieurs fois, l'état final reste le même : seules les actions nécessaires seront exécutées.
Ansible peut exécuter des actions sur des serveurs distants sous : Ansible peut exécuter des actions sur des serveurs distants sous :
@ -70,7 +70,7 @@ Options utiles pour [ansible-playbook](https://manpages.debian.org/cgi-bin/man.c
## Les éléments d'Ansible ## Les éléments d'Ansible
L'utilisation d'Ansible se fait principalement avec un **playbook**. C'est l'élément clé, qui peut suffir à 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 *play*). Chaque *play* indiques quelques réglages généraux comme le groupe de serveurs concernés, la manière de devenenir administrateur, quels fichiers de variables charger… L'utilisation d'Ansible se fait principalement avec un **playbook**. C'est l'élément clé, qui peut suffir à 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** indiques quelques réglages généraux comme le groupe de serveurs concernés, la manière de devenenir administrateur, quels fichiers de variables charger…
Un playbook va ensuite dérouler des _actions_ qui seront organisées en _roles_, _tasks_ et _handlers_. Un playbook va ensuite dérouler des _actions_ qui seront organisées en _roles_, _tasks_ et _handlers_.
@ -149,7 +149,7 @@ Un playbook plus complexe :
post_tasks: post_tasks:
- include: "{{ external_tasks }}/commit_etc_git.yml" - include: "{{ external_tasks }}/commit_etc_git.yml"
vars: vars:
commit_message: "Ansible run capitaldata-firewall.yml" commit_message: "Ansible run firewall.yml"
handlers: handlers:
- name: restart minifirewall - name: restart minifirewall
@ -321,7 +321,9 @@ Voici quelques exemples :
state: restarted state: restarted
~~~ ~~~
#### replace: vs lineinfile: #### replace vs lineinfile
Le fonctionnement exacte de `replace` et de `lineinfile` est potentiellement déroutant. Voici quelques constatations :
* `lineinfile`: si `regexp:` n'est pas matché… il insère quand même la ligne ! avec `lineinfile` `regexp` n'est pas une condition pour l'insertion mais une condition pour remplacer au lieu d'insérer ! * `lineinfile`: si `regexp:` n'est pas matché… il insère quand même la ligne ! avec `lineinfile` `regexp` n'est pas une condition pour l'insertion mais une condition pour remplacer au lieu d'insérer !
* avec `lineinfile`: sauf cas tordus, `regexp` doit matcher `line` (sinon il va insérer la valeur de `line` à l'infini !) * avec `lineinfile`: sauf cas tordus, `regexp` doit matcher `line` (sinon il va insérer la valeur de `line` à l'infini !)
@ -332,7 +334,7 @@ Voici quelques exemples :
### vars ### vars
Gestion des variables : Les variables sont un élément clé de la configuration des playbooks et roles. Exemple :
~~~{.yaml} ~~~{.yaml}
vars: vars:
@ -343,6 +345,25 @@ tasks:
- command: echo {{ ip }} >> {{ conf_file }} - command: echo {{ ip }} >> {{ conf_file }}
~~~ ~~~
Les variables peuvent être définies à de multiples niveaux, chacun ayant une certaine précédence (extrait de la [documentation](http://docs.ansible.com/ansible/playbooks_variables.html#variable-precedence-where-should-i-put-a-variable)) :
* role defaults
* inventory vars
* 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)
Pour gérer de nombreuses variables dans un projet, on peut stocker toutes celles qui correspondent à un groupe de serveur dans un fichier portant le nom du groupe, ainsi que toutes celle d'un serveur en particulier dans un fichier du nom du serveur. Voici l'arborescence conseillée : Pour gérer de nombreuses variables dans un projet, on peut stocker toutes celles qui correspondent à un groupe de serveur dans un fichier portant le nom du groupe, ainsi que toutes celle d'un serveur en particulier dans un fichier du nom du serveur. Voici l'arborescence conseillée :
~~~ ~~~
@ -364,7 +385,7 @@ La directive `notify`, disponible pour tous les modules, permet de déclencher u
L'enregistrement du handler se fait seulement lorsque l'action a provoqué un changement. 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. 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 programmes. Les handlers servent le plus souvent à redémarrer des programmes. Exemple :
~~~{.yaml} ~~~{.yaml}
tasks: tasks:
@ -379,7 +400,7 @@ handlers:
state: restarted state: restarted
~~~ ~~~
Dans des rôles longs, il ets conseillé de purger les handlers de temps en temps (en fin de groupe d'action) pour que si le playbook ets interrompu les handlers ne soient pas exécutés alors que l'action qui les a déclenchés a bien eu lieu. On insère alors l'action suivante : 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} ```{.yaml}
- meta: flush_handlers - meta: flush_handlers
@ -414,9 +435,10 @@ Note : on peut également _taguer_ des `include`.
### Register ### Register
`register`. est un élément/champ que l'on peut rajouter pour tout type de tâche et qui initialisera la variable (par le nom donné) avec les valeurs retour du module. `register` est un attribut d'action que l'on peut rajouter pour tout type de tâche et qui initialisera la variable (par le nom donné) avec les valeurs retour du module.
Pour shell, on a le droit à `.stdout`, `.stderr`, `.rc`, ... Mais cela dépend bien des valeurs de retour du module. Pour `shell`, on a le droit à `.stdout`, `.stderr`, `.rc`… Mais cela dépend bien des valeurs de retour du module.
Mais pour être sûr des valeurs initialisées, on peut utiliser `debug` afin d'analyser le résultat.
Il est possible de consulter le contenu détaillé de la variable avec `debug` :
~~~{.yaml} ~~~{.yaml}
- stat: - stat:
@ -431,7 +453,7 @@ Mais pour être sûr des valeurs initialisées, on peut utiliser `debug` afin d'
when: st.stat.pw_name != 'root' when: st.stat.pw_name != 'root'
~~~ ~~~
Pour certains modules, `register` est un passage obligatoire pour une utilisation cohérente des éléments (stat…). Pour certains modules, `register` est presque un passage obligatoire pour une utilisation cohérente des éléments (stat…).
## Configuration ## Configuration
@ -480,13 +502,13 @@ proxy=proxy.mercerie.example.com
* `hostname.internal` : host hors groupe * `hostname.internal` : host hors groupe
* `[httpservers]` : le nom du groupe (pour les serveurs <http).> Les noms de hosts qui suivent appartiendront à ce 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_ * `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 * `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ée * `[dbservers]` : groupe pour les serveurs de base de donnée
* `machine50.example.com` : cette machine est déjà présente dans le groupe _httpservers_, mais sera aussi accessible à partir du groupe _dbservers_ * `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'est pas un vrai FQDN mais pointera vers _192.168.1.50_ car on a indiqué des variables propre à Ansible. Il est aussi disponible `ansible_connection` (local ou ssh) ou `ansible_user` (le nom de l'utilisateur de la machine distante avec lequel _ansible_ se connectera en ssh) * `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 auto-completé lié à host1 * `host1 http_port=80 maxRequestsPerChild=808` : des variables qui seront automatiquement auto-completées liées à _host1_
* `[commercant:vars]` : des variables qui seront liés au groupe _commercant_. On peut aussi créer des groupes de groupes en utilisant `:children` * `[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 : [<http://docs.ansible.com/ansible/intro_inventory.html#splitting-out-host-and-group-specific-data>]. On peut aussi découper le fichier "inventory" selon les groupes et les variables : [<http://docs.ansible.com/ansible/intro_inventory.html#splitting-out-host-and-group-specific-data>].
@ -498,11 +520,9 @@ Options utiles (TODO : à revoir) :
* `display_args_to_stdout` : mettre à `True` si on veut voir tout le contenu du _tasks_ executé pour chaque étape écrit sur _stdout_ * `display_args_to_stdout` : mettre à `True` si on veut voir tout le contenu du _tasks_ executé pour chaque étape écrit sur _stdout_
* `display_skipped_hosts` : mettre à `False` si on ne veut pas voir affiché sur _stdout_ l'information d'un _task_ qui n'est pas exécuté _(le nom de variable est confu - mais il s'agit bien de l'affichage du task)_ * `display_skipped_hosts` : mettre à `False` si on ne veut pas voir affiché sur _stdout_ l'information d'un _task_ qui n'est pas exécuté _(le nom de variable est confu - mais il s'agit bien de l'affichage du task)_
* *error_on_undefined_vars* : mettre à `True` pour être sûr que le script ansible s'arrête si une variable n'est pas défini (alors qu'il y a utilisation de cette dernière dans une _task_) * `error_on_undefined_vars` : mettre à `True` pour être sûr que le script Ansible s'arrête si une variable n'est pas définie (alors qu'il y a utilisation de cette dernière dans une _task_)
* `force_color` : mettre à `1` pour forcer la couleur * `force_color` : mettre à `1` pour forcer la couleur
* `forks` : le nombre de processus en parallèle possible lors déploiement du script ansible sur nombreux _hosts_. * `forks` : le nombre de processus en parallèle possible lors déploiement du script Ansible sur nombreux _hosts_
* `module_lang` : changer la langue
* `module_name` : le module par défaut (`command`) lors de l'utilisation de la commande `ansible`
* `hosts` : accès vers les _hosts_ par défaut (`all`) * `hosts` : accès vers les _hosts_ par défaut (`all`)
* `private_key_file` : le chemin pour la clé pem * `private_key_file` : le chemin pour la clé pem
* `remote_port` : le port ssh par défaut (`22`) * `remote_port` : le port ssh par défaut (`22`)
@ -518,7 +538,7 @@ Options utiles (TODO : à revoir) :
fatal: [test.evolix.net]: FAILED! => {"failed": true, "reason": "error while splitting arguments, either an unbalanced jinja2 block or quotes"} fatal: [test.evolix.net]: FAILED! => {"failed": true, "reason": "error while splitting arguments, either an unbalanced jinja2 block or quotes"}
~~~ ~~~
Vérifier bien la syntaxe du document qui est référé pour cette erreur. Cela peut être une quote mal fermé (ou mélange simple quote, double quote), ou encore histoire de crochet devenant une parenthèse... Vérifier bien la syntaxe du document qui est référé pour cette erreur. Cela peut être un guillemet mal fermé (ou mélange simples/doubles guillemets), ou encore histoire de crochet devenant une parenthèse…
### UNREACHABLE! ### UNREACHABLE!
@ -546,13 +566,13 @@ Et quand ça marche - la session ne se referme pas, et est réutilisé par les e
14:59:00 localhost sshd[20339]: Accepted password for service from 192.168.4.137 port 42511 ssh2 14:59:00 localhost sshd[20339]: Accepted password for service from 192.168.4.137 port 42511 ssh2
14:59:00 localhost sshd[20339]: pam_unix(sshd:session): session opened for user service by (uid=0) 14:59:00 localhost sshd[20339]: pam_unix(sshd:session): session opened for user service by (uid=0)
14:59:00 localhost systemd-logind[641]: New session 182 of user service. 14:59:00 localhost systemd-logind[641]: New session 182 of user service.
... (…)
15:01:08 localhost sshd[23034]: Received disconnect from 192.168.4.137: 11: disconnected by user 15:01:08 localhost sshd[23034]: Received disconnect from 192.168.4.137: 11: disconnected by user
15:01:08 localhost sshd[23024]: pam_unix(sshd:session): session closed for user service 15:01:08 localhost sshd[23024]: pam_unix(sshd:session): session closed for user service
15:01:08 localhost systemd-logind[641]: Removed session 182. 15:01:08 localhost systemd-logind[641]: Removed session 182.
~~~ ~~~
Mais si tentative de déploiement ansible juste après la session supprimé (par ex la 182), c'est à ce moment là qu'on se retrouve avec des *UNREACHABLE* -> savoir pourquoi sshd supprime la session ssh... Mais si tentative de déploiement Ansible juste après la session supprimée (par ex la 182), c'est à ce moment là qu'on se retrouve avec des *UNREACHABLE* -> savoir pourquoi sshd supprime la session ssh
### Missing required arguments ### Missing required arguments
@ -560,7 +580,7 @@ Mais si tentative de déploiement ansible juste après la session supprimé (par
fatal: [test.evolix.net]: FAILED! => {"changed": false, "failed": true, "msg": "missing required arguments: section"} fatal: [test.evolix.net]: FAILED! => {"changed": false, "failed": true, "msg": "missing required arguments: section"}
~~~ ~~~
Le message est assez clair, donc bien relire la doc du module sur ansible, et toujours ajouter les arguments obligatoires pour ce module. Le message est assez clair, donc bien relire la doc du module sur Ansible, et toujours ajouter les arguments obligatoires pour ce module.
### Requires stdlib json or simplejson module ### Requires stdlib json or simplejson module
@ -584,13 +604,13 @@ $ ansible-playbook --syntax-check my-experimental-playbook.yml
<http://www.yamllint.com/> <http://www.yamllint.com/>
* vérifier les action qui vont être faite (mode _dry-run_) sans rien exécuter : * vérifier les action qui vont être faite (mode `dry-run`) sans rien exécuter :
~~~ ~~~
$ ansible-playbook --check my-experimental-playbook.yml $ ansible-playbook --check my-experimental-playbook.yml
~~~ ~~~
* avoir le diff des fichiers modifiés (ne marche pas avec les modules replace/lineinfile à priori) : * avoir le diff des fichiers modifiés (ne marche pas avec les modules `replace`/`lineinfile` à priori) :
~~~ ~~~
$ ansible-playbook --check --diff my-experimental-playbook.yml $ ansible-playbook --check --diff my-experimental-playbook.yml
@ -612,29 +632,36 @@ Avoir des infos sur un module:
The [shell] module takes the command name followed by a li The [shell] module takes the command name followed by a li
space-delimited arguments. It is almost exactly like the [ space-delimited arguments. It is almost exactly like the [
... (…)
~~~ ~~~
### Stopper l'éxecution du code ### Stopper l'éxecution du code
Pour par exemple, stopper le code à un moment pour lire les valeurs d'une variables Pour par exemple, stopper le code à un moment pour lire les valeurs d'une variables
~~~ ~~~{.yaml}
- debug: var=cequejeveuxvoir - debug:
var: foo
- command: /bin/false - command: /bin/false
~~~ ~~~
ou ou
~~~ ~~~{.yaml}
- debug: var=cequejeveuxvoir - debug:
- fail: msg="FAIL" var: foo
- fail:
msg: "FAIL"
~~~ ~~~
ou ou
~~~ ~~~{.yaml}
- debug: var=cequejeveuxvoir - debug:
var: foo
- pause: - pause:
~~~ ~~~
@ -644,13 +671,13 @@ ou
[<https://docs.ansible.com/ansible/raw_module.html>] [<https://docs.ansible.com/ansible/raw_module.html>]
(bzip2, php, ... selon services à installer) (bzip2, php, selon services à installer)
~~~ ~~~
- raw: apt-get -y install python-simplejson [bzip2 php5-cli] - raw: apt-get -y install python-simplejson [bzip2 php5-cli]
~~~ ~~~
* Si pas encore fait, donner droit mysql à l'utilisateur * Si pas encore fait, donner les droits mysql à l'utilisateur
~~~ ~~~
> GRANT ALL ON db.* TO 'user'@'localhost'; > GRANT ALL ON db.* TO 'user'@'localhost';
@ -658,13 +685,13 @@ ou
### Limiter l'exécution à certaines machines ### Limiter l'exécution à certaines machines
* Limiter aux groupes www et sql (www et sql peuvent en fait être indifféremment des groupes ou des serveurs) : * Limiter aux groupes _www_ et _sql_ (qui peuvent être indifféremment des groupes ou des serveurs) :
~~~ ~~~
$ ansible-playbook -l "www:sql" playbook.yml $ ansible-playbook -l "www:sql" playbook.yml
~~~ ~~~
* limiter aux serveurs foo-www01, foo-lb01, foo-filer, etc… : * limiter aux serveurs _foo-www01_, _foo-lb01_, _foo-filer_… :
~~~ ~~~
$ ansible-playbook -l "foo-*" playbook.yml $ ansible-playbook -l "foo-*" playbook.yml
@ -682,29 +709,31 @@ $ ansible-playbook -l "*[0:9]" playbook.yml
$ ansible-playbook -l "*[10:]" playbook.yml $ ansible-playbook -l "*[10:]" playbook.yml
~~~ ~~~
Il est de toute façon préférable de ne pas mettre *all* dans le champs *hosts* dans le playbook pour éviter un -_possible_- oublie de "limite" de machines. Il est de toute façon préférable de ne pas mettre *all* dans le champs *hosts* dans le playbook pour éviter un - _possible_ - oubli de "limite" de machines.
### Lancement tâches hosts asynchrone ### Lancement tâches hosts asynchrone
Pour éviter que les différentes tâches s'appliquent une par une sur tout les hosts impliqués par le déploiement du play ansible, on peut utiliser l'option `strategy` à la valeur `free` pour que chaques tâches sur un host puisse continuer dès la fin de son exécution sans attendre l'état des autres hosts concernés en cours. Pour éviter que les différentes tâches s'appliquent une par une sur tout les hosts impliqués par l'exécution du playbook, on peut utiliser l'option `strategy` à la valeur `free` pour que chaques tâches sur un host puisse continuer dès la fin de son exécution sans attendre l'état des autres hosts concernés en cours.
~~~ ~~~{.yaml}
- hosts: all - hosts: all
... (…)
strategy: free strategy: free
~~~ ~~~
_Ne plus se fier au texte `host changed` après texte de la tâche, car il pourrait s'agir d'une autre tâche affiché plus en haut dans le texte de l'historique_ _Note: Ne plus se fier au texte `host changed` après texte de la tâche, car il pourrait s'agir d'une autre tâche affiché plus en haut dans le texte de l'historique_
### Fréquence des hosts ### Fréquence des hosts
Lors d'une appel d'un play, on peut indiquer une fréquence sur le nombre d'host touché par l'éxecution du playbook. Lors d'une appel d'un play, on peut indiquer une fréquence sur le nombre d'hôtes concernés par l'éxecution du playbook.
* `Fork` pour le nombre de hosts simultanés (changeable dans le fichier _ansible.cfg_ - mettre une valeur importante > centaine). * `Fork` pour le nombre d'hôtes simultanés (modifiable dans le fichier _ansible.cfg_ - mettre une valeur importante > centaine).
* `serial` en en-tête contenant une valeur numérique qui représente le nombre de machines pour chaque tour d'éxecution de playbook, ou un pourcentage par rapport à la liste inventory concerné. * `serial` en en-tête contenant une valeur numérique qui représente le nombre de machines pour chaque tour d'éxecution de playbook, ou un pourcentage par rapport à la liste inventory concerné.
### Cowsay ### Cowsay
Si la commande `cowsay` est disponible sur votre machine, vous verrez un message à la fin :
~~~ ~~~
____________________ ____________________
< NO MORE HOSTS LEFT > < NO MORE HOSTS LEFT >
@ -724,9 +753,7 @@ Lors d'une appel d'un play, on peut indiquer une fréquence sur le nombre d'host
|| || || ||
~~~ ~~~
Pour avoir les messages de la vache : *cowsay*. Pour le désactiver : `export ANSIBLE_NOCOWS=1`
Si installer, pour le désactiver : `export ANSIBLE_NOCOWS=1`
Disponible aussi dans la conf du fichier `/etc/ansible/ansible.cfg` Disponible aussi dans la conf du fichier `/etc/ansible/ansible.cfg`
@ -738,31 +765,32 @@ Disponible aussi dans la conf du fichier `/etc/ansible/ansible.cfg`
{% if python_is_installed is defined %} {% if python_is_installed is defined %}
Ansible devrait marcher -pardi! Ansible devrait marcher -pardi!
{% endif %} {% endif %}
~~~ ~~~
<http://jinja.pocoo.org/docs/dev/templates/#builtin-tests> <http://jinja.pocoo.org/docs/dev/templates/#builtin-tests>
Voir la doc pour plus de features : <http://jinja.pocoo.org/docs/dev/> Voir la doc pour plus de détails : <http://jinja.pocoo.org/docs/dev/>
### Lire une entrée au clavier ### Lire une entrée au clavier
S'il manque une valeur pour la suite du script, soit on le gère en mettant une erreur, ou une valeur par défaut, mais sinon on peut aussi demander une valeur a être tapé sur le clavier : S'il manque une valeur pour la suite du script, soit on le gère en mettant une erreur, ou une valeur par défaut, mais sinon on peut aussi demander une saisie clavier :
~~~ ~~~{.yaml}
vars_prompt: vars_prompt:
- name: 'prenom' - name: 'prenom'
prompt: 'Quel est votre prénom ?' prompt: 'Quel est votre prénom ?'
private: no private: no
tasks: tasks:
- debug: var=prenom - debug:
var: prenom
~~~ ~~~
Malheureusement pour le moment, doit se situer avant `tasks`. Malheureusement pour le moment, doit se situer avant `tasks`.
Si on veut utiliser cette variable dans une tâche, il faut simplement utiliser le nom de la variable, et si on veut l'utiliser (explicitement) pour un play ne se trouvant pas dans le même fichier (donc ici la variable dans autre.yml s'appelera _prenom_de_autre_ et non prenom) : Si on veut utiliser cette variable dans une tâche, il faut simplement utiliser le nom de la variable, et si on veut l'utiliser (explicitement) pour un play ne se trouvant pas dans le même fichier (donc ici la variable dans autre.yml s'appelera _prenom_de_autre_ et non prenom) :
~~~ ~~~{.yaml}
- include: './tasks/autre.yml' - include: './tasks/autre.yml'
vars: vars:
prenom_de_autre: prenom prenom_de_autre: prenom
@ -770,46 +798,48 @@ Si on veut utiliser cette variable dans une tâche, il faut simplement utiliser
<https://docs.ansible.com/ansible/playbooks_prompts.html> <https://docs.ansible.com/ansible/playbooks_prompts.html>
### Lancer playbook en mode interactif ### Exécuter un playbook en mode interactif
~~~ ~~~
ansible-playbook playbook.yml --step $ ansible-playbook playbook.yml --step
~~~ ~~~
<https://docs.ansible.com/ansible/playbooks_startnstep.html> <https://docs.ansible.com/ansible/playbooks_startnstep.html>
### Ne pas lancer une commande shell si le fichier existe ### Ne pas lancer une commande shell si le fichier existe
En initialisant le champs *creates* indiquant le chemin de fichier lors de l'utilisation du module shell, cette tâche là ne s'executera seulement si le fichier (chemint *creates* correspondant) n'existe pas. En indiquant l'attribut `creates` indiquant le chemin de fichier lors de l'utilisation du module shell, cette tâche ne s'executera que si le fichier indiqué par `creates` n'existe pas.
Et le contraire (si le fichier existe la tâche se lancera), avec le champs *removes*. Le corollaire est possible avec l'attribut `removes` qui empêche l'exécution si le fichier n'existe pas.
Disponible sur certains module (comme *shell*).
Plus simple et rapide que de tester le fichier par le module *stat* juste avant. Ces attributs sont disponibles pour certains modules (comme `shell`).
### Lancer tâche sur machine précise (voir local) C'est beaucoup plus simple et rapide que de tester le fichier par le module *stat* juste avant.
### Lancer tâche sur machine précise (voire locale)
~~~ ~~~
- name: /etc/hosts - name: /etc/hosts
shell: cat /etc/hosts command: cat /etc/hosts
register: tmp register: tmp
delegate_to: localhost delegate_to: localhost
- debug: var=tmp.stdout - debug:
var: tmp.stdout
~~~ ~~~
Pour local, peut être remplacer par la directive `local_action`. Pour une exécution locale, on peut aussi utiliser l'attribut `local_action`.
<https://docs.ansible.com/ansible/playbooks_delegation.html#delegation> <https://docs.ansible.com/ansible/playbooks_delegation.html#delegation>
### Lancer tâche qu'une seule fois ### Ne lancer tâche qu'une seule fois
~~~ ~~~
- name: Début installation, envoie email - name: Début installation, envoie email
run_once: true run_once: true
... (…)
~~~ ~~~
Si ajouter avec `delegate_to`, seule cette machine executera cette tâche, sinon c'est la première dans la liste de l'inventory. Si cet attribut est utilisé avec `delegate_to`, alors cette machine sera la seule à exécuter cette tâche. Sinon, c'est la première dans la liste de l'inventaire.
<https://docs.ansible.com/ansible/playbooks_delegation.html#run-once> <https://docs.ansible.com/ansible/playbooks_delegation.html#run-once>
@ -817,14 +847,14 @@ Si ajouter avec `delegate_to`, seule cette machine executera cette tâche, sinon
#### with_items #### with_items
~~~ ~~~{.yaml}
- name: Manger les fruits - name: Manger les fruits
shell: eat '{{ item }}' command: eat '{{ item }}'
with_items: with_items:
- Apple - Apple
- Orange - Orange
- Strawberry - Strawberry
- Mango - Mango
~~~ ~~~
Par exemple pour l'installation de plusieurs nouveaux packages : Par exemple pour l'installation de plusieurs nouveaux packages :
@ -834,40 +864,41 @@ Par exemple pour l'installation de plusieurs nouveaux packages :
- hosts: localhost - hosts: localhost
tasks: tasks:
- apt: name='{{ item }}' state=present - apt:
with_items: [ 'cmatrix', 'tetrinet-server', 'tetrinet-client', 'xtel', 'xtell' ] name: '{{ item }}'
state: present
with_items:
- cmatrix
- tetrinet-server
- tetrinet-client
- xtel
- xtell
~~~ ~~~
Même si il y aura plusieurs packages installés, cela ne comptera que pour *un* changement (changed=1). Même si il y aura plusieurs packages installés, cela ne comptera que pour *un* changement (`changed=1`).
Cette tâche appellera un par un les éléments de la liste (présent dans with_items) pour le module. Cette tâche appellera un par un les éléments de la liste (présent dans `with_items`) pour le module.
En l'appelant :
~~~
ansible-playbook -b --ask-become-pass --become-method='su' --become-user='root' apt.yml
~~~
#### with_nested #### with_nested
Pour croiser les éléments des items -> *"with_nested"*. Pour croiser les éléments des items :
~~~ ~~~
tasks: tasks:
- include: "./ajout_utilisateur_sur_machine.yml" - include: "./ajout_utilisateur_sur_machine.yml"
vars: vars:
user: "{{ item[0] }}" user: "{{ item[0] }}"
server: "{{ item[1] }}" server: "{{ item[1] }}"
with_nested: with_nested:
- [ 'alice', 'bob' ] - [ 'alice', 'bob' ]
- [ 'machine1', 'machine2', 'machine-backup' ] - [ 'machine1', 'machine2', 'machine-backup' ]
~~~ ~~~
Qui aura pour effet d'appeler le include avec comme argument : item[0]=alice, item[1]=machine1; puis item[0]=alice, item[0]=machine2; ... puis item[0]=bob, item[1]=machine1, ... Cela a pour effet d'exécuter l'inclusion pour `alice` pour chacune des 3 machines, puis pour `bob` pour chacune des 3 machines.
#### with_dict #### with_dict
Avec hash ... Avec hash :
~~~ ~~~
users: users:
@ -880,39 +911,44 @@ users:
uid: 1001 uid: 1001
home: home:
tasks: tasks:
- user: name="{{ item.key }}" comment="{{ item.value.name }}" uid="{{ item.value.uid }}" home="{{ item.value.home }}" - user:
name: "{{ item.key }}"
comment: "{{ item.value.name }}"
uid: "{{ item.value.uid }}"
home: "{{ item.value.home }}"
with_dict: "{{ users }}" with_dict: "{{ users }}"
~~~ ~~~
### Se connecter sous un autre utilisateur UNIX ### Se connecter sous un autre utilisateur UNIX
Par défaut, l'utilisateur se connectant sur le serveur distant est {{ ansible_user_id }} (càd uid courant UNIX). On peut soit le préciser dans le fichier de conf principal de ansible avec `remote_user : michu` ou en l'indiquant en argument lors de l'éxecution du playbook. Par défaut, l'utilisateur se connectant sur le serveur distant l'utilisateur UNIX courant. On peut soit le préciser dans le fichier de conf principal d'Ansible avec `remote_user: michu`, dans l'inventaire pour un groupe ou un serveur précis ou encore en l'indiquant en argument lors de l'éxecution du playbook.
~~~ ~~~
ansible-playbook -u michu -k play.yml $ ansible-playbook -u michu -k play.yml
~~~ ~~~
### Éviter que la commande shell indique élement 'changed' ### Éviter que la commande shell indique élement 'changed'
Sur tout les modules, chaque taches retourne un statut sur son résultat : Sur tous les modules, chaque tache retourne un statut sur son résultat :
* Ok : Tout s'est bien passé * `ok` : Tout s'est bien passé
* Changed : Tout s'est bien passé mais il y a eu modification par rapport à l'état précédent (droits fichiers,...) * `changed` : Tout s'est bien passé mais il y a eu modification par rapport à l'état précédent (droits fichiers)
* Failed : Raté * `failed` : Raté
Le soucis est qu'il parrait bien difficile pour le module shell de connaître après execution de la commande s'il y a eu modification. Par défaut, il y a changement - quand bien même l'utilisation du shell se fait pour afficher un résultat (id, date, ...). Pour des modules comme `shell`, `command`… Ansible ne peut savoir si un changement a eu lieu ou pas. Il indiqu alors toujours `changed`.
Pour éviter cela on peut mettre :
Il est possible de forcer le statut du changement :
~~~ ~~~
- shell: date - command: date
changed_when: false changed_when: False
~~~ ~~~
Ou donner une condition Ou donner une condition
~~~ ~~~
- shell: cat > {{ fichier }} </dev/null - shell: cat > {{ fichier }} < /dev/null
changed_when: {{ fichier.stats.exist }} changed_when: {{ fichier.stats.exist }}
~~~ ~~~
@ -922,7 +958,7 @@ Ou donner une condition
$ ansible -m setup <hostname> $ ansible -m setup <hostname>
service.evolix.net | SUCCESS => { service.evolix.net | SUCCESS => {
"ansible_facts": { "ansible_facts": {
... (…)
"ansible_architecture": "x86_64", "ansible_architecture": "x86_64",
"ansible_bios_date": "12/01/2006", "ansible_bios_date": "12/01/2006",
"ansible_bios_version": "VirtualBox", "ansible_bios_version": "VirtualBox",
@ -937,10 +973,10 @@ service.evolix.net | SUCCESS => {
"day": "06", "day": "06",
"epoch": "1462546886", "epoch": "1462546886",
"hour": "17", "hour": "17",
... (…)
}, },
"ansible_default_ipv4": { "ansible_default_ipv4": {
... (…)
} }
~~~ ~~~
@ -956,7 +992,8 @@ Pour récupérer toutes les adresses MAC des machines :
gather_facts: true gather_facts: true
tasks: tasks:
- debug: var=ansible_eth0.macaddress - debug:
var: ansible_eth0.macaddress
~~~ ~~~
que l'on pourra combiner par exemple avec un pipe en ligne de commande : que l'on pourra combiner par exemple avec un pipe en ligne de commande :
@ -974,6 +1011,7 @@ Il est possible aussi d'accéder aux variables d'environnement shell :
## Ressources utiles ## Ressources utiles
* [Documentation officielle](http://docs.ansible.com/ansible/) (voir notamment la partie [Best Practices](http://docs.ansible.com/ansible/playbooks_best_practices.html)) * [Documentation officielle](http://docs.ansible.com/ansible/) (voir notamment la partie [Best Practices](http://docs.ansible.com/ansible/playbooks_best_practices.html))
* Vidéos [ansible-best-practices](https://www.ansible.com/ansible-best-practices) et [ansible-tips-and-tricks](https://www.ansible.com/ansible-tips-and-tricks)
* [Ansible 101 - on a Cluster of Raspberry Pi 2s](https://www.youtube.com/watch?v=ZNB1at8mJWY) * [Ansible 101 - on a Cluster of Raspberry Pi 2s](https://www.youtube.com/watch?v=ZNB1at8mJWY)
* Sysadmin Casts (épisodes [43](https://sysadmincasts.com/episodes/43-19-minutes-with-ansible-part-1-4), [45](https://sysadmincasts.com/episodes/45-learning-ansible-with-vagrant-part-2-4), [46](https://sysadmincasts.com/episodes/46-configuration-management-with-ansible-part-3-4) et [47](https://sysadmincasts.com/episodes/47-zero-downtime-deployments-with-ansible-part-4-4)) * Sysadmin Casts (épisodes [43](https://sysadmincasts.com/episodes/43-19-minutes-with-ansible-part-1-4), [45](https://sysadmincasts.com/episodes/45-learning-ansible-with-vagrant-part-2-4), [46](https://sysadmincasts.com/episodes/46-configuration-management-with-ansible-part-3-4) et [47](https://sysadmincasts.com/episodes/47-zero-downtime-deployments-with-ansible-part-4-4))
* [How Twitter uses Ansible](https://www.youtube.com/watch?v=fwGrKXzocg4) (AnsibleFest 2014) * [How Twitter uses Ansible](https://www.youtube.com/watch?v=fwGrKXzocg4) (AnsibleFest 2014)