Ansible : Automatiser la gestion de serveurs
Table des matières
Ansible est un logiciel de déploiement/configuration à distance, créé par M. De Hann ( Redhat : Cobbler et Func) et est écrit en python. Il utilise seulement SSH et ne nécessite pas de serveur : une simple station de travail peut suffire. Il est bon de noter qu'il fonctionne en mode push (de Ansible vers le serveur cible). La machine hébergeant Ansible ne nécessite que python 2.4+, et Ansible est extensible en n'importe quel langage.
Cette solution est plutôt dédiée à un usage professionnel.
J'ai mis en œuvre Ansible sur plusieurs systèmes différents : Gentoo et CentOS.
Je décris ici l'installation des deux systèmes.
Il est nécessaire d'installer les dépôts EPEL.
Vous pouvez aussi installer le paquet centos-release-ansible pour activer le dépôt Ansible
Ensuite, installer Ansible via
CentOS 7 :
CentOS 8 :
Ansible est disponible directement, et installable via
Sur la machine qui possède Ansible, configurer le fichier /etc/hosts avec le nom des hôtes (si DNS non fonctionnel) :
On génère ensuite une paire de clés SSH :
Puis on copie notre clé sur chaque serveur :
On peut vérifier le bon fonctionnement en se connectant à chacune des machines...
Tout se passe dans le fichier /etc/ansible/hosts. On édite le fichier. Voici un exemple :
Entre crochet, on trouve les groupes d'hôtes, avec les hôtes concernés en dessous.
Ici, j'ai donc 5 groupes, un rhel7 avec les hôtes samba et lamp01 et le groupe cluster avec cluster1 et cluster2 etc...
Pour me simplifier la vie, j'ai créé un dossier ansible dans /root et j'ai fait des liens symboliques vers les fichiers de config.
C'est dans /root/ansible que je mets tous mes playbooks (on verra plus bas) :
Ansible s'articule autour de modules.
Plus d'infos sur les modules : https://docs.ansible.com/ansible/latest/collections/all_plugins.html
Pour tester la communication, on peut utiliser le module ping d'Ansible :
all ici signifie tous les hôtes :
Pour tester le bon fonctionnement, on va installer htop sur inf en utilisant le module dnf:
La console affiche l'état des opérations :
On peut aussi tester sur Gentoo en installant aussi htop sur les 2 machines du groupe cluster en utilisant le module portage:
La console affiche moins de choses, puisque les paquets sont déjà installés :
Et un test sur debian, avec le module apt, installons htop :
Par exemple, pour mettre à jour les serveurs rhel 7 :
Un playbook est une sorte de mégascript qui va automatiser des tâches de manière séquentielle.
Le chapeau contient :
Les hôtes peuvent être : 1 hôte ou all
Globalement, un playbook est composé de tâches comme ceci :
Voici une illustration avec un playbook "test" pour installer htop sous Gentoo :
TODO Syntaxe ancienne a réviser
Ici, je vise les hôtes du groupe cluster, et le playbook ne comporte qu'une seule tâche.
On lance le playbook via la commande
Une autre expérience intéressante consiste à relancer l’exécution du playbook
Tout devrait aller beaucoup plus vite, et à la place de "changed" après chaque instruction, on lit "ok". Les paquets ne sont pas réinstallés s'ils sont déjà présents.
Ce qui veut dire qu’un playbook est plus intelligent qu’un bête script, et ne se contente pas d’exécuter des instructions.
Ansible va garantir quel tel service soit bien actif et qu’il utilise bien le dernier fichier de conf. Ce qui en fait l’outil parfait pour tester vos systèmes automatiquement.
On peut aussi ajouter une section handlers. Elle permet d'effectuer des actions quand il y a eu un changement. Exemple ici de redémarrage sur RHEL du service httpd :
Ci-dessous, un playbook que j'ai réalisé, pour installer LAMP sur Gentoo :
Cette fois-ci, il n'y a pas que des installations ...
TODO Syntaxe ancienne a réviser
Le fichier lamp-gentoo.yml.portage.use.php contient ceci :
Si on souhaite modifier un USE, on édite le fichier lamp-gentoo.yml.portage.use.php et on relance le playbook. Ce fichier sera mis à jour automatiquement sur les serveurs cible, et php recompilé. Apache par exemple n'étant pas modifié, il ne sera pas recompilé.
Voici l'illustration de l'exécution du playbook :
Ci-dessous, un playbook que j'ai réalisé, pour installer LAMP sur RHEL et dérivées.
A noter l'apparition d'une nouvelle section nommée handlers comme expliquée précédemment.
Et pour pimenter les choses, l'étape 3 installe plusieurs paquets d'un coup et à l'étape 8 je vous montre une autre syntaxe :
Après mise à jour du système :
Ce playbook est donc bien complet.
Voici une illustration d'exécution du playbook :
Cette fois-ci un playbook d'installation de SAMBA sur Debian.
A noter qu'il est déjà installé sur le serveur connex, comme le montrera l'exécution du playbook.
Le playbook : samba-debian.yml
TODO Syntaxe ancienne a réviser
Le fichier samba-debian.yml.smb.conf.ansible
On exécute ensuite le playbook :
La console affiche :
Cette fois-ci un playbook "universel" pour mettre à jour ses serveurs !
Le playbook : linux-update.yml
TODO Syntaxe ancienne a réviser
On exécute ensuite le playbook :
Tous les hôtes sont concernés.
Afin de rendre universel le playbook, j'ai utilisé ansible.builtin.package au lieu de dnf ou apt. C'est moins complet que ces derniers, mais pour une mise à jour ça fait l'affaire.
J'ai aussi utilisé when qui permet de définir une condition en fonction ici de la famille du système ! En effet, entre RedHat et Debian, certains services diffèrent. Le problème est donc réglé avec ces conditions !
Introduction
Ansible est un logiciel de déploiement/configuration à distance, créé par M. De Hann ( Redhat : Cobbler et Func) et est écrit en python. Il utilise seulement SSH et ne nécessite pas de serveur : une simple station de travail peut suffire. Il est bon de noter qu'il fonctionne en mode push (de Ansible vers le serveur cible). La machine hébergeant Ansible ne nécessite que python 2.4+, et Ansible est extensible en n'importe quel langage.
Cette solution est plutôt dédiée à un usage professionnel.
Installer Ansible
J'ai mis en œuvre Ansible sur plusieurs systèmes différents : Gentoo et CentOS.
Je décris ici l'installation des deux systèmes.
CentOS
Il est nécessaire d'installer les dépôts EPEL.
Vous pouvez aussi installer le paquet centos-release-ansible pour activer le dépôt Ansible
Ensuite, installer Ansible via
CentOS 7 :
Code BASH :
yum install ansible
CentOS 8 :
Code BASH :
dnf install ansible
Gentoo
Ansible est disponible directement, et installable via
Code BASH :
emerge -av app-admin/ansible
Préparer le terrain
Configurer le "serveur" Ansible
Sur la machine qui possède Ansible, configurer le fichier /etc/hosts avec le nom des hôtes (si DNS non fonctionnel) :
Code BASH :
127.0.0.1 localhost.localdomain localhost 192.168.21.11 cluster1.adrien.lan cluster1 192.168.21.12 cluster2.adrien.lan cluster2 192.168.21.13 inf 192.168.21.14 samba 192.168.21.15 lamp01 192.168.21.16 sftp 192.168.21.17 zabbix 192.168.21.18 ocs 192.168.21.19 glpi 192.168.21.21 cluster1 192.168.21.22 cluster2 192.168.21.23 connex
Partie SSH
On génère ensuite une paire de clés SSH :
Code BASH :
ssh-keygen -t ecdsa -b 384
Puis on copie notre clé sur chaque serveur :
Code BASH :
ssh-copy-id cluster1 ssh-copy-id cluster2 ssh-copy-id zabbix ssh-copy-id ocs
On peut vérifier le bon fonctionnement en se connectant à chacune des machines...
Configuration des hôtes Ansible
Tout se passe dans le fichier /etc/ansible/hosts. On édite le fichier. Voici un exemple :
Code BASH :
[test] inf [rhel7] samba [rhel8] sftp zabbix ocs glpi srv-web01 [cluster] cluster1 cluster2 [deb] connex
Entre crochet, on trouve les groupes d'hôtes, avec les hôtes concernés en dessous.
Ici, j'ai donc 5 groupes, un rhel7 avec les hôtes samba et lamp01 et le groupe cluster avec cluster1 et cluster2 etc...
Pour se simplifier la vie
Pour me simplifier la vie, j'ai créé un dossier ansible dans /root et j'ai fait des liens symboliques vers les fichiers de config.
C'est dans /root/ansible que je mets tous mes playbooks (on verra plus bas) :
Code BASH :
mkdir /root/ansible cd /root/ansible ln -sv /etc/ansible/hosts ansible-hosts ln -sv /etc/hosts system-hosts
Utiliser Ansible
Ansible s'articule autour de modules.
Plus d'infos sur les modules : https://docs.ansible.com/ansible/latest/collections/all_plugins.html
Tester la communication
Pour tester la communication, on peut utiliser le module ping d'Ansible :
Code BASH :
ansible -m ping all
all ici signifie tous les hôtes :
Code BASH :
inf | success >> { "changed": false, "ping": "pong" } cluster1 | success >> { "changed": false, "ping": "pong" } cluster2 | success >> { "changed": false, "ping": "pong" } samba | success >> { "changed": false, "ping": "pong" } srv-web01 | success >> { "changed": false, "ping": "pong" } sftp | success >> { "changed": false, "ping": "pong" } zabbix | success >> { "changed": false, "ping": "pong" } ocs | success >> { "changed": false, "ping": "pong" } glpi | success >> { "changed": false, "ping": "pong" } connex | success >> { "changed": false, "ping": "pong" }
En mode "on-liner"
Pour tester le bon fonctionnement, on va installer htop sur inf en utilisant le module dnf:
Code BASH :
ansible -m dnf -a 'name=htop state=present' inf
La console affiche l'état des opérations :
Code BASH :
inf | CHANGED => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "changed": true, "msg": "", "rc": 0, "results": [ "Installed: htop-3.0.5-4.fc34.x86_64" ] }
On peut aussi tester sur Gentoo en installant aussi htop sur les 2 machines du groupe cluster en utilisant le module portage:
Code BASH :
ansible -m portage -a 'name=htop state=present' cluster
La console affiche moins de choses, puisque les paquets sont déjà installés :
Code BASH :
cluster1 | success >> { "changed": false, "msg": "Packages already present." } cluster2 | success >> { "changed": false, "msg": "Packages already present." }
Et un test sur debian, avec le module apt, installons htop :
Code BASH :
ansible -m apt -a 'name=htop' connex
Par exemple, pour mettre à jour les serveurs rhel 7 :
Code BASH :
ansible -m yum -a 'name=* state=latest' rhel7
Utiliser les playbooks
Un playbook est une sorte de mégascript qui va automatiser des tâches de manière séquentielle.
Le chapeau contient :
Code BASH :
---
- hosts: hotes
tasks:
Les hôtes peuvent être : 1 hôte ou all
Globalement, un playbook est composé de tâches comme ceci :
Code BASH :
- name: Texte qui décrit votre tâche module: option: valeur
Un exemple simple
Voici une illustration avec un playbook "test" pour installer htop sous Gentoo :
Code BASH :
vi htop.yml
TODO Syntaxe ancienne a réviser
Code BASH :
--- - hosts: cluster tasks: - name: 1. install htop portage: name=htop state=present
Ici, je vise les hôtes du groupe cluster, et le playbook ne comporte qu'une seule tâche.
On lance le playbook via la commande
Code BASH :
ansible-playbook htop.yml
Code BASH :
PLAY [cluster] **************************************************************** GATHERING FACTS *************************************************************** ok: [cluster2] ok: [cluster1] TASK: [1. install htop] ******************************************************* changed: [cluster1] changed: [cluster2] PLAY RECAP ******************************************************************** cluster1 : ok=2 changed=1 unreachable=0 failed=0 cluster2 : ok=2 changed=1 unreachable=0 failed=0
Une autre expérience intéressante consiste à relancer l’exécution du playbook
Code BASH :
PLAY [cluster] **************************************************************** GATHERING FACTS *************************************************************** ok: [cluster2] ok: [cluster1] TASK: [1. install htop] ******************************************************* ok: [cluster1] ok: [cluster2] PLAY RECAP ******************************************************************** cluster1 : ok=2 changed=0 unreachable=0 failed=0 cluster2 : ok=2 changed=0 unreachable=0 failed=0
Tout devrait aller beaucoup plus vite, et à la place de "changed" après chaque instruction, on lit "ok". Les paquets ne sont pas réinstallés s'ils sont déjà présents.
Ce qui veut dire qu’un playbook est plus intelligent qu’un bête script, et ne se contente pas d’exécuter des instructions.
Ansible va garantir quel tel service soit bien actif et qu’il utilise bien le dernier fichier de conf. Ce qui en fait l’outil parfait pour tester vos systèmes automatiquement.
On peut aussi ajouter une section handlers. Elle permet d'effectuer des actions quand il y a eu un changement. Exemple ici de redémarrage sur RHEL du service httpd :
Code BASH :
--- - hosts: rhel8 tasks: - name: 1. MàJ dnf: name: "*" state: latest handlers: - name: Restart apache service: name: httpd state: restarted
Exemples de Playbook
Installer LAMP sur Gentoo
Ci-dessous, un playbook que j'ai réalisé, pour installer LAMP sur Gentoo :
Cette fois-ci, il n'y a pas que des installations ...
Code BASH :
vi lamp-gentoo.yml
TODO Syntaxe ancienne a réviser
Code BASH :
--- - hosts: cluster tasks: - name: 1. Installation Apache portage: name=www-servers/apache state=present - name: 2. Installation fichier des USE copy: src=lamp-calculate.yml.portage.use.php dest=/etc/portage/package.use/php - name: 3. Installation PHP portage: name=dev-lang/php state=present - name: 4. Installation de MySQL portage: name=dev-db/mysql - name: 5. Démarrage Apache service: name=apache2 state=running enabled=yes - name: 6. Initialisation MySQL command: /usr/share/mysql/scripts/mysql_install_db chdir=/usr - name: 7. Démarrage MySQL service: name=mysql state=running enabled=yes
- Installation d'un paquet.
- Copie d'un fichier vers le serveur cible.
- Installation d'un paquet.
- Installation d'un paquet.
- Vérification d'un service sur la position "démarré"
- Exécution d'une commande sur la machine cible, en l'exécutant depuis le répertoire /usr
- Démarrage d'un service.
Le fichier lamp-gentoo.yml.portage.use.php contient ceci :
Code BASH :
dev-lang/php gd apache2 mysqli pdo zip soap app-admin/eselect-php apache2
Si on souhaite modifier un USE, on édite le fichier lamp-gentoo.yml.portage.use.php et on relance le playbook. Ce fichier sera mis à jour automatiquement sur les serveurs cible, et php recompilé. Apache par exemple n'étant pas modifié, il ne sera pas recompilé.
Voici l'illustration de l'exécution du playbook :
Code BASH :
ansible-playbook lamp-gentoo.yml
Code BASH :
PLAY [cluster] **************************************************************** GATHERING FACTS *************************************************************** ok: [cluster2] ok: [cluster1] TASK: [1. Installation Apache] ************************************************ changed: [cluster2] changed: [cluster1] TASK: [2. Installation fichier des USE] *************************************** changed: [cluster1] changed: [cluster2] TASK: [3. Installation PHP] *************************************************** changed: [cluster2] changed: [cluster1] TASK: [4. Installation de MySQL] ********************************************** changed: [cluster2] changed: [cluster1] TASK: [5. Démarrage Apache] *************************************************** changed: [cluster2] changed: [cluster1] TASK: [6. Initialisation MySQL] *********************************************** changed: [cluster2] changed: [cluster1] TASK: [7. Démarrage MySQL] **************************************************** changed: [cluster2] changed: [cluster1] PLAY RECAP ******************************************************************** cluster1 : ok=8 changed=7 unreachable=0 failed=0 cluster2 : ok=8 changed=7 unreachable=0 failed=0
Installer LAMP, sous RHEL
Ci-dessous, un playbook que j'ai réalisé, pour installer LAMP sur RHEL et dérivées.
A noter l'apparition d'une nouvelle section nommée handlers comme expliquée précédemment.
Et pour pimenter les choses, l'étape 3 installe plusieurs paquets d'un coup et à l'étape 8 je vous montre une autre syntaxe :
Code BASH :
vi rhel-lamp.yaml
Code BASH :
--- - hosts: srv-web01 handlers: - name: restart httpd service: name: httpd state: restarted tasks: - name: 0. MàJ système dnf: name: "*" state: latest - name: 1. Installation Apache dnf: name: httpd state: latest - name: 2. Installation PHP dnf: name: php state: latest - name: 3. Installation extensions PHP dnf: name : - php-pdo - php-soap - php-gd state: latest - name: 4. Installation de MariaDB dnf: name: mariadb-server state: latest - name: 5. Démarrage Apache service: name: httpd state: started enabled: yes - name: 6. Démarrage MariaDB service: name: mariadb state: started enabled: yes - name: 7. Installation index copy: src: rhel-lamp.index.php dest: /var/www/html/index.php owner: apache group: apache mode: 0644 - name: 8. Ajout de la regle de parefeu ansible.posix.firewalld: zone: public service: "{{ item }}" permanent: yes state: enabled immediate: yes with_items: - http - https
Après mise à jour du système :
- Installation d'un paquet.
- Installation d'un paquet.
- Illustration de l'installation de plusieurs paquets dans une même tâche.
- Installation d'un paquet.
- Vérification d'un service sur la position "démarré" et activé au boot
- Vérification d'un service sur la position "démarré" et activé au boot
- Copier Coller d'un fichier du "serveur Ansible" vers le serveur cible.
- Ouverture du pare-feu
Ce playbook est donc bien complet.
Voici une illustration d'exécution du playbook :
Code BASH :
ansible-playbook lamp-centos.yml
Code BASH :
PLAY [srv-web01] *************************************************************** TASK [Gathering Facts] ********************************************************* ok: [srv-web01] TASK [0. MàJ système] ********************************************************** changed: [srv-web01] TASK [1. Installation Apache] ************************************************** changed: [srv-web01] TASK [2. Installation PHP] ***************************************************** changed: [srv-web01] TASK [3. Installation extensions PHP] ****************************************** changed: [srv-web01] TASK [4. Installation de MariaDB] ********************************************** changed: [srv-web01] TASK [5. Démarrage Apache] ***************************************************** changed: [srv-web01] TASK [6. Démarrage MariaDB] **************************************************** changed: [srv-web01] TASK [7. Installation index] *************************************************** changed: [srv-web01] TASK [8. Ajout de la regle de parefeu] ***************************************** changed: [srv-web01] => (item=http) changed: [srv-web01] => (item=https) PLAY RECAP ********************************************************************* srv-web01 : ok=10 changed=9 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Installer et Configurer SAMBA, sous Debian
Cette fois-ci un playbook d'installation de SAMBA sur Debian.
A noter qu'il est déjà installé sur le serveur connex, comme le montrera l'exécution du playbook.
Le playbook : samba-debian.yml
Code BASH :
vi samba-debian.yml
TODO Syntaxe ancienne a réviser
Code BASH :
--- - hosts: connex handlers: - name: restart samba service: name=samba state=restarted tasks: - name: 1. Installer samba apt: name=samba state=present - name: 2. Démarrer service samba service: name=samba state=running enabled=yes - name: 3. Création du dossier public command: mkdir -p /srv/samba/commun - name: 4. Correction des droits file: path=/srv/samba/commun owner=root group=users mode=0777 - name: 5. Modification du fichier smb.conf lineinfile: dest=/etc/samba/smb.conf backup=yes line="include /etc/samba/smb.conf.ansible" - name: 6 Copier le fichier personnalisé de samba copy: src=samba-debian.yml.smb.conf.ansible dest=/etc/samba/smb.conf.ansible notify: - restart samba
Le fichier samba-debian.yml.smb.conf.ansible
Code BASH :
[commun] comment = Partage de fichiers commun browseable = yes public = yes path = /srv/samba/commun writable = yes
On exécute ensuite le playbook :
Code BASH :
ansible-playbook samba-debian.yml
La console affiche :
Code BASH :
PLAY [connex] *********************************************************** GATHERING FACTS *************************************************************** ok: [connex] TASK: [1. Installer samba] **************************************************** ok: [connex] TASK: [2. Démarrer service samba] ********************************************* ok: [connex] TASK: [3. Création du dossier public] ***************************************** changed: [connex] TASK: [4. Correction des droits] ********************************************** ok: [connex] TASK: [5. Modification du fichier smb.conf] *********************************** changed: [connex] TASK: [6 Copier le fichier personnalisé de samba] ***************************** changed: [connex] NOTIFIED: [restart samba] ***************************************************** changed: [connex] PLAY RECAP ******************************************************************** connex : ok=8 changed=4 unreachable=0 failed=0
- Installation d'un paquet.
- Démarrage d'un service.
- Exécution d'une commande.
- Modification de droits.
- Ajout d'une ligne dans un fichier.
- Copie de fichier + notification
Mettre à jour ses serveurs
Cette fois-ci un playbook "universel" pour mettre à jour ses serveurs !
Le playbook : linux-update.yml
Code BASH :
vi linux-update.yml
TODO Syntaxe ancienne a réviser
Code BASH :
--- - hosts: all tasks: - name: 1. MàJ ansible.builtin.package: name=* state=latest handlers: - name: Restart apache service: name=httpd state=restarted when: ansible_os_family == 'RedHat' - name: Restart php-fpm service: name=php-fpm state=restarted when: ansible_os_family == 'RedHat' - name: Restart apache service: name=apache2 state=restarted when: ansible_os_family == 'Debian' - name: Restat Zabbix-agent service: name=zabbix-agent state=restarted when: ansible_os_family == 'RedHat' - name: Restart Zabbix-server service: name=zabbix-server state=restarted when: ansible_os_family == 'RedHat' - name: Restart SSH service: name=sshd state=restarted when: ansible_os_family == 'RedHat' - name: Restart SSH service: name=ssh state=restarted when: ansible_os_family == 'Debian' - name: Restart Samba service: name=smbd state=restarted when: ansible_os_family == 'RedHat'
On exécute ensuite le playbook :
Code BASH :
ansible-playbook linux-update.yml
Tous les hôtes sont concernés.
Afin de rendre universel le playbook, j'ai utilisé ansible.builtin.package au lieu de dnf ou apt. C'est moins complet que ces derniers, mais pour une mise à jour ça fait l'affaire.
J'ai aussi utilisé when qui permet de définir une condition en fonction ici de la famille du système ! En effet, entre RedHat et Debian, certains services diffèrent. Le problème est donc réglé avec ces conditions !