Ansible
Installation
Après avoir mis à jour le système, activer le repo rhel-7-server-extras-rpms :
subscription-manager repos --enable rhel-7-server-extras-rpms
Ensuite pour installer ansible :
yum install ansible
Utilisation basique (dite Ad-Hoc)
Ansible se connecte et exécute les actions sur les différents nœuds via le protocole ssh (outil en python), il n’a donc pas besoin de client. Pour cette exemple, nous partons du principe qu’un user « admin » avec des droits sudo est déployé sur toutes les machines et avec la clé publique du serveur Ansible dans l’authorized_keys.
Avant toutes choses, il faut renseigner le fichier « /etc/ansible/hosts » dans lequel il faut ajouter tous les serveurs qui pourront être requêtés, exemple :
[test] serv-stockage-01 serv-stockage-02 serv-samba-01
La balise [test] permet de définir un groupe « test » qui contient toutes les machines définies dessous.
Nous pouvons donc demander à Ansible de lancer par exemple la commande « vgs » sur tous les serveurs du groupe test :
[root@serv-ansible-01 playbook]# ansible -m command -a "vgs" test -u admin -b -k
SSH password:
serv-stockage-01 | SUCCESS | rc=0 >>
VG #PV #LV #SN Attr VSize VFree
rhel 1 5 0 wz--n- <15,51g <4,91g
vg_u01 1 1 0 wz--n- <2,00g 0
serv-stockage-02 | SUCCESS | rc=0 >>
VG #PV #LV #SN Attr VSize VFree
rhel 1 5 0 wz--n- 15,51g 4,91g
vg_u01 1 1 0 wz--n- 2,00g 0
serv-samba-01 | SUCCESS | rc=0 >>
VG #PV #LV #SN Attr VSize VFree
rhel 2 5 0 wz--n- 45,50g 20,90g
« -m » (comme module) permet de spécifier un module à utiliser pour Ansible, ici on utilise le module « command » qui va permettre de spécifier une commande à exécuter via l’option « -a » (comme argument).
« -u » (comme user) permet de spécifier à Ansible avec quel user effectuer la connexion, il faudra le coupler à « -k » (comme keypass) afin de taper le mdp (si la clé publique du user admin n’étant pas défini dans l’authorized_keys des autres serveurs).
« -b » (comme become) permet d’effectuer la commande en tant que root (par defaut) via l’utilisation de sudo (par defaut aussi), car nous voulons effectuer la commande « vgs » qui n’est possible qu’avec des droits root (le user admin doit avoir le droit de faire un sudo pour toutes commandes et sans avoir à spécifier le mdp).
Remarque : On aurait aussi pu rajouter l’option « -l » suivit d’un nom d’host (du groupe test) afin qu’Ansible ne réalise les actions que sur ce dernier.
Il existe de nombreux modules disponibles ici : http://docs.ansible.com/ansible/latest/modules_by_category.html
Les playbooks
Un playbook est un simple fichier au format YAML qui au final représente une suite d’exécution un peu comme si on les avait faites-les une à la suite des autres via le mode Ad-Hoc.
Nous allons créer un playbook permettant de créer un petit serveur web. On créer donc par exemple le group « web » dans « /etc/ansible/hosts » :
[web] v-ansi-client1-gt v-ansi-client2-gt
Puis on créer le fichier « /etc/ansible/playbooks/apache.yml » avec dedans :
- hosts: web #ce playbook se lancera sur les host du groupe web become: true #les actions seront exécutées via sudo tasks: #balise task pour définir toutes les actions à faire - name: Installation d'Apache #chaque action débute par une balise « name » yum: name=httpd state=latest #utilisation du module « yum » pour installer le paquet httpd à la dernière version dispo - name: Copie du vhost pour l'application app1 copy: src=/etc/ansible/files/vhost-app1.conf dest=/etc/httpd/conf.d/app1.conf mode=0644 #utilisation du module « copy » qui copie le vhost-app1.cfg sur les serveurs notify: restart_apache #si modification dans cette partie on utilisera le handlers « restart_apache » défini plus bas - name: Suppression des vhosts actuels file: dest=/etc/httpd/conf.d/{{ item }} state=absent #utilisation du module « file » qui va supprimer les fichiers « item » with_items: #définition des fichiers « item » - autoindex.conf - welcome.conf - userdir.conf notify: restart_apache - name: Création du dossier de log file: dest=/var/log/apache2 state=directory notify: restart_apache - name: Création du documentroot file: dest=/var/www/app1 state=directory notify: restart_apache - name: Copie du index.html copy: src=/etc/ansible/files/index.html dest=/var/www/app1/ notify: restart_apache handlers: #on définit ici des handlers via un « name » et une action, ils sont appelés par les balises « notify » si il y a eu modification - name: restart_apache service: name=httpd state=restarted
Nous avons donc 2 fichiers sources à envoyer sur les serveurs (/etc/ansible/files/vhost-app1.conf et /etc/ansible/files/index.html).
/etc/ansible/files/vhost-app1.conf :
<VirtualHost *:80> DocumentRoot /var/www/app1 Options -Indexes ErrorLog /var/log/apache2/error.log TransferLog /var/log/apache2/access.log </VirtualHost>
/etc/ansible/files/index.html :
test
On lance le playbook :
[root@serv-ansible-01 ansible]# ansible-playbook -u admin -k playbooks/apache.yml SSH password: PLAY [web] ************************************************************************************************************************************************************* TASK [Gathering Facts] ************************************************************************************************************************************************* ok: [v-ansi-client2-gt] ok: [v-ansi-client1-gt] TASK [Installation d'Apache] ******************************************************************************************************************************************* changed: [v-ansi-client2-gt] changed: [v-ansi-client1-gt] TASK [Copie du vhost pour l'application app1] ************************************************************************************************************************** changed: [v-ansi-client1-gt] changed: [v-ansi-client2-gt] TASK [Suppression des vhosts actuels] *********************************************************************************************************************************** changed: [v-ansi-client1-gt] => (item=autoindex.conf) changed: [v-ansi-client2-gt] => (item=autoindex.conf) changed: [v-ansi-client1-gt] => (item=welcome.conf) changed: [v-ansi-client2-gt] => (item=welcome.conf) changed: [v-ansi-client1-gt] => (item=userdir.conf) changed: [v-ansi-client2-gt] => (item=userdir.conf) TASK [Création du dossier de log] ************************************************************************************************************************************** changed: [v-ansi-client1-gt] changed: [v-ansi-client2-gt] TASK [Création du documentroot] **************************************************************************************************************************************** changed: [v-ansi-client1-gt] changed: [v-ansi-client2-gt] TASK [Copie du index.html] ********************************************************************************************************************************************* changed: [v-ansi-client1-gt] changed: [v-ansi-client2-gt] RUNNING HANDLER [restart_apache] *************************************************************************************************************************************** changed: [v-ansi-client2-gt] changed: [v-ansi-client1-gt] PLAY RECAP ************************************************************************************************************************************************************* v-ansi-client1-gt : ok=8 changed=7 unreachable=0 failed=0 v-ansi-client2-gt : ok=8 changed=7 unreachable=0 failed=0
La première tâche « Gathering Facts » que fait Ansible est de tester si les hosts à modifier sont bien accessibles. Il déroule ensuite les tâches définies dans le playbook. Nous voyons à la fin qu’il y a 8 tâches OK et 7 changées, on a donc les 8 tâches qui se sont bien déroulées (la première tâche de test au début comprise) dont 7 qui ont effectuées un changement sur les machines.
On voit aussi que comme il y a eu changement, le handler a été appelé et a relancé apache.
Si on relance à nouveau le playbook, on peut voir que tout est en OK et rien en « changed » car tout est déjà paramétré comme stipulé sur le playbook (de plus le handler ne sera pas appelé) :
[root@serv-ansible-01 ansible]# ansible-playbook -u admin -k playbooks/apache.yml SSH password: PLAY [web] ************************************************************************************************************************************************************* TASK [Gathering Facts] ************************************************************************************************************************************************* ok: [v-ansi-client2-gt] ok: [v-ansi-client1-gt] TASK [Installation d'Apache] ******************************************************************************************************************************************* ok: [v-ansi-client2-gt] ok: [v-ansi-client1-gt] TASK [Copie du vhost pour l'application app1] ************************************************************************************************************************** ok: [v-ansi-client1-gt] ok: [v-ansi-client2-gt] TASK [Suppression des vhosts actuels] *********************************************************************************************************************************** ok: [v-ansi-client1-gt] => (item=autoindex.conf) ok: [v-ansi-client2-gt] => (item=autoindex.conf) ok: [v-ansi-client2-gt] => (item=welcome.conf) ok: [v-ansi-client1-gt] => (item=welcome.conf) ok: [v-ansi-client1-gt] => (item=userdir.conf) ok: [v-ansi-client2-gt] => (item=userdir.conf) TASK [Création du dossier de log] ************************************************************************************************************************************** ok: [v-ansi-client1-gt] ok: [v-ansi-client2-gt] TASK [Création du documentroot] **************************************************************************************************************************************** ok: [v-ansi-client1-gt] ok: [v-ansi-client2-gt] TASK [Copie du index.html] ********************************************************************************************************************************************* ok: [v-ansi-client1-gt] ok: [v-ansi-client2-gt] PLAY RECAP ************************************************************************************************************************************************************* v-ansi-client1-gt : ok=7 changed=0 unreachable=0 failed=0 v-ansi-client2-gt : ok=7 changed=0 unreachable=0 failed=0
Les variables
Variables complexes
Imaginons que nous voulons pousser un fichier (via le module template) vers plusieurs serveurs et que nous voulons que certaines lignes de ce fichier soient remplies dynamiquement. Par exemple un fichier de conf d’un loadbalancer qui contiendrait une ligne indiquant l’ip du serveur où on l’installe et plusieurs lignes en fonction des membres. Le fichier template serait de la sorte :
…. listen cluster bind {{ ansible_eth1['ipv4']['address'] }}:80 #voir 1) {% for mavariablehosts in groups['web'] %} #voir 2) server {{ hostvars[mavariablehosts]['ansible_hostname'] }} {{ hostvars[mavariablehosts]['ansible_eth1']['ipv4']['address'] }} check port 80 {% endfor %} ….
1) {{ ansible_eth1[‘ipv4’][‘address’] }} va récupérer l’ip du serveur où sera poussé le fichier, on peut trouver ces variables via le module « setup » :
[root@serv-ansible-01 ansible]# ansible -m setup -u admin -k -b v-ansi-client1-gt …. "ansible_eth0": { "active": true, "device": "eth0", "features": { "busy_poll": "off [fixed]", … … "vlan_challenged": "off [fixed]" }, "hw_timestamp_filters": [], "ipv4": { "address": "10.251.28.23", "broadcast": "10.251.28.255", "netmask": "255.255.252.0", "network": "10.251.28.0"
Les éléments se notent les l’un après les autres en cascade (json).
2) {% for mavariablehosts in groups[‘web’] %}
server {{ hostvars[mavariablehosts][‘ansible_hostname’] }} {{ hostvars[mavariablehosts][‘ansible_eth1’][‘ipv4’][‘address’] }} check port 80
{% endfor %}
Pareil qu’au-dessus sauf que nous faisons une boucle for qui va prendre en argument tous les hosts du groupe « web » (défini dans le fichier /etc/ansible.hosts), il faut juste utiliser « hostvars[mavariablehosts] » devant les éléments à récupérer.
Attention : pour copier un template il faut utiliser le module « template » et non « copy ».
Variables statiques
On peut définir des variables directement dans un playbook comme ceci :
- hosts: webservers vars: http_port: 80
On peut aussi définir des variables pour des groupes, il faut alors créer le fichier /etc/ansible/group_vars/nomdugroupe. On créer ensuite les variables comme ceci dans le fichier :
mavariable: value
Pareil pour les hosts seul, on créer le fichier /etc/ansible/host_vars/nomduhost ou placer les variables après le nom d’host dans le fichier « hosts » comme ceci :
[test]
serv-stockage-01 mavariable=value
serv-stockage-02
serv-samba-01
On appelle ensuite ces variables comme ceci :
{{ mavariable }}
Les conditions
Conditionner le résultat d’un module
En utilisant l’option -v avec ansible-playbook, on s’aperçoit que les modules remontent différentes informations :
TASK [apache : Création du DocumentRoot] *******************************************************************************************************************************
ok: [v-ansi-client2-gt] => {"changed": false, "failed": false, "gid": 0, "group": "root", "mode": "0755", "owner": "root", "path": "/var/www/app1", "size": 23, "state": "directory", "uid": 0}
Il est possible d’enregistrer le résultat d’un module dans une variable comme ceci :
- name: Création du DocumentRoot
file: dest=/var/www/app1 state=directory
notify: restart_apache
register: mavariabletest
Pour avoir par exemple le groupe du dossier que l’on vient de créer, on utilisera « {{ mavariabletest.group }} », cette variable nous remontera « root ».
On peut aussi se servir de cette variable pour conditionner le passage d’autres tasks :
- name: Création du DocumentRoot file: dest=/var/www/app1 state=directory notify: restart_apache register: mavariabletest - name: Copie du vhost pour l'application app1 copy: src=/etc/ansible/files/toto dest=/var/www/app1/toto.conf when: mavariabletest|succeeded
Ou alors :
- name: Installation de Yum-cron
yum: name=yum-cron state=latest enablerepo=rhel-7-server-optional-rpms
when: ansible_os_family == "RedHat" and ansible_distribution_major_version|int >= 7
Dans le cas de plusieurs task conditionnées, on peut utiliser un block :
- name: Configuration d'Apache block: - name: Création du DocumentRoot file: dest=/var/www/app1 state=directory - name: Copie du vhost pour l'application app1 copy: src=/etc/ansible/files/toto dest=/var/www/app1/toto.conf when: ansible_os_family == "RedHat" and ansible_distribution_major_version|int >= 7
If dans un template
Imaginons que nous avons ce fichier hosts :
[test] serv-stockage-01 domaine=mondomaine.a serv-stockage-02 domaine=mondomaine.b serv-samba-01
Nous voulons déployer le fichier /etc/resolv.conf avec le module template en fonction du domaine, on aura un template de ce type :
search {{ domaine }} {% if domaine == 'mondomaine.a' %} nameserver 10.10.5.100 nameserver 10.10.5.101 {% else %} nameserver 10.10.4.100 nameserver 10.10.4.101 {% endif %}
Les rôles
Au lieu de créer un playbook contenant l’installation de multiples outils et de configurer plusieurs choses, il est plus élégant de créer un rôle par outils et d’ensuite appeler ces rôles dans un playbook.
Pour créer un rôle il faut créer une arborescence, par exemple pour le rôle apache, il faut créer le dossier roles/apache avec en sous dossier :
– tasks : contient la liste des tâches à effectuer
– handlers : les handlers qu’on appelle via les « tasks »
– vars : les variables
– files : les fichiers à déployer
– templates : les fichiers template à déployer
– meta : les metadata
Remarque : il existe d’autres sous dossiers pouvant être créé afin d’accéder à d’autres fonction d’Ansible (voir http://docs.ansible.com/ansible/latest/playbooks_reuse_roles.html )
Dans chaque sous dossier les fichiers se nommeront « main.yml », nous pouvons donc transformer notre playbook apache vu plus haut en rôle.
Il faut alors se placer dans le dossier « /etc/ansible/roles » et utiliser la commande « ansible-galaxy init apache » qui va créer l’arborescence défini au-dessus.
Mettre les « tasks » dans « /etc/ansible/roles/apache/tasks/main.yml » :
--- # tasks file for apache - name: Installation d'Apache yum: name=httpd state=latest - name: Copie du vhost pour l'application app1 copy: src=/etc/ansible/sources/vhost-app1.conf dest=/etc/httpd/conf.d/app1.conf mode=0644 notify: restart_apache - name: Supression des vhosts actuels file: dest="/etc/httpd/conf.d/{{ item }}" state=absent with_items: - autoindex.conf - welcome.conf - userdir.conf notify: restart_apache - name: Création du dossier de log file: dest=/var/log/apache2 state=directory notify: restart_apache - name: Création du documentroot file: dest=/var/www/app1 state=directory notify: restart_apache - name: Copie du index.html copy: src=/etc/ansible/sources/index.html dest=/var/www/app1/ notify: restart_apache
Mettre le handlers dans « /etc/ansible/roles/apache/handlers/main.yml » :
--- # handlers file for apache - name: restart_apache service: name=httpd state=restarted
Puis créer le playbook qui appellera le rôle dans « /etc/ansible/playbooks/apache.yml » :
- hosts: web
become: true
roles:
- apache