AnsibleVoici le onzième article de la formation Ansible. Dans notre précédent article, nous avons installé un serveur web simple à l’aide d’un playbook Ansible. Aujourd’hui nous allons peaufiner la configuration d’Apache, ce qui nous permettra de découvrir un autre concept fondamental : les handlers.

Exécuter une tâche en cas de changement

Placez-vous dans le répertoire du onzième atelier pratique :

$ cd ~/formation-ansible/atelier-11

Voici les quatre machines virtuelles de cet atelier :

Machine virtuelle Adresse IP
control 10.23.45.10
target01 10.23.45.20
target02 10.23.45.30
target03 10.23.45.40

Toutes ces VM tournent sous Debian. Démarrez-les :

$ vagrant up

Connectez-vous au Control Host :

$ vagrant ssh control

L’environnement de cet atelier est préconfiguré et prêt à l’emploi :

  • Ansible est installé sur le Control Host.
  • Le fichier /etc/hosts du Control Host est correctement renseigné.
  • L’authentification par clé SSH est établie sur les trois Target Hosts.
  • Le répertoire du projet existe et contient une configuration de base et un inventaire.
  • Direnv est installé et activé pour le projet.
  • Le validateur de syntaxe yamllint est également disponible.

Rendez-vous dans le répertoire du projet :

$ cd ansible/projets/ema/
direnv: loading ~/ansible/projets/ema/.envrc
direnv: export +ANSIBLE_CONFIG
$ ls -l
total 12
-rw-r--r-- 1 vagrant vagrant   65 Sep 19 11:13 ansible.cfg
-rw-r--r-- 1 vagrant vagrant  135 Sep 19 11:13 inventory
drwxr-xr-x 2 vagrant vagrant 4096 Sep 19 11:13 playbooks

L’inventaire des machines définit un groupe [debian] avec les trois Target Hosts :

$ cat inventory 
[debian]
target01
target02
target03

[debian:vars]
ansible_python_interpreter=/usr/bin/python3
ansible_user=vagrant
ansible_become=yes

Testez la connectivité à vos cibles :

$ ansible debian -m ping
target01 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
target02 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
target03 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

Exécutez le playbook apache-01.yml du précédent article :

$ cd playbooks/
$ ansible-playbook apache-01.yml

Pour commencer, nous allons étoffer notre playbook en ajoutant une redirection :

    - name: Configure redirect
      copy:
        dest: /etc/apache2/conf-available/redirect.conf
        content: |
          Redirect /start http://www.startpage.com

Sur les systèmes Debian, ce genre de configuration doit être intégrée à l’aide de la commande a2enconf qui crée un lien symbolique dans le répertoire adjacent /etc/apache2/conf-enabled. Le paramètre creates garantit l’idempotence de la tâche :

    - name: Activate redirect
      command:
        cmd: a2enconf redirect
        creates: /etc/apache2/conf-enabled/redirect.conf

La commande a2enconf est définie par le paramètre cmd du module command. On pourrait très bien se contenter de ce seul paramètre. Dans ce cas, Ansible exécuterait la commande à chaque invocation du playbook en affichant un changement, étant donné que les commandes ne sont jamais idempotentes par défaut.

C’est le paramètre creates qui garantit l’idempotence ici. Il indique le chemin vers un fichier (ou un lien symbolique dans notre cas). Si le fichier (ou le lien symbolique) en question est déjà présent sur le système, Ansible fait l’impasse sur l’exécution de la commande et affiche ok.

Il ne reste plus qu’à prendre en compte cette nouvelle configuration :

    - name: Reload Apache
      service:
        name: apache2
        state: reloaded

Exécutez le playbook avec les tâches que nous venons de définir :

$ ansible-playbook apache-01.yml --start-at-task="*redirect*"

PLAY [debian] ************************************************************************

TASK [Gathering Facts] ***************************************************************
ok: [target02]
ok: [target03]
ok: [target01]

TASK [Configure redirect] ************************************************************
changed: [target03]
changed: [target02]
changed: [target01]

TASK [Activate redirect] *************************************************************
changed: [target02]
changed: [target03]
changed: [target01]

TASK [Reload Apache] *****************************************************************
changed: [target01]
changed: [target02]
changed: [target03]

PLAY RECAP ***************************************************************************
target01   : ok=4  changed=3  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0   
target02   : ok=4  changed=3  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0   
target03   : ok=4  changed=3  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0

Testez le résultat dans un navigateur web ou plus simplement avec curl :

$ curl target01/start
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>302 Found</title>
</head><body>
<h1>Found</h1>
<p>The document has moved <a href="http://www.startpage.com">here</a>.</p>
<hr>
<address>Apache/2.4.62 (Debian) Server at target01 Port 80</address>
</body></html>

À première vue, tout se passe comme prévu. Le problème ici, c’est que les états reloaded et restarted du module service ne sont jamais idempotents. Autrement dit : le rechargement du service s’effectue dans tous les cas, peu importe s’il y a eu un changement dans la configuration ou non.

Et c’est précisément dans ce genre de situation où nous allons utiliser un handler : une tâche qui est appelée uniquement lorsqu’il y a eu un changement.

Dans un premier temps, nous allons modifier la tâche qui effectue le changement en question. La section notify envoie une notification au handler :

    - name: Configure redirect
      copy:
        dest: /etc/apache2/conf-available/redirect.conf
        content: |
          Redirect /start http://www.startpage.com
      notify: Reload Apache

La tâche en question va déménager dans une nouvelle section handlers qui se situe au même niveau que la section tasks :

  handlers:

    - name: Reload Apache
      service:
        name: apache2
        state: reloaded

Dans l’état actuel des choses, il n’y aura rien de nouveau sous le soleil. Pour voir notre handler en action, il nous suffit de modifier la redirection comme ceci par exemple :

    - name: Configure redirect
      copy:
        dest: /etc/apache2/conf-available/redirect.conf
        content: |
          Redirect /start https://www.startpage.com
      notify: Reload Apache

Voici la nouvelle mouture de notre playbook dans son intégralité :

---  # apache-01.yml

- hosts: debian

  tasks:

    - name: Update package information
      apt:
        update_cache: true
        cache_valid_time: 3600

    - name: Install Apache
      apt:
        name: apache2

    - name: Start & enable Apache
      service:
        name: apache2
        state: started
        enabled: true

    - name: Install custom web page
      copy:
        dest: /var/www/html/index.html
        mode: 0644
        content: |
          <!doctype html>
          <html>
            <head>
              <meta charset="utf-8">
              <title>Test</title>
            </head>
            <body>
              <h1>My first Ansible-managed website</h1>
            </body>
          </html>

    - name: Configure redirect
      copy:
        dest: /etc/apache2/conf-available/redirect.conf
        content: |
          Redirect /start https://www.startpage.com
      notify: Reload Apache

    - name: Activate redirect
      command:
        cmd: a2enconf redirect
        creates: /etc/apache2/conf-enabled/redirect.conf

  handlers:

    - name: Reload Apache
      service:
        name: apache2
        state: reloaded

...

Et voici le résultat des courses :

$ ansible-playbook apache-01.yml --start-at-task="*redirect*"

PLAY [debian] ************************************************************************

TASK [Gathering Facts] ***************************************************************
ok: [target02]
ok: [target03]
ok: [target01]

TASK [Configure redirect] ************************************************************
changed: [target03]
changed: [target02]
changed: [target01]

TASK [Activate redirect] *************************************************************
ok: [target03]
ok: [target02]
ok: [target01]

RUNNING HANDLER [Reload Apache] ******************************************************
changed: [target01]
changed: [target03]
changed: [target02]

PLAY RECAP ***************************************************************************
target01   : ok=4  changed=2  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0   
target02   : ok=4  changed=2  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0   
target03   : ok=4  changed=2  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0

Quelques remarques en vrac :

  • Un handler est identifié par son nom.
  • La notification est envoyée dans la section notify de la tâche.
  • Les handlers sont exécutés à la fin d’un play le cas échéant.

AstuceIl arrive assez régulièrement que vous ayez besoin d’exécuter un handler avant la fin d’un play. Dans ce cas, vous pouvez forcer l’exécution de tous les handlers notifiés jusqu’ici en utilisant la commande suivante dans le playbook :

- meta: flush_handlers

Quittez le Control Host :

$ exit

Supprimez toutes les VM :

$ vagrant destroy -f

Exercice

Placez-vous dans le répertoire du douzième atelier pratique :

$ cd ~/formation-ansible/atelier-12

Voici les quatre machines virtuelles de cet atelier :

Machine virtuelle Adresse IP
control 10.23.45.10
target01 10.23.45.20
target02 10.23.45.30
target03 10.23.45.40

Les VM tournent toutes sous Rocky Linux. Démarrez-les :

$ vagrant up

Connectez-vous au Control Host :

$ vagrant ssh control

L’environnement de cet atelier est préconfiguré et prêt à l’emploi :

  • Ansible est installé sur le Control Host.
  • Le fichier /etc/hosts du Control Host est correctement renseigné.
  • L’authentification par clé SSH est établie sur les trois Target Hosts.
  • Le répertoire du projet existe et contient une configuration de base et un inventaire.
  • Direnv est installé et activé pour le projet.
  • Le validateur de syntaxe yamllint est également disponible.

Rendez-vous dans le répertoire du projet :

$ cd ansible/projets/ema/
direnv: loading ~/ansible/projets/ema/.envrc
direnv: export +ANSIBLE_CONFIG
$ ls -l
total 8
-rw-r--r--. 1 vagrant vagrant  65 Sep 19 14:26 ansible.cfg
-rw-r--r--. 1 vagrant vagrant 128 Sep 19 14:26 inventory
drwxr-xr-x. 2 vagrant vagrant   6 Sep 19 14:26 playbooks

L’inventaire des machines définit un groupe [redhat] avec les trois Target Hosts :

$ cat inventory 
[redhat]
target01
target02
target03

[redhat:vars]
ansible_python_interpreter=/usr/bin/python3
ansible_user=vagrant
ansible_become=yes

Écrivez un playbook chrony.yml qui assure la synchronisation NTP de tous vos Target Hosts :

  • Installez le paquet chrony.
  • Activez et démarrez le service chronyd correspondant.
  • Jetez un œil sur le fichier de configuration /etc/chrony.conf fourni par défaut.
  • Installez une configuration personnalisée (cf. ci-dessous).
  • Prenez en compte cette nouvelle configuration.
  • Vérifiez la syntaxe correcte de votre playbook chrony.yml.
  • Vérifiez l’idempotence de votre playbook.

Voici la configuration à installer :

# /etc/chrony.conf
server 0.fr.pool.ntp.org iburst
server 1.fr.pool.ntp.org iburst
server 2.fr.pool.ntp.org iburst
server 3.fr.pool.ntp.org iburst
driftfile /var/lib/chrony/drift
makestep 1.0 3
rtcsync
logdir /var/log/chrony

ImportantAttention : le service chronyd n’accepte pas la directive reloaded. Autrement dit, vous devez passer par un systemctl restart chronyd pour prendre en charge la nouvelle configuration. Il faudra donc utiliser la directive restarted dans le module service.

La suite au prochain numéro.


La rédaction de cette documentation demande du temps et des quantités significatives de café espresso. Vous appréciez ce blog ? Offrez un café au rédacteur en cliquant sur la tasse.

 

Catégories : Formation

2 commentaires

olaf · 20 septembre 2024 à 19 h 30 min

Super article comme toute la série, juste une remarque, Ansible met à disposition un module pour la gestion des plugins Apache : https://docs.ansible.com/ansible/latest/collections/community/general/apache2_module_module.html ça permet de garder la notion d’indempotence que le module « cmd » casse un peu

    kikinovak · 20 septembre 2024 à 19 h 50 min

    Merci pour la remarque. C’est un peu la raison pour laquelle j’ai précisé au tout début qu’il s’agissait avant tout d’un atelier pratique à visée pédagogique.

Laisser un commentaire

Emplacement de l’avatar

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *