# TP5 - Ansible ## Configuration et Gestion d'un parc de serveurs ### Installation de l'environnement Vous disposez de deux machines virtuelles Linux (`manager` et `server1`). L'une aura le rôle de serveur à configurer et l'autre de manager. Les machines sont en mode NAT Network. Ce mode particulier de VirtualBox permet d'avoir un réseau de machines virtuelles qui peuvent communiquer ensemble et elles ont également accès au NET via le NAT. Avant de continuer le TD, n'oubliez pas de relever les adresses IP de vos machines virtuelles (`ip addr`). 1. Connectez vous à la machine `manager` et passez root 2. Editez le fichier `/etc/hostname` et remplacez `debian` par `manager` 3. Editez le fichier `/etc/hosts` et remplacez `debian` par `manager` 4. Redémarrez la machine : ``` [...]$ systemctl reboot ``` - **Répétez ces opérations sur la machine `server1`** Sur le `manager`, installez Ansible : ``` apt update apt install ansible ``` Ansible accède aux serveurs via `ssh`. La configuration commune pour accéder à un serveur d'entreprise est la suivante : on ne peut pas se connecter dessus en ssh en tant que root. Il faut se connecter en tant qu'utilisateur normal (pas root) puis devenir root (`su -`). On va mettre en place une connexion pour l'utilisateur `rt` (`adduser...`) par clé et non pas mot de passe. Sur le manager, créez une paire de clef publique/privée via la commande ci-dessous pour `rt` et en tant que `rt` (`su - rt`) (**ne travaillez pas en root!!!!**) (**Ne pas mettre de mot de passe sur la clef**) ``` ssh-keygen ``` Il faut maintenant mettre la clef publique dans la liste des clefs autorisées sur le serveur avec la commande : _Attention : ces commandes s'effectuent depuis le manager (machine Ansible) et on envoie les clefs sur le serveur._ ``` ssh-copy-id -i ~/.ssh/id_rsa.pub rt@[adresse_ip_serveur] ``` Si tout se passe bien, vous devez pouvoir vous loger sur le serveur sans mot de passe : ``` ssh rt@[adresse_ip_serveur] ``` L'idée de cette authentification est que le serveur envoie une chaîne de caractères que le client (ici le manager) doit crypter avec la clef privée. Le client renvoie la chaîne cryptée et si le serveur arrive à décrypter avec la clef publique, c'est que le client possède bien la clef privée et il est authentifié et autorisé à accéder, voir Fig. 1 (source : [https://www.digitalocean.com/community/tutorials/how-to-configure-ssh-key-based-authentication-on-a-linux-server](https://www.digitalocean.com/community/tutorials/how-to-configure-ssh-key-based-authentication-on-a-linux-server)). ![](https://i.imgur.com/z246een.png) Figure 1: Clés SSH Sur le manager, logez vous en `rt` et créez un répertoire `playbook` dans lequel nous allons placer toutes les recettes qu'Ansible utilisera pour configurer le serveur. Il nous manque encore une dernière chose sur le **serveur**: *faire en sorte que rt puisse faire un sudo sans mot de passe*. Il faut créer le fichier (en root) `/etc/sudoers.d/rt` (installez le paquet `sudo` si ce répertoire manque) et le remplir comme suit : ``` rt ALL=(ALL) NOPASSWD: ALL ``` Vérifiez ensuite que tout fonctionne en vous connectant en tant que `rt` sur le serveur. ### Connexion et Exécution d'un script simple Ansible accède aux serveurs via ssh et cherche les paramètres de connexion aux serveurs à configurer dans le fichier `ansible.cfg` qui peut-être à divers endroits : 1. pointé par la variable d'environnement `ANSIBLE_CONFIG` 2. `ansible.cfg` (fichier local à l'endroit où on exécute ansible) 3. `/.ansible.cfg` si vous souhaitez avoir un seul fichier global. 4. `/etc/ansible/ansible.cfg` Nous allons utiliser l'option 2 car souvent on met les recettes et les "coordonnées" des serveurs au même endroit (pour pouvoir ensuite tout sauvegarder d'un coup, par exemple via git ou svn). Sur le manager, créez un fichier `ansible.cfg` avec la configuration suivante : ``` [defaults] inventory = hosts remote_user = rt private_key_file = ~rt/.ssh/id_rsa fact_caching = jsonfile fact_caching_connection = ./facts.d [ssh_connection] ssh_args = -o StrictHostKeyChecking=no ``` La première ligne indique que le fichier avec les noms des hôtes (l'inventaire) s'appelle `hosts`. Nous allons donc créer le fichier `hosts` dans notre répertoire `playbook` et y ajouter les coordonnées du serveur (adapter l'adresse IP à votre cas bien sûr) : ``` testserver ansible_ssh_host=10.0.2.4 ``` `testserver` va servir d'alias dans les commandes ansible. On peut ensuite effectuer sa première commande ansible : ``` ansible testserver -m ping ``` `-m` introduit le module qui va être utilisé par ansible. Le module `ping` vérifie que le serveur est bien accessible, c'est-à-dire que la connectivité ssh est bonne. Si tout se passe bien, vous devriez obtenir : ``` testserver | success >> { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python3" }, "changed": false, "ping": "pong" } ``` Si ça se passe mal, il faut débugger avec `-vvv...` Testons un autre module : `command`. Ce module permet d'exécuter une commande arbitraire sur le serveur avec l'option `-a` : ``` ansible testserver -m command -a uptime ``` avec comme réponse : ``` testserver | CHANGED | rc=0 >> 02:55:24 up 300 min, 2 users, load average: 0,09 0,08 0,05 ``` Le module `command` est en fait le module par défaut et il est facultatif. Ré-exécutez la commande précédente en l'omettant. Certaines commandes nécessitent d'être root sur la machine distante. Testez les commandes ``` ansible testserver -m command -a "tail -v /var/log/syslog" ```` et ``` ansible testserver -b -m command -a "tail -v /var/log/syslog" ``` - Que concluez-vous de l'analyse des résultats ? On peut aussi installer des paquets avec le module `apt` (il ne s'agit pas de la commande `apt` directement, mais d'un module qui va utiliser `apt` et automatiser certaines actions comme la réponse automatique à la question "Do you want to continue [Y/n]" qui apparaît lors de l'installation d'un paquet. Nous allons installer un serveur Web nginx. Avant de l'installer vérifiez si il n'y a pas déjà un serveur Web qui tourne sur la machine `server1` et, si c'est le cas, arrêtez le (1ère commande ci-dessous), retirez apache du démarrage automatique (2nde commande ci-dessous) puis désinstallez totalement le paquet : ``` apt purge apache2 apt-get autoremove ``` On peut maintenant installer nginx (à partir du `manager`) : ``` ansible testserver -b -m apt -a name=nginx ``` Vérifier que l'installation sur le serveur s'est bien passée : ![](https://i.imgur.com/T8hMrZ2.png) Ce n'est pas en général une bonne idée d'installer directement un paquet. Il faut commencer par mettre à jour la base des paquets (la télécharger du dépôt) de manière à demander une version qui existe encore d'un paquet. On peut le faire faire à Ansible au travers de l'ajout de l'option `update_cache` après le nom du paquet : ``` ansible testserver -b -m apt -a "name=nginx update_cache=yes" ``` On peut aussi avoir besoin de redémarrer un service : ``` ansible testserver -b -m service -a "name=nginx state=restarted" ``` ### Premier playbook Un playbook est un script Ansible qui permet d'enchainer plusieurs actions pour mener à la configuration complète d'un service sur une machine. Prenons un premier exemple simple qui montre comment on passe de la ligne de commande au script. On va mettre dans ce script l'équivalent des 2 commandes que l'on appellera tâches :`ansible testserver -b -m apt -a name="nginx update_cache=yes"` et `ansible testserver -b -m service -a "name=nginx state=restarted"` Avant de poursuivre il faut supprimer `Nginx` du serveur : ``` apt purge nginx-common apt autoremove ```` Créez un fichier `nginx.yml` dans le répertoire playbook qui contient (attention il faut respecter les indentations) : ``` - name: Configure webserver with nginx hosts: testserver become: yes tasks: - name: install nginx apt: name=nginx update_cache=yes notify: restart nginx handlers: - name: restart nginx service: name=nginx state=restarted ``` `become: yes` permet de passer root sur le serveur. On va jouer ce playbook avec la commande : ``` ansible-playbook nginx.yml ``` Si tout se passe bien, vous devriez avoir une sortie du type de la figure 2. ![](https://i.imgur.com/IXyqXPU.png) Figure 2: Installation de NGINX On peut lister les tâches qui sont contenues dans un playbook et en avoir une explication synthétique avec la commande ``` ansible-playbook --list-tasks nginx.yml ``` De la même manière, on peut lister les machines que ce playbook va modifier ``` ansible-playbook --list-hosts nginx.yml ``` #### Facts Si on regarde la sortie de `ansible-playbook-nginx.yml` on voit qu'en fait il y a une tâche de plus que ce nous donne l'option `-list-tasks`. Il s'agit de la phase de collecte d'informations (`Gathering Facts`) sur le serveur distant. Ces informations sont stockées par Ansible et utilisables par la suite dans le script si besoin. On peut voir l'ensemble des informations collectées avec le module setup de la commande en ligne ansible : ``` ansible all -m setup ``` - Testez cette commande et analysez les informations collectées pour le serveur testserver ? - Comment sont-elles collectées par Ansible ? #### Idempotence Nous allons commencer cette partie en desinstallant Nginx sur le serveur. En suivant la [documentation ansible](https://docs.ansible.com/ansible/latest/modules/apt_module.html), pour déinstaller avec le module `apt` il faut un `purge` et un `auto remove` : ``` ansible testserver -b -m apt -a "name=nginx-common state=absent purge=yes autoremove=yes" ``` Une propriété importante des playbooks que l'on souhaite avoir est l'idempotence. Cela veut dire que si l'on exécute plusieurs fois le même script, Ansible ne reporte que les modifications effectivement faites sur le serveur. Vérifiez ce qui se passe en exécutant plusieurs fois le playbook `nginx.yml` et en analysant la sortie retournée par ansible. ## Un exemple plus complet de configuration de serveur WEB On veut configurer de manière propre un serveur nginx. Commençons par retirer le paquet nginx de la machine testserver `(apt-get purge nginx-common`). Les fichiers de configuration ci-dessous (`web.yml`, `templates/index.html.j2` et `files/default`) vont nous permettre de : 1. Installer `nginx` 2. Modifier la page d'accueil par défaut du site par défaut. On va copier le fichier de la nouvelle page d'accueil depuis le `manager` vers le `testserver` avec le module file. 3. Modifier la page d'accueil en la personnalisant avec une variable d'environnement d'Ansible. Il va falloir utiliser le module template qui permet de customiser un fichier. 4. Redémarrer nginx pour prendre en compte les modifications. Voici les contenus des fichiers : Fichier `web.yml` ``` - name: Configure webserver with nginx hosts: testserver become: yes tasks: - name: install nginx apt: name=nginx update_cache=yes - name: copy nginx config file copy: src=files/default dest=/etc/nginx/sites-available/default - name: enable configuration file: > dest=/etc/nginx/sites-enabled/default src=/etc/nginx/sites-available/default state=link - name: copy index.html template: src=templates/index.html.j2 dest=/usr/share/nginx/html/index.html mode=0644 - name: restart nginx service: name=nginx state=restarted ``` Fichier `default` à placer dans le répertoire `playbook/files` à créer. ``` server { listen 80 default_server; listen [::]:80 default_server; root /usr/share/nginx/html; index index.html index.htm; server_name localhost; } ``` Fichier `index.html.j2` à placer dans le répertoire `playbook/templates` (l'extension `j2` sert à identifier les templates pour Ansible) : ``` <html> <head> <title>Welcome to Polytech Angers</title> </head> <body> <h1>nginx, configured by Ansible</h1> <p>If you can see this, Ansible successfully installed nginx.</p> <p> This server is running under {{ ansible_lsb.description }}</p> </body> </html> ```` - Analyser succinctement le playbook avec l'option `-list-tasks`. - Appliquez le template sur le serveur testserver. - Observez le résultat sur le site Web. - Modifier le template `index.html.j2` de manière à ce que soit retournée dans la page Web l'adresse IP de l'interface `enp0s3`. Aidez-vous de la documentation de Ansible : [https://docs.ansible.com/ansible/2.3/playbooks_variables.html](https://docs.ansible.com/ansible/2.3/playbooks_variables.html)