Ansible
Cet article est un gros brouillon de fatras. Ansible est utilisé par certains ici, mais moi, j'y connais rien, et je m'y lance dans l'optique de répliquer certaines manip, entre autre avec BURPS.
C'est donc de la doc pour noob avec probablement des erreurs.
— zatalyz 2017/10/13 11:26
Ansible s'installe sur sa machine à soi (pas sur un serveur), afin d'administrer ses serveurs en automatisant pleins de choses. C'est censé simplifier le boulot du sysadmin1).
Donc, suivant votre système :
sudo apt-get install ansible yaourt -S ansible dnf install ansible ...
Seul point important : il faut que python2) soit installé sur les serveurs cibles. Mais c'est tout.
Agent ssh
Pensez à activer l'agent ssh qui va se souvenir de la passphrase de votre clé ssh le temps de la session, sinon vous devrez retaper cette passphrase pour vous connecter aux serveurs. Et il y en a beaucoup, si vous utilisez Ansible.
L'agent ssh est un passage obligé avec ansible (à moins d'utiliser une paire de clefs sans passphrase, ce qui est déconseillé).
eval "$(ssh-agent -s)" ssh-add ~/.ssh/id_ed25519
Fichiers de base
Généralement on se crée sa propre arborescence (dans ~/Ansible
par exemple) ; cela permet d'adapter à ses besoins.
Il y a beaucoup de possibilités, mais il n'est pas nécessaire de tout renseigner. En fait, de base, il faut surtout les fichiers suivants :
- ansible.cfg : stocke la configuration adapté à notre usage d'ansible (variables d'environnement, etc).
- hosts : liste les serveurs sur lesquels on va utiliser ansible.
- playbook.yml : une recette à appliquer sur les serveurs.
Une architecture propre (et complexe) peut ressembler à ceci :
~/Ansible/
├── ansible.cfg # Config Ansible (inventaire, roles_path, etc.)
├── hosts # Inventaire des machines
├── group_vars/ # Variables spécifiques à un groupe d’hôtes
│ ├── all.yml
│ ├── webservers.yml
│ └── dbservers.yml
├── host_vars/ # Variables spécifiques à un hôte
│ ├── srv01.yml
│ └── srv02.yml
├── vars/ # Variables globales (optionnel si group/host_vars)
│ └── global.yml
├── playbooks/ # Playbooks principaux
│ ├── site.yml # Playbook principal (peut appeler plusieurs rôles)
│ ├── web.yml
│ └── db.yml
├── roles/ # Dossier des rôles (autorisés par ansible.cfg)
│ ├── common/
│ │ ├── tasks/
│ │ │ └── main.yml
│ │ ├── handlers/
│ │ ├── templates/
│ │ ├── files/
│ │ ├── vars/
│ │ ├── defaults/
│ │ ├── meta/
│ │ └── README.md
│ ├── webserver/
│ └── database/
├── files/ # Fichiers généraux partagés
│ └── ssh_config
├── templates/ # Templates Jinja2 globaux
│ └── motd.j2
├── scripts/ # Scripts shell ou Python à exécuter
│ └── postinstall.sh
└── vault/ # Informations chiffrés (mots de passe, tokens...)
└── secrets.yml
Quelques définitions rapides :
- Un playbook est la liste de “recettes” à appliquer dans un contexte précis (par exemple “créer une nouvelle machine”)
- Un rôle est un module qui pourra être appelé dans divers playbooks. Ça évite de se répéter : on importe juste le role dans le playbook.
ansible.cfg
Un fichier minimal ressemble à ça :
- ansible.cfg
[defaults] inventory = hosts retry_files_enabled = False timeout = 10
Voir https://docs.ansible.com/ansible/latest/reference_appendices/config.html pour les diverses options.
hosts
Le fichier où on renseigne ses serveurs.
Par défaut, Ansible les cherche dans /etc/ansible/hosts
. Mais on peut créer le fichier du nom qu'on veut à l'emplacement qu'on veut ; il suffira soit de le renseigner dans /etc/ansible/ansible.cfg
(c'est la variable inventory = /chemin/fichier
), soit de lancer Ansible avec la variable -i
en indiquant le chemin.
ansible -i /chemin/hosts
Créer un ou des fichiers a quelques avantages :
- cela permet de séparer suivant les infrastructures, par exemple un fichier pour les serveurs en production, un autre pour les serveurs en pré-production ;
- cela permet de renseigner son fichier hosts sans avoir besoin des droits root, typiquement quand c'est dans home…
Il y a visiblement plusieurs syntaxes possibles (peut-être à paramétrer dans /etc/ansible/ansible.cfg
?), on va rester au truc par défaut qui est la syntaxe de type INI, c'est à dire avec des crochets pour les sections, dans ce genre-là :
[webservers] foo.example.org bar.example.org [dbservers] one.example.org two.example.org three.example.org [webservers:vars] ansible_python_interpreter=/usr/bin/python3
Ce qui est entre crochets est le nom donné à un groupe et ce nom est libre, mais il ne doit pas reprendre un nom des serveurs gérés dessous. Ici par exemple, il y a un groupe de serveurs qui concerne le web, un autre groupe avec les machines hébergeant les bases de données ; ils sont déclarés avec leur nom de domaine.
Les serveurs renseignés dessous peuvent l'être suivant diverses syntaxes :
- d'après le nom de domaine qui leur est lié (par exemple foo.example.org)
- d'après leur ip (par exemple 9.9.9.9)
- d'après leur raccourci renseigné dans
.ssh/config
(le plus pratique car cela précise le port, l'utilisateur…). Voir ici pour plus de détail sur ce fichier. Et c'est ma méthode préféré, car cela renseignera automatiquement le port, l'utilisatrice, etc.
La partie “vars” précisent des variables affectant tout le groupe : ici, déclarer le chemin de python afin d'éviter un warning sans grand intérêt.
Pour tester votre configuration, vous pouvez faire un ping-pong avec vos serveurs !
Exemple avec l'host suivant, où les serveurs sont déclarés d'après leurs variables de la config ssh3) :
[khanat] lirria jukni [khanat:vars] ansible_python_interpreter=/usr/bin/python3
ansible -i ./khanat_host -m ping khanat
lirria | SUCCESS => { "changed": false, "failed": false, "ping": "pong" } jukni | SUCCESS => { "changed": false, "failed": false, "ping": "pong" }
Playbooks
On va entrer dans le vif du sujet : automatiser des trucs.
Le principal intérêt d'Ansible est d'automatiser des opérations qu'on répète sur plusieurs serveurs ; dit autrement, Ansible permet (entre autres) de faire des opérations sur plusieurs serveurs et ce en parallèle.
Un exemple très bête : la mise à jour des dépôts, le téléchargement et déployement de nouveaux paquets. Le tout en gardant un œil quand même pour voir si tout se passe bien… Cette action est à faire régulièrement sur les serveurs : elle peut se scripter via Ansible4).
La première manipulation de cet article a permis d'envoyer une commande sur plusieurs serveurs (un ping) via l'option -m
. Pour passer plusieurs commandes, on crée des “playbooks”. Ce sont des fichiers écrits au format YAML. À noter qu'Ansible a pour but de favoriser la lecture par des êtres humains : il y a donc beaucoup de façons d'écrire ces playbooks…
Créez un répertoire dans lequel seront stockés vos playbooks, puis un playbook maj_base.yml
:
Pour exécuter le playbook :
ansible-playbook playbooks/maj_base.yml -K
Mais avant de l'exécuter, il faut l'écrire…
YAML est assez strict sur les indentations. Si vous avez des erreurs lors de vos premiers palybooks, c'est peut-être à cause d'une espace manquante…
Syntaxe
Dans les playbooks et la configuration
- Commentaires avec
#
: retour à la ligne avant le commentaire
Dans les appels en ligne de commande (plus de détails avec man ansible-playbook
) :
-K
: renseigner le mot de passe sudo-l
(“limit”) : appliquer uniquement au host.-l vm1
–step
: confirmer chaque étape avant de passer à la suivante–syntax-check
: vérifie la syntaxe sans exécuter le playbook-e
(“extra-vars”) : précise des variables additionnelles à utiliser dans le playbook. Par exemple-e “user=marcelle”
. Mais il existe aussi une façon de le demander dans l'execution du playbook (avec prompt).
Actions de sudoers sur les serveurs
Il est plus que probable que certains automatismes vous demande sudo5). Il faut donc donner le mot de passe sudoer à Ansible.
Pour cela, deux méthodes :
- la commande
-K
- le fichier
password.yml
Malheureusement je n'ai pas trouvé comment faire pour qu'Ansible demande le mot de passe de façon interactive, c'est à dire : seulement quand il a besoin de sudo. Généralement, -K
est le plus simple : lors de l'appel d'un playbook, on ajoute ça.
ansible-playbook superplaybook.yml -K
On peut aussi gérer des secrets dans un fichier chiffré, dont le mot de passe sudo.
Créer un fichier chiffré où stocker des infos sensibles :
ansible-vault create password.yml
Il faut définir un mot de passe pour ouvrir le fichier en question.
Dans le fichier en question, ajouter cette ligne pour stocker le mot de passe sudo :
ansible_become_pass: "mot_de_passe_sudo"
Pour éditer :
ansible-vault edit password.yml
Dans les playbooks, chaque fois qu'on a besoin de sudo, on aura ces lignes au début :
become: true vars_files: - password.yml
En adaptant le chemin à votre architecture de dossier, bien sûr.
On peut aussi ajouter ask_vault_pass = true
dans la section [defaults]
de ansible.cfg
: cela demandera le mot de passe du fichier vault chaque fois qu'on lance un playbook. Même quand il n'y en a pas besoin6).
Écrire un playbook
Pour la rédaction des playbooks, les possibilités sont nombreuses et il vaut mieux se référerer à la documentation officielle. Pour les options de base :
- Nous renseignons les hosts où le playbook sera lancé
- Nous listons les tâches qui seront exécutées dessus
Il est possible de renseigner des variables et diverses options.
Chaque tâche (task) démarre par un tiret -
après lequel on peut spécifier un ensemble d'options. On peut renseigner une variable “name”, qui nous indiquera, lors du lancement du playbook, à quel stade il en est. On peut faire appel au shell, ou même directement à un programme. On peut passer root via sudo, ou devenir un autre utilisateur (pour peu qu'on aie les droits), le temps d'une commande. On peut avoir des retours si un reboot est nécessaire, etc.
- name: Texte décrivant la tâche module: option=value
Commençons en douceur… on reproduit notre “ping” via le playbook.
- khanatping.yml
# Indiquer sur quels serveurs ce playbook va marcher, ici ceux du groupe "khanat" - hosts: khanat # Nous indiquons les tâches qui vont s'exécuter tasks: - action: ping
Lors de l'exécution de ansible-playbook -i ./khanat_host ./ansible/playbooks/khanatping.yml
, la réponse est affichée différemment de la commande ansible -i ./khanat_host -m ping khanat
mais le résultat est le même : ça marche.
Allons un peu plus loin et créons un playbook pour mettre à jour un serveur Debian. La lecture des possibilités offerte par les modules apt sous ansible est utile.
Les mises à jour doivent se faire avec les droits root. Mais comme on respecte les règles de sécurité, on ne se connecte jamais directement en root mais via un utilisateur ayant le droit d'utiliser ses superpouvoirs via sudo. Et toujours pour des questions de sécurité, cet utilisateur doit taper son mot de passe pour devenir root. On ajoutera donc la commande -K
en appelant le script. On va aussi ajouter dans le playbook de quoi passer en root quand nécessaire.
- maj_base.yml
--- # J'aime bien rappeler la commande avec les bonnes options à lancer pour le playbook au début # Ça aide quand on revient sur un playbook après quelques temps. # ansible-playbook playbooks/maj_base.yml -K -l HOST - name: Création d'une utilisatrice avec clé SSH # Nos hôtes concernés par le playbook # On pourrait mettre "all" au lieu de khanat # Puis préciser qu'on cible le groupe khanat avec "-l khanat" dans la commande hosts: khanat # On prévient que sur cet host et pour ce playbook, on va utiliser sudo. become: yes become_method: sudo # On renseigne les tâches et on leur donne des descriptions tasks: - name: mise à jour des dépôts apt: update_cache=yes - name: Mettre à jour les paquets apt: upgrade=yes - name: Vérifie s'il faut rebooter register: reboot stat: path=/var/run/reboot-required get_md5=no
Puis lancer, depuis le répertoire “Ansible” :
ansible-playbook playbooks/maj_base.yml --ask-become-pass
Insérer des playbooks dans des playbooks
Imbriquer les playbooks peut être utile. Par exemple, vous avez un playbook bien fait pour les mises à jour, mais vous souhaitez le lancer parfois pour un groupe de serveur, parfois pour un autre7).
Il y a plusieurs façon de réaliser des imbrications, définir des rôles, inclure des tâches… Cette dernière façon de faire est assez simple.
Créez votre premier playbook contenant vos hosts, et appelez dedans un second playbook, qui contiendra la liste des tâches :
- khanat.yml
- hosts: khanat tasks: - import_tasks: includeping.yml
- includeping.yml
- action: ping
Mises à jour Debian via Ansible
Pour les mises à jour sur Debian, utiliser apt-listbug est complexe avec Ansible, et sans, le risque existe de mettre à jour des paquets avec des bugs gênants et pourtant référencés. On travaille en stable, généralement, donc le risque n'est pas énorme, mais… il existe. Une solution est d'exécuter d'abord apt-listbug, d'examiner les rapports, puis de passer ensuite en variable les paquets à geler, et de faire le reste de la mise à jour.
Ce playbook n'est pas testé, je note ça en piste.
- apt-listbug.yml
- name: Lister les bugs critiques détectés par apt-listbugs shell: | apt-get update -qq && apt-listbugs -y -T critical,grave list upgrade register: bugscan ignore_errors: true - name: Afficher les bugs trouvés debug: var: bugscan.stdout_lines - name: Stopper si des bugs critiques sont listés fail: msg: "Des bugs critiques ont été détectés, mise à jour arrêtée." when: bugscan.stdout is search("BUG:")
Voir aussi la doc ansible sur Apt pour le reste de la mise à jour.
Astuces diverses
- Il est possible d'utiliser des conditions dans un playbook (“si… alors…”), ainsi que des boucles (loop)
- prompt permet d'afficher un prompt à l'exécutante du playbook, et de définir ainsi des variables à la demande.
Sources
- Ansible : mes premiers pas (memo-linux.com)
- Présentation d'Ansible et version 2 à venir (Linuxfr.org)
- Utilisation avancée : piste pour utiliser des boucles, en français. Le détail dans la documentation officielle en anglais
Ansible ou bash ?
Parce qu'il faut savoir critiquer !
Ansible est intéressant pour faire les mêmes choses sur plusieurs serveurs. D'un autre côté sa syntaxe particulière demande un apprentissage. Et je passe parfois un temps fou à trouver comment réaliser un truc avec Ansible, alors qu'avec bash, c'est 2 lignes de commandes.
Pour un certain nombre de tâches, Bash a un avantage indéniable. Il s'appuie sur les outils du système, il est robuste, et c'est rare que les syntaxes changent. Et il donne des retours directement. Couplé à Terminator qui permet de répéter la même commande dans plusieurs terminaux, il a une force de frappe impressionnante.
Parfois, Ansible ressemble à un tank sorti pour écraser une mouche. Parfois, c'est Bash. Pour chaque type d'action, ça mérite de se demander lequel va être le plus adapté, ou si une combinaison des deux sera l'idéal.
Ansible permet tout de même quelque chose d'assez amusant : envoyer votre collection de script bash sur vos serveurs, et les déclencher. Et ça, il y a pas mal de moments où ça peut simplifier la vie d'un sysadmin.
Pour le reste, si nous arrivons à avoir une collection de playbooks (et de scripts !) réellement pertinentes, on les partagera dans la forge.