Services et serveurs

Ansible : Automatiser la gestion de serveurs

Cet article a été mis à jour, vous consultez ici une archive de cet article!
Table des matières

ansible



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...

Il est tout à fait possible d'avoir Ansible installé sur une machine Gentoo, et d'installer des paquets sur une distribution Linux autre (CentOS, Debian ...)

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



A propos des 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


Exécuter le playbook sur d'autres hôtes que ceux prévus



Dans le cas d'un playbook prévu ainsi, avec all comme hôtes :

Code BASH :
---
- hosts: all
  tasks:
    - name: 1. MàJ
      dnf:
        name: "*" 
        state: latest
 


Si ce playbook est exécuté avec :

Code BASH :
ansible-playbook all-linux.yml


Tous les hôtes vont être concernés.
On peut alors spécifier uniquement certains hôtes en indiquant l'option -l (L minuscule). Exemple ici avec l'hôte ocs :

Code BASH :
ansible-playbook -l ocs all-linux.yml



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


  1. Installation d'un paquet.
  2. Copie d'un fichier vers le serveur cible.
  3. Installation d'un paquet.
  4. Installation d'un paquet.
  5. Vérification d'un service sur la position "démarré"
  6. Exécution d'une commande sur la machine cible, en l'exécutant depuis le répertoire /usr
  7. 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 :
  1. Installation d'un paquet.
  2. Installation d'un paquet.
  3. Illustration de l'installation de plusieurs paquets dans une même tâche.
  4. Installation d'un paquet.
  5. Vérification d'un service sur la position "démarré" et activé au boot
  6. Vérification d'un service sur la position "démarré" et activé au boot
  7. Copier Coller d'un fichier du "serveur Ansible" vers le serveur cible.
  8. 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
 


  1. Installation d'un paquet.
  2. Démarrage d'un service.
  3. Exécution d'une commande.
  4. Modification de droits.
  5. Ajout d'une ligne dans un fichier.
  6. 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 !