Ansible
Ansible (ansible.com) se révèle aujourd'hui être un outil de choix dans le domaine de l'automatisation. Nous utilisons cette technologie depuis plusieurs mois maintenant et après Europython 2017, pendant lequel nous avons pu voir plusieurs cas d'utilisation, nous avons décidé de mettre les bouchées doubles sur cette technologie et faire monter en compétence une partie de l'équipe qui ne l'utilise encore que peu.
Un des plus gros avantage d'Ansible est sa simplicité d'utilisation et notamment ses fichiers de configuration relativement simples.
Il existe un service payant, ansible tower, mais ce qui nous interesse ici est la documentation sur leur site.
Voyons ensemble une configuration simple d'ansible pour automatiser l'installation de nginx sur un serveur distant. (Exemple simple)
Installation (http://docs.ansible.com/ansible/latest/intro_installation.html)
Via apt ubuntu :
sudo apt-add-repository ppa:ansible/ansible
sudo apt-get update
sudo apt-get install ansible
Créons un fichier avec la liste de nos serveurs, par example "hosts" :
192.168.33.10
192.168.33.11
192.168.33.12
192.168.33.13
192.168.33.14
Cela permet de lancer des recettes par groupe.
Prenons pour l'instant un exemple simple avec un unique serveur
192.168.33.10
On va écrire une recette en "yaml", créons un fichier playbook.yml :
hosts: web
remote_user: root
- name: Installation de git
(...)
remote_user étant l'utilisateur que l'on veut utiliser.
Nous pouvons dès à présent définir certaines tâches à effectuer.
Pour cela, ansible fournit un certain nombre de module pour faciliter la mise en place de ces taches. Par exemple vous pouvez en apprecier un certain nombre dans leur documentation officielle : docs.ansible.com/ansible/modules_by_category.html
Ici, nous voulons installer "git". Nous pouvons alors utiliser le module "apt" qui demande comme argument "name" et "update-cache".
Ajoutons "vim" également tant que nous y sommes.
hosts: web
remote_user: root
- name: Installation de git
apt: name=git update_cache=yes
- name: installation de Vim
apt: name=vim
Pour lancer ce playbook, nous devons utiliser la commande suivante :
La sortie en console de cette commandedevrait être similaire à :
ok: [192.168.33.10]
changed: [192.168.33.10]
changed: [192.168.33.10]
192.168.33.10 : ok=3 changed=2 unreachable=0 failed=0
En relancant la commande, ansible va d'abord vérifier si les paquets sont installés et sera donc plus rapide.
Nous pouvons gagner un peu de temps et spécifier en une fois les paquets qu'il faut installer. (Et ajoutons au passage "htop")
hosts: web
remote_user: root
- name: Installation des dépendances
apt: name={{ item }} update_cache=yes state=latest
with_items:
- vim
- htop
- git
Cela reste relativement simple et peu utile comme playbook.
Ajoutons la création d'un nouvel utilisateur. Pour cela, nous allons avoir besoin d'un nouveau module : "user".
hosts: web
remote_user: root
- name: Installation des dépendances
apt: name={{ item }} update_cache=yes state=latest
with_items:
- vim
- htop
- git
- name: Création d'un utilisateur
user: name=adrien comment='Mon nouvel utilisateur' shell=/usr/bin/bash
- name: Ajout de la clé SSH
authorized_key: user=adrien key="{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
Les modules sont très bien documentés et ces lignes viennent directement de la documentation. Pas de panique si vous pensez être perdu : il faut s'en tenir à la documentation.
Après avoir éxecuté notre playbook, nous pouvons essayer de nous connecter à notre serveur avec l'utilisateur "adrien" et nous devrions bien accéder au serveur.
Nous avons cependant ici une répétition dans notre fichier de configuration. En effet, nous spécifions deux fois le nom de l'utilisateur ("adrien").
Factorisons notre fichier avec l'extraction de la variable du nom d'utilisateur :
hosts: web
remote_user: root
vars:
user: adrien
- name: Installation des dépendances
apt: name={{ item }} update_cache=yes state=latest
with_items:
- vim
- htop
- git
- name: Création d'un utilisateur
user: name={{ user }} comment='Mon nouvel utilisateur' shell=/usr/bin/bash
- name: Ajout de la clé SSH
authorized_key: user={{ user }} key="{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
Ainsi, cette recette est maintenant beaucoup plus facilement réutilisable par quelqu'un d'autre, il suffit de changer le nom de l'utilisateur.
Par ailleurs, nous pouvons également ajouter des conditions à nos tâches. Prenons l'exemple ou l'utilisateur n'a pas été défini, alors la recette pourrait être :
hosts: web
remote_user: root
vars:
- name: Installation des dépendances
apt: name={{ item }} update_cache=yes state=latest
with_items:
- vim
- htop
- git
- name: Création d'un utilisateur
when: user is defined
user: name={{ user }} comment='Mon nouvel utilisateur' shell=/usr/bin/bash
- name: Ajout de la clé SSH
when: user is defined
authorized_key: user={{ user }} key="{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
Si on lance notre playbook, alors les deux tâches seront ignorées ("skipping").
Notre utilisateur fraichement créé n'a pas les droits utilisateurs, ajoutons le au groupe des "sudoers". Pour cela nous allons créer un template et utiliser notre premier fichier jinja !
Créons templates/sudoers.j2 :
Et au sein de notre playbook :
hosts: web
remote_user: root
vars:
- name: Installation des dépendances
apt: name={{ item }} update_cache=yes state=latest
with_items:
- vim
- htop
- git
- name: Création d'un utilisateur
when: user is defined
user: name={{ user }} comment='Mon nouvel utilisateur' shell=/usr/bin/bash
- name: Ajout de la clé SSH
when: user is defined
authorized_key: user={{ user }} key="{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
- name: "{{ user }}" devient un sudoer
when: user is defined
template: src=templates/sudoers.j2 dest=/etc/sudoers.d/{{ user }}-sudoer validate='visudo -cf %s'
Passons maintenant à notre objectif initial : l'installation de nginx et vérifions que le nom de domaine fonctionne.
Avant de se lancer tête baissée, prenons un peu de recul. Si nous continuons notre script, nous risquons vite d'obtenir un fichier un peu long et nous allons perdre la réutilisabilité de notre script.
Pour éviter ce genre de problème, ansible utilise ce qu'on appelle des "roles".
Créons un dossier roles/utils/tasks et ajoutons le fichier main.yml. Son contenu sera juste un extrait de notre playbook précédent :
apt: name={{ item }} update_cache=yes state=latest
with_items:
- vim
- htop
- git
De même créons roles/user/tasks et ajoutons le fichier main.yml :
when: user is defined
user: name={{ user }} comment='Mon nouvel utilisateur' shell=/usr/bin/bash
- name: Ajout de la clé SSH
when: user is defined
authorized_key: user={{ user }} key="{{ lookup('file', '~/ssh/id_rsa.pub') }}"
- name: "{{ user }} devient un sudoer"
when: user is defined
template: src=templates/sudoers.j2 dest=/etc/sudoers.d/{{ user }}-sudoer validate='visudo -cf %s'
et notre playbook devient :
hosts: web
remote_user: root
vars:
- utils
- user
Vous devez vous douter que nous ne sommes pas les premiers explorateurs d'ansible à écrire nos roles. Il existe une quantité affolante de rôles prêts à être utilisés sans que vous ayez à tous les réécrire. Vous pouvez explorer le site galaxy.ansible.com .
Attention toutefois : n'oubliez pas que vous risquez d'éxécuter un code que vous ne connaissez pas et que vous ne maitrisez potentiellement pas sur votre machine si vous utilisez directement un rôle écrit par quelqu'un d'autre. Cependant, vous pouvez toujours allez explorer leur code sur github et vous en inspirer.
Maintenant que notre playbook est découpé en rôles, ajoutons le rôle nginx :
hosts: web
remote_user: root
vars:
- utils
- user
- nginx
et créons le fichier roles/nginx/tasks/main.yml :
become: yes
apt: name=nginx state=latest
become: yes
service: name=nginx state=started enabled=true
become: yes permet de s'assurer que la commande essaie de se lancer en tant qu'administrateur.
En lançant ce playbook, vous devriez donc avoir installé nginx. En vous rendant sur l'URL de votre serveur, vous devez maintenant observer la page d'accueil de nginx.
Souvent, nous ne voulons pas laisser cette page visible. Pour cela, nous pouvons supprimer la configuration par défaut.
Ensuite, nous devrions redemarrer nginx. Cela ferait deux nouvelles taches et toute modification imposera un rechargement de nginx.
Plutôt que de créer toujours plus de tâches qui se répètent, nous pouvons créer des "handlers". Chaque tâche peut notifier un handler et ceux ci seront déclencher une fois toutes les tâches effectuées s'ils ont été notifié. Le redémarrage de nginx rentre parfaitement dans ce cas d'utilisation !
Réécrivons ce role nginx:
become: yes
apt: name=nginx state=latest
become: yes
service: name=nginx state=started enabled=true
become: yes
file: path=/etc/nginx/sites-enabled/default state=absent
notify: reload nginx
et créons le fichier roles/nginx/handlers/main.yml :
action: service name=nginx state=reloaded
Cette fois-ci, la page par défaut de nginx ne devrait plus être accessible.
Nous pouvons continuer ainsi et automatiser toute la chaîne de mise en production. A travers cet exemple simple, nous avons pu aborder les tasks, les roles, les templates et les handlers.
Cela devrait être suffisant pour commencer à s'amuser avec Ansible ! A vous de jouer !