La gestion automatisée de tout un parc de serveurs,
De l'installation d'applications à la configuration en passant par toutes les tâches de maintenance.
Tout est automatique grâce aux playbooks Ansible.
Pré-requis: maitriser le YAML, disposer d'un environnement virtuel en Python et disposer sur son système du programme sshpass.
Pour les systèmes à base de dnf
$ sudo dnf install python3 sshpass
Pour les systèmes à base de apt
$ sudo apt install python3 sshpass
Pour les systèmes à base de zypper
$ sudo zypper install python3 sshpass
Fichier attaché | Taille |
---|---|
logo ansible | 34.32 Ko |
Pour utiliser Ansible, il est nécessaire de disposer d'un environnement virtuel Python.
Pour ce faire, rien de plus simple:
1 - Création de l'environnement virtuel dans son dossier personnel
$ cd ~
$ python3.11 -m venv ansible
2 - Activation de l'environnement virtuel et effectuer la mise à jour de l'outil python pip
$ cd ansible/
$ source bin/activate
$ python3 -m pip install -U pip
3 - Installation du module Ansible
$ python3 -m pip install -U ansible
4 - Vérifier la bonne installation de Ansible
$ ansible --version
ansible [core 2.16.7]
config file = None
configured module search path = ['/home/ronan/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /home/ronan/ansible/lib/python3.11/site-packages/ansible
ansible collection location = /home/ronan/.ansible/collections:/usr/share/ansible/collections
executable location = /home/ronan/ansible/bin/ansible
python version = 3.11.3 (main, Aug 9 2023, 09:30:45) [GCC 8.5.0 20210514 (Red Hat 8.5.0-18)] (/home/ronan/ansible/bin/python3)
jinja version = 3.1.4
libyaml = True
$ ansible -m ping localhost
[WARNING]: No inventory was parsed, only implicit localhost is available
localhost | SUCCESS => {
"changed": false,
"ping": "pong"
}
Et voilà, l'environnement virtuel Python pour Ansible est prêt et opérationnel.
Pour fonctionner, Ansible a besoin de connaitre la liste exhaustive de tous les serveurs qu'il doit administrer.
La documentation Ansible en ligne
1 - Création du fichier hosts
$ vim hosts
2 - Le contenu du fichier doit ressembler à ceci
$ cat hosts
[nginx]
SERVER1
SERVER2
[rabbitmq]
SERVER3
[glpi]
SERVER4
[docker]
SERVER5
SERVER6
[prod]
SERVER1
SERVER3
SERVER4
SERVER5
[dev]
SERVER2
SERVER6
Il est possible d'indiquer entre [...] un terme permettant de regrouper les différents serveurs en catégorie.
La commande suivante permet d'afficher tous les hosts présents dans le fichier inventaire.
$ ansible-inventory --graph
@all:
|--@ungrouped:
|--@nginx:
| |--SERVER1
| |--SERVER2
|--@rabbitmq:
| |--SERVER3
|--@glpi:
| |--SERVER4
|--@docker:
| |--SERVER5
| |--SERVER6
|--@prod:
| |--SERVER1
| |--SERVER3
| |--SERVER4
| |--SERVER5
|--@dev:
| |--SERVER2
| |--SERVER6
... d'un groupe en particulier
$ ansible-inventory --graph nginx
@nginx:
|--SERVER1
|--SERVER2
Le fichier ansible.cfg n'est pas forcément nécessaire pour l'utilisation d'Ansible, mais il est fortement conseillé pour la simplifier.
Documentation Ansible en ligne
Voici un exemple limité des principaux paramètres à renseigner.
$ cat ansible.cfg
[defaults]
inventory = ./hosts
interpreter_python = /usr/bin/python3
host_key_checking = False
log_path = ./ansible.log
remote_user = root
- On y indique l'emplacement du fichier inventaire hosts
- L'interpréteur python à utiliser sur les serveurs distants
- On ne vérifie pas les clés des serveurs distants. Cela évite d'avoir tous les serveurs de renseignés dans le fichier ~/.ssh/know_hosts
- On renseigne le fichier pour les logs
- Le user utilisé pour se connecter aux différents serveurs
Il est possible de générer automatiquement le fichier de configuration via cette commande
$ ansible-config init --disabled > ansible.cfg
Pour éviter, à chaque fois, de saisir le mot de passe (option -k) permettant de se connecter aux serveurs distants, il est possible d'utiliser une connexion par clé ssh.
La documentation Ansible en ligne
Tout est indiqué ici pour générer une clé ssh.
Une fois la clé ssh générée, il faut la copier sur tous les serveurs distants.
Pour cela, nous allons utiliser Ansible pour le faire à grande échelle.
Nous allons donc utiliser la commande authorized_key pour le faire.
$ ansible -m authorized_key -u mon_user -a "user=mon_user exclusive=true state=present key=""{{ lookup('file', '~/.ssh/id_rsa.pub') }}""" all -k
- L'option -m authorized_keys permet d'indiquer que nous souhaitons utiliser la commande authorized_keys
- L'option -u mon_user permet d'indiquer avec quel utilisateur se connecter aux serveurs distants.
- L'option -a "..." permet de renseigner les arguments nécessaires pour la commande authorized_keys. On y indique le user concerné, le terme exclusive permet d'indiquer qu'il doit y avoir une seule clé ssh d'autorisée, le terme state permet d'indiquer que la clé publique doit être présente (absent si l'on souhaite la supprimer) et enfin, le terme key permet, grâce à la fonction lookup, de copier le contenu du fichier ~/.ssh/id_rsa.pub du serveur local dans le fichier authorized_keys du ou des serveurs distants.
- Le mot clé all permet d'indiquer que l'opération doit être effectuée sur tous les hosts présents dans l'inventaire.
- L'option -k permet de saisir le mot de passe pour se connecter aux serveurs distants.
Voici quelques exemples d'utilisation des différentes commandes disponibles avec Ansible
La première commande Ansible que nous allons utiliser est la commande ping.
La documentation Ansible en ligne
Cette commande va permettre de tester si tous les serveurs sont accessibles et disponibles.
L'option -k permet de saisir le mot de passe pour se connecter aux serveurs. Le mot de passe doit être identique à tous les serveurs. Pour éviter cette manipulation, il est possible d'utiliser une connexion via une clé ssh.
L'option -m permet d'indique la commande à utiliser (ping).
Le mot clé all permet d'indiquer que nous souhaitons exécuter la commande sur tous les hosts présents dans le fichier inventaire.
$ ansible -k -m ping all
SSH password:
SERVER5 | SUCCESS => {
"changed": false,
"ping": "pong"
}
SERVER2 | SUCCESS => {
"changed": false,
"ping": "pong"
}
SERVER1 | SUCCESS => {
"changed": false,
"ping": "pong"
}
SERVER3 | SUCCESS => {
"changed": false,
"ping": "pong"
}
SERVER4 | SUCCESS => {
"changed": false,
"ping": "pong"
}
SERVER6 | SUCCESS => {
"changed": false,
"ping": "pong"
}
Si je souhaite exécuter la commande ping uniquement sur mes hosts nginx
$ ansible -k -m ping nginx
SSH password:
SERVER2 | SUCCESS => {
"changed": false,
"ping": "pong"
}
SERVER1 | SUCCESS => {
"changed": false,
"ping": "pong"
}
Rien de plus simple.
La comande setup permet d'obtenir un inventaire exhaustif des hosts de l'inventaire.
La documentation Ansible en ligne
Attention, cette commande retourne un json de plus de 1000 lignes contenant toutes les informations du ou des hosts interrogés.
Chaque clé du json peut être utilisée dans les playbooks Ansible grâce à la variable ansible_facts.
$ ansible -m setup SERVER1
SERVER1 | SUCCESS => {
"ansible_facts": {
"ansible_all_ipv4_addresses": [
"10.50.2.253",
"172.17.0.1"
],
...
"module_setup": true
},
"changed": false
}
Il est possible de filtrer directement sur un paramètre précis.
$ ansible -m setup SERVER1 -a "filter=ansible_processor_nproc"
SERVER1 | SUCCESS => {
"ansible_facts": {
"ansible_processor_nproc": 2
},
"changed": false
}
Pour info, tout comme les hosts présents dans le fichier inventaire, le serveur Ansible lui-même peut être utilisé grâce au mot clé localhost.
$ ansible -m setup localhost -a "filter=ansible_loadavg"
localhost | SUCCESS => {
"ansible_facts": {
"ansible_loadavg": {
"15m": 0.43,
"1m": 1.9,
"5m": 0.78
}
},
"changed": false
}
La commande command permet d'exécuter une commande simple.
Voir la commande shell pour des exécutions plus complexes.
La documentation Ansible en ligne
Le commande n'étant pas exécutée par l'interpréteur de commande, l'utilisation des caractères spéciaux tels que * < > | ; & ne fonctionne pas.
Quelques exemples:
$ ansible -m command -a "uptime" SERVER1
SERVER1 | CHANGED | rc=0 >>
13:37:18 up 12 days, 3:58, 2 users, load average: 0,16, 0,11, 0,25
$ ansible -m command -a "w" SERVER2
SERVER2 | CHANGED | rc=0 >>
13:41:22 up 12 days, 4:02, 2 users, load average: 0,10, 0,09, 0,20
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
user pts/0 10.99.17.12 lun.10 2.00s 1.31s 0.00s w
user pts/1 10.99.17.12 09:53 34:37 0.25s 0.01s sshd: user [priv]
$ SERVER3 -m command -a "date" SERVER3
localhost | CHANGED | rc=0 >>
mer. juin 5 13:55:55 CEST 2024
La commande shell permet d'exécuter des commandes simples mais aussi complexes et également sous forme de scripts.
Documentation Ansible en ligne
Exemples:
$ ansible -m shell -a 'find /var/log/nginx/ -type f -name "access.log*" | xargs stat --printf "%-80n%-15s%-35w\n"' nginx
SERVER2 | CHANGED | rc=0 >>
/var/log/nginx/access.log-20240116 7052050 2023-11-15 17:37:33.620096531 +0100
/var/log/nginx/access.log 10191 2024-01-16 09:28:20.137458998 +0100
SERVER1 | CHANGED | rc=0 >>
/var/log/nginx/access.log-20240116 2101711 2023-11-15 16:03:11.480149331 +0100
/var/log/nginx/access.log 22278 2024-01-16 09:29:28.959290202 +0100
$ ansible -m shell -a 'cat /etc/passwd | grep root' SERVER1:SERVER2
SERVER1 | CHANGED | rc=0 >>
root:x:0:0:root:/root:/bin/bash
SERVER2 | CHANGED | rc=0 >>
root:x:0:0:root:/root:/bin/bash
$ ansible -m shell -a 'echo "Hello world!" > /tmp/hello_world ; ls -l /tmp/hello_world ; cat /tmp/hello_world' localhost
localhost | CHANGED | rc=0 >>
-rw-r--r-- 1 user group 13 5 juin 14:20 /tmp/hello_world
Hello world!
Quelques exemples de playbooks.
Voici un template basique pour démarrer un nouveau playbook Ansible.
Le format est en YAML.
Il faut être très rigoureux concernant l'indentation.
Enormément d'erreurs sont dues à une mauvaise indentation.
La clé name permet de nommer le playbook
La clé hosts permet d'indiquer la liste des hosts présents dans l'inventaire à utiliser
La clé gather_facts permet d'obtenir un inventaire complet des hosts (true ou false)
La clé tasks permet de renseigner toutes les tâches que le playbook doit exécuter.
Concernant la clé hosts, la sélection est multiple.
Par exemple, le mot clé all permet d'indiquer la liste complète de tous les hosts.
Si je veux un host en particulier
# (attention à la casse pour les nom de hosts).hosts: SERVER1
Si je veux deux hosts
hosts: SERVER1:SERVER2
Si je veux tous les hosts exceptés le SERVER2
hosts: all:!SERVER2
Si je veux mes hosts nginx de prod
hosts: nginx:&prod
Voici un template plus élaboré pour démarrer un nouveau playbook Ansible et prenant en compte des variables.
Quelques petites explications:
- gather_facts (true/false) permet d'obtenir l'inventaire du ou des hosts
- gather_subset (liste exhaustive) permet d'obtenir des informations supplémentaire concernant le ou les hosts
- become (true/false) permet d'obtenir les droits SUDO
- serial (true/false) permet d'indiquer s'il faut exécuter les tâches en série, host par host ou simultanément sur tous les hosts
- collections permet d'inclure des plugins Ansible supplémentaires
- vars permet d'inclure différentes variables (string, int, dict, list)
- vars_prompt permet d'inclure des variables initialisées par un prompt lors de l'exécution du playbook
- pre_tasks permet d'exécuter des tâches avant l'exécution des tâches du playbook (j'inclus des variables globales grâce à include_vars)
Pour info, la commande lookup('pipe', ...) permet de récupérer le résultat de la sortie standard de la commande passée en paramètre.
Plus d'info ici