HowtoAnsible: encore des corections

This commit is contained in:
Jérémy Lecour 2017-01-22 22:30:37 +01:00
parent 402743d3a7
commit 322be992b9

View file

@ -6,7 +6,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 exécuté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 :
@ -62,10 +62,10 @@ $ ansible-playbook PLAYBOOK_WITH-SUDO.yml --limit HOSTNAME --ask-become-pass
Options utiles pour [ansible-playbook](https://manpages.debian.org/cgi-bin/man.cgi?query=ansible-playbook&apropos=0&sektion=0&manpath=Debian+unstable+sid&format=html&locale=en) : Options utiles pour [ansible-playbook](https://manpages.debian.org/cgi-bin/man.cgi?query=ansible-playbook&apropos=0&sektion=0&manpath=Debian+unstable+sid&format=html&locale=en) :
* `-vvvv` : très verbeux (utile notamment pour debug SSH quand on a une erreur _unreachable_) * `-vvvv` : très verbeux (utile notamment pour debug SSH quand on a une erreur _unreachable_)
* `-k` / `--ask-pass` : demande le mot de passe utilisateur (si connexion SSH sans clé) * `-k` / `--ask-pass` : demande le mot de passe pour la connexion SSH
* `-K` / `--ask-become-pass` : demande le mot de passe utilisateur pour sudo * `-K` / `--ask-become-pass` : demande le mot de passe pour l'escalade (via sudo, su, doas…)
* `-l` / `--limit HOSTNAME` : limite la connexion au serveur HOSTNAME (attention, par défaut c'est *all*, cf `/etc/ansible/hosts`) * `-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é en parallèle (par défaut 5)… peut être utile de mettre à 1 pour ne pas paralléliser * `-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 ## Les éléments d'Ansible
@ -81,9 +81,9 @@ Une **action** est l'invocation d'une fonctionnalité particulière fournie par
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 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 demnde, via l'attribut `notify`. 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 une structure conventionnelle : 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 (idéalement) une structure conventionnelle :
~~~ ~~~
foo foo
@ -105,7 +105,7 @@ foo
└── main.yml └── main.yml
~~~ ~~~
La partie **inventory** correspond à la description de l'inventaire des serveurs à configurer et inclus un mécanisme de configuration individuel et par groupe. Nous y revenons plus loin. 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 ## Howto Playbook
@ -324,12 +324,12 @@ Voici quelques exemples :
Le fonctionnement exacte de `replace` et de `lineinfile` est potentiellement déroutant. Voici quelques constatations : 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 ! * avec le module `lineinfile`, si l'argument `regexp` n'est pas matché… il insère quand même la ligne ! `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 le module `lineinfile`, sauf cas tordus, l'argument `regexp` doit matcher l'argument `line` (sinon il va insérer la valeur de `line` à chaue exécution !)
* `lineinfile`: va 1. regarder si `regexp` existe et il remplace la dernière ligne, 2. si `regexp` n'existe pas, il ajoute `line` (sans d'autre condition… même si elle existe déjà) * le module `lineinfile` va d'abord évaluer si `regexp` matche et remplacer la dernière occurrence, puis si `regexp` ne matche pas il ajoute alors `line` (sans d'autre condition… même si elle existe déjà)
* `replace`: il va remplacer uniquement si `regex` est matché, logique (comme la commande `sed`) * le module `replace` va remplacer uniquement si `regex` est matché, comme la commande `sed`
* si l'on veut utiliser une référence (`\1`) dans `line` avec `lineinfile`, ça donne une erreur, il faut utiliser `replace` * 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 `lineinfile` + `backrefs: yes` c'est pour utiliser une référence au sein de `regexp` (et non pas au sein de `line`) * 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 ### vars
@ -384,7 +384,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. Exemple : Les handlers servent le plus souvent à redémarrer des services. Exemple :
~~~{.yaml} ~~~{.yaml}
tasks: tasks:
@ -499,19 +499,21 @@ ntp_server=ntp.mercerie.example.com
proxy=proxy.mercerie.example.com proxy=proxy.mercerie.example.com
~~~ ~~~
* `hostname.internal` : host hors groupe * `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 * `[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ées
* `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'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) * `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ées liées à _host1_ * `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` * `[commercant:vars]` : des variables qui seront liées au groupe _commercant_.
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 créer des groupes de groupes en utilisant `:children`
Les variables propres à Ansible : [<http://docs.ansible.com/ansible/intro_inventory.html#list-of-behavioral-inventory-parameters>] 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>
Les variables propres à Ansible : <http://docs.ansible.com/ansible/intro_inventory.html#list-of-behavioral-inventory-parameters>
### ansible.cfg ### ansible.cfg
@ -524,8 +526,8 @@ Options utiles (TODO : à revoir) :
* `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_
* `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`)
* `remote_user` : l'utilisateur pour la connexion ssh par défaut (`root`) * `remote_user` : l'utilisateur pour la connexion SSH par défaut (`root`)
* `retry_files_enabled` : mettre à `True` pour la création de fichier `.retry` après une failure de ansible pour reprendre le travail précédent - ajouté en argument dans l'appel de la commande * `retry_files_enabled` : mettre à `True` pour la création de fichier `.retry` après une failure de ansible pour reprendre le travail précédent - ajouté en argument dans l'appel de la commande
@ -545,7 +547,7 @@ Vérifier bien la syntaxe du document qui est référé pour cette erreur. Cela
fatal: [test.evolix.net]: UNREACHABLE! => {"changed": false, "msg": "SSH encountered an unknown error during the connection. We recommend you re-run the command using -vvvv, which will enable SSH debugging output to help diagnose the issue", "unreachable": true} fatal: [test.evolix.net]: UNREACHABLE! => {"changed": false, "msg": "SSH encountered an unknown error during the connection. We recommend you re-run the command using -vvvv, which will enable SSH debugging output to help diagnose the issue", "unreachable": true}
~~~ ~~~
Malheureusement, cela arrive souvent sur certaines machines (une?), mais pas de solutions pour le moment -> boire du café - beaucoup de café. Malheureusement, cela arrive souvent sur certaines machines (une ?), mais pas de solution pour le moment -> boire du café - beaucoup de café.
Du côté de la machine distante, dans le fichier `/var/log/auth.log`: Du côté de la machine distante, dans le fichier `/var/log/auth.log`:
@ -603,12 +605,14 @@ $ 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 actions qui vont être faite s(mode `dry-run`) sans rien exécuter :
~~~ ~~~
$ ansible-playbook --check my-experimental-playbook.yml $ ansible-playbook --check my-experimental-playbook.yml
~~~ ~~~
*Note* : certaines actions ne sont pas exécutées en mode "check", cela peut donc perturber celles qui sont bassées dessus.
* 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) :
~~~ ~~~
@ -630,17 +634,19 @@ Avoir des infos sur un module:
> SHELL > SHELL
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
(…) (…)
~~~ ~~~
*Note* : c'est pratique pur avoir la documentation exacte pour votre version d'Ansible. En effet, celle du site corespond à la dernière version et n'indique pas toujours toutes les différences.
### 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} ~~~{.yaml}
- debug: - debug:
var: foo var: foo
- command: /bin/false - command: /bin/false
~~~ ~~~
@ -649,17 +655,17 @@ ou
~~~{.yaml} ~~~{.yaml}
- debug: - debug:
var: foo var: foo
- fail: - fail:
msg: "FAIL" msg: "FAIL"
~~~ ~~~
ou ou
~~~{.yaml} ~~~{.yaml}
- debug: - debug:
var: foo var: foo
- pause: - pause:
~~~ ~~~
@ -668,12 +674,12 @@ ou
* Installer package python * Installer package python
[<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 -y install python-simplejson [bzip2 php5-cli]
~~~ ~~~
* Si pas encore fait, donner les droits mysql à l'utilisateur * Si pas encore fait, donner les droits mysql à l'utilisateur
@ -708,7 +714,7 @@ $ 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_ - oubli 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
@ -720,11 +726,11 @@ Pour éviter que les différentes tâches s'appliquent une par une sur tout les
strategy: free strategy: free
~~~ ~~~
_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_ *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ée 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'hôtes concernés par l'éxecution du playbook. Lors de l'exécution 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 d'hôtes simultanés (modifiable 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é.
@ -782,7 +788,7 @@ vars_prompt:
tasks: tasks:
- debug: - debug:
var: prenom var: prenom
~~~ ~~~
Malheureusement pour le moment, doit se situer avant `tasks`. Malheureusement pour le moment, doit se situer avant `tasks`.
@ -807,12 +813,12 @@ $ ansible-playbook playbook.yml --step
### Ne pas lancer une commande shell si le fichier existe ### Ne pas lancer une commande shell si le fichier existe
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. En indiquant l'argument `creates` indiquant le chemin de fichier lors de l'utilisation du module shell, cette tâche ne s'exécutera que si le fichier indiqué par `creates` n'existe pas.
Le corollaire est possible avec l'attribut `removes` qui empêche l'exécution si le fichier n'existe pas. Le corollaire est possible avec l'argument `removes` qui empêche l'exécution si le fichier n'existe pas.
Ces attributs sont disponibles pour certains modules (comme `shell`). Ces arguments sont disponibles pour certains modules (comme `shell`).
C'est beaucoup plus simple et rapide que de tester le fichier par le module *stat* juste avant. 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) ### Lancer tâche sur machine précise (voire locale)
@ -823,7 +829,7 @@ C'est beaucoup plus simple et rapide que de tester le fichier par le module *sta
delegate_to: localhost delegate_to: localhost
- debug: - debug:
var: tmp.stdout var: tmp.stdout
~~~ ~~~
Pour une exécution locale, on peut aussi utiliser l'attribut `local_action`. Pour une exécution locale, on peut aussi utiliser l'attribut `local_action`.
@ -856,7 +862,7 @@ Si cet attribut est utilisé avec `delegate_to`, alors cette machine sera la seu
- Mango - Mango
~~~ ~~~
Par exemple pour l'installation de plusieurs nouveaux packages : Par exemple pour l'installation de plusieurs nouveaux paquets :
~~~{.yaml} ~~~{.yaml}
--- ---
@ -872,11 +878,10 @@ Par exemple pour l'installation de plusieurs nouveaux packages :
- tetrinet-client - tetrinet-client
- xtel - xtel
- xtell - 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 paquets 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ésents dans `with_items`) pour le module.
#### with_nested #### with_nested
@ -919,6 +924,24 @@ tasks:
with_dict: "{{ users }}" with_dict: "{{ users }}"
~~~ ~~~
#### [with_first_found](http://docs.ansible.com/ansible/playbooks_loops.html#finding-first-matched-files)
Permet de prendre le premier fichier trouvé :
~~~{.yaml}
- name: Copy HAProxy configuration
template:
src: "{{ item }}"
dest: /etc/haproxy/haproxy.cfg
force: yes
with_first_found:
- haproxy.cfg/{{ inventory_hostname }}
- haproxy.cfg/{{ host_group }}
- haproxy.cfg/default
~~~
De cette manière, si un fichier portant le nom du serveur en cours existe, il sera utilisé, sinon on cherche un fichier du nom du groupe du serveur et enfin on cherche un fichier par défaut, valable pour tous les serveurs qui n'ont pas de configuratio spécifique ou de groupe.
### 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 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. 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.
@ -927,15 +950,15 @@ Par défaut, l'utilisateur se connectant sur le serveur distant l'utilisateur UN
$ 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 n'indique d'élement 'changed'
Sur tous les modules, chaque tache retourne un statut sur son résultat : Sur tous les modules, chaque tâche retourne un statut sur son résultat :
* `ok` : Tout s'est bien passé * `ok` : aucune modification n'a été nécessaire
* `changed` : Tout s'est bien passé mais il y a eu modification par rapport à l'état précédent (droits fichiers…) * `changed` : une modification a eu lieu par rapport à l'état précédent (droits fichiers…)
* `failed` : Raté * `failed` : une erreur s'est produite
Pour des modules comme `shell`, `command`… Ansible ne peut savoir si un changement a eu lieu ou pas. Il indiqu alors toujours `changed`. Pour des modules comme `shell`, `command`… Ansible ne peut savoir si un changement a eu lieu ou pas. Il indique alors toujours `changed`.
Il est possible de forcer le statut du changement : Il est possible de forcer le statut du changement :