URL: https://linuxfr.org/news/presentation-d-ansible-et-version-2-a-venir Title: Présentation d'Ansible et version 2 à venir Authors: yannig Bruno Michel, Xavier Teyssier, Florent Zara, Benoît Sibaud, ZeroHeure, palm123 et bubar🦥 Date: 2015年04月04日T16:42:31+02:00 License: CC By-SA Tags: ansible, devops et déploiement Score: 39 Ansible est un outil de la mouvance actuelle autour du DevOps. J'ai commencé à aborder son fonctionnement il y a maintenant quelques mois et force est de constater que sa prise en main est beaucoup plus simple que d'autres produits ayant la même finalité, ceci pour plusieurs raisons : - l'apprentissage du langage est très rapide ; - il n'y a pas d'infrastructure à gérer : pas de serveur central (j'y reviendrai un peu plus tard) et surtout pas d'agent. ![logo Ansible](http://www.ansible.com/hs-fs/hub/330046/file-764918171-png/Official_Logos/ansible_wordlogo_black_small.png) ---- [Ansible](http://www.ansible.com) [Documentation sur Ansible](http://docs.ansible.com/) [Ansible Galaxy](https://galaxy.ansible.com/) [Nouveautés à venir de Ansible v2](http://fr.slideshare.net/jimi-c/whats-new-in-v2-ansiblefest-london-2015) [Ansible weekly neews](https://devopsu.com/newsletters/ansible-weekly-newsletter.html) ---- Pas d'agent =========== Et oui, ni agent Puppet/Chef et ni Puppet master/server à installer. À partir du moment où vous avez un serveur SSH sur votre machine à gérer, elle est potentiellement éligible à la gestion via Ansible. Même la machine permettant de lancer les opérations Ansible peut n'être qu'un banal poste Linux avec les ouvertures de flux qui vont bien. L'autre gros point positif vient du langage en lui même qui est très simple à aborder. Pour faire une analogie, on retrouve le pouvoir descriptif d'un Puppet sans l'aspect prise de tête (les dépendances). En effet, un playbook Ansible exécute ses opérations séquentiellement. Pas besoin de dire que la création de l'utilisateur tif dépend du groupe poil : il suffit de le déclarer en premier et le groupe sera créé en premier. Les seuls aspects que je trouve perfectibles viennent de l'expressivité du langage (qui reste pour l'instant inférieur à Puppet par exemple) et la vitesse d'exécution qui est là encore en deça d'un Agent Puppet (surtout sur les tâches complexes). Pour info, avant de faire de l'Ansible, j'ai fait pas mal de choses avec Puppet pour industrialiser des déploiements de serveurs d'application (type java J2EE). Là où l'agent Puppet ne repassera pas sur les éléments déjà présents et/ou conformes, Ansible procédera à une vérification séquentielle qui peut être significativement plus longue. Mais bon, je vous rassure, ça reste généralement très acceptable. Installation =========== Plusieurs solutions possibles pour l'installation : - via l'utilisation de package rpm ou deb ; - via l'utilisation de pip install ; - enfin directement à l'aide du code source. Sous **RHEL/CentOS**, après avoir activé les ressources **epel**, il suffira de l'installer avec la commande suivante : ``` yum install ansible ``` Sous Ubuntu, il faut **ajouter la source ppa ansible** avec la commande suivante : ``` sudo apt-add-repository ppa:ansible/ansible ``` Suivi du traditionnel **apt-get update/install** : ``` apt-get update && apt-get install ansible. ``` Pour l'installation à l'aide de pip, il faut utiliser la commande suivante : ``` pip install ansible ``` Enfin, pour l'utilisation via le code source, il vous faudra exécuter un git clone : ``` git clone git://github.com/ansible/ansible.git cd ansible make install ``` Première utilisation =========== Nous allons créer un fichier **hosts** dans le répertoire courant de l'utilisateur avec la liste des machines que nous allons gérer : ``` localhost ansible_connection=local [backup] robert [desktop] gertrude ``` Ce fichier d'inventaire indique que `robert` servira à faire des sauvegardes et `gertrude` sera une bête machine _desktop_ (mon poste de travail). Pour la machine localhost, nous avons ajouté l'option **ansible_connection=local** pour indiquer qu'il s'agit d'une machine accessible directement en local sans passer par une connexion SSH. Il nous faut maintenant configurer la communication SSH avec les machines à gérer. Pour ce faire, nous allons échanger les clés SSH avec les différentes machines distantes. Pour ma part, j'utilise beaucoup la commande **ssh-copy-id ** pour initier cet échange de clés, mais rien ne vous empêche de le faire par le mécanisme qu'il vous plaira (et bien sûr, on peut aussi le faire avec Ansible pour peu que l'on dispose de l'utilitaire sshpass). Exemple d'échange de clés entre mon utilisateur et la machine distante `robert` en tant que root : ``` ssh-copy-id root@robert ``` NB : pour éviter d'ajouter root@ devant `robert`, je fais souvent appel au fichier **~/.ssh/config**. En effet, il est possible de spécifier l'utilisateur par défaut. Ci-dessous un exemple pour la machine `robert` : ``` Host robert User root ``` Maintenant que notre échange de clés est réalisé, il est possible de faire un test de création de fichier avec la commande suivante : ``` ansible -i hosts -l localhost -m file -a "path=/tmp/test state=touch" all ``` Dans ce test, nous allons appeler le module **file** afin de créer un fichier vide **/tmp/test** sur la machine `localhost` (grâce à l'option de restriction **-l localhost**). Si tout se passe bien, nous devrions avoir le résultat suivant sur la ligne de commande : ``` $ ansible -i hosts -l localhost -m file -a "path=/tmp/test state=touch" all localhost | success>> { "changed": true, "dest": "/tmp/test", "gid": 1000, "group": "yannig", "mode": "0644", "owner": "yannig", "size": 0, "state": "file", "uid": 1000 } ``` Rien de particulier à signaler, mais vous pouvez maintenant lancer n'importe quel module Ansible. Vous pourriez tout à fait imaginer lancer un arrêt/relance de démon ou la mise à jour d'un paquet système suite à la découverte d'une nouvelle faille SSL/NTP/DNS/noyau (rayer la mention inutile). Pour faire une analogie avec Puppet, il s'agit de l'équivalent de `mcollective` (sans l'aspect middleware de communication puisque tout passe par SSH). Création d'un premier playbook =========== Nous avons maintenant la possibilité de lancer des opérations unitaires. Passons alors à l'écriture d'un premier playbook qui va nous permettre de chaîner un ensemble d'opérations. Notre premier playbook ---------------------- Pour ce faire, nous allons écrire un playbook qui va mettre à jour la configuration du serveur de temps de notre machine. Les opérations sont assez simples et vont consister à : - s'assurer de la présence du package ntpd sur la machine ; - mettre à jour le fichier **/etc/ntp.conf** ; - faire un arrêt/relance le cas échéant. Vous vous en doutez, ces opérations se réalisent en trois lignes de description Ansible. Pour simplifier les choses, nous nous mettrons uniquement dans le contexte d'une distribution de type RHEL/CentOS/Fedora. Voici cette première version : ```yaml --- # Playbook de gestion de NTP - name: "NTP configuration" hosts: all remote_user: root tasks: - name: "NTP package" yum: name=ntp state=installed - name: "NTP configuration" template: src=ntp.conf.j2 dest=/etc/ntp.conf - name: "NTP restart" service: name=ntpd state=restarted enabled=yes ``` Il faudra également stocker le fichier **ntp.conf.j2** au même emplacement. Ci-dessous son contenu : ``` #{{ ansible_managed }} driftfile /var/lib/ntp/drift restrict default nomodify notrap nopeer noquery restrict 127.0.0.1 restrict ::1 {% for i in ntp_servers %} server {{ i }} {% endfor %} includefile /etc/ntp/crypto/pw keys /etc/ntp/keys disable monitor ``` Nous allons également ajouter un fichier **group_vars/all** (au même niveau que le fichier **hosts**) avec le contenu suivant afin de pouvoir gérer la liste de serveur NTP : ```yaml --- ntp_servers: [ "192.168.0.1", "192.168.0.2" ] ``` Lancement du playbook --------------------- Un rapide lancement pour voir ce que ça donne : ``` $ ansible-playbook -i hosts -l robert ntp.yml PLAY [NTP configuration] ****************************************************** GATHERING FACTS *************************************************************** ok: [robert] TASK: [NTP package] *********************************************************** changed: [robert] TASK: [NTP configuration] ***************************************************** changed: [robert] TASK: [NTP restart] *********************************************************** changed: [robert] PLAY RECAP ******************************************************************** robert : ok=4 changed=3 unreachable=0 failed=0 ``` Comme on peut le voir, ça semble bien fonctionner. Relançons notre playbook pour voir le comportement d'Ansible : ```` $ ansible-playbook -i hosts -l robert ntp.yml PLAY [NTP configuration] ****************************************************** GATHERING FACTS *************************************************************** ok: [robert] TASK: [NTP package] *********************************************************** ok: [robert] TASK: [NTP configuration] ***************************************************** ok: [robert] TASK: [NTP restart] *********************************************************** changed: [robert] PLAY RECAP ******************************************************************** robert : ok=4 changed=1 unreachable=0 failed=0 ``` Petit problème : le serveur NTP est systématiquement relancé alors que notre fichier de configuration n'a pas bougé... Introduction du handler ----------------------- Pour éviter cet arrêt/relance intempestif, la méthode la plus naturelle est de passer par un mécanisme de handler. Voici ce que ça donne dans notre cas : ```yaml --- # Playbook de gestion de NTP - name: "NTP configuration" hosts: all remote_user: root tasks: - name: "NTP package" yum: name=ntp state=installed - name: "NTP configuration" template: src=ntp.conf.j2 dest=/etc/ntp.conf # Notification du handler de redémarrage notify: "NTP restart" # Section handler handlers: - name: "NTP restart" service: name=ntpd state=restarted enabled=yes ``` Relançons maintenant notre playbook : ``` $ ansible-playbook -i hosts toto.yml PLAY [NTP configuration] ****************************************************** GATHERING FACTS *************************************************************** ok: [robert] TASK: [NTP package] *********************************************************** ok: [robert] TASK: [NTP configuration] ***************************************************** ok: [robert] PLAY RECAP ******************************************************************** robert : ok=3 changed=0 unreachable=0 failed=0 ``` Nous voyons que le redémarrage n'a plus lieu, évitant des opérations inutiles. Nouveautés v2 ============= La progression du projet Ansible a été très rapide et, comme souvent, l'évolution a été un peu anarchique. Partant de ce constat, les auteurs ont donc eu besoin de remettre les choses à plat. Il s'agit pour l'essentiel d'une réécriture, mais la version 2 apportera également quelques nouveautés. À noter que les auteurs assurent qu'il ne devrait pas y avoir de régression sur les playbooks existants. Bloc d'instructions ----------------- Un des plus gros défauts du langage d'Ansible vient de la gestion des boucles. Prenons un exemple tout simple en créant une liste d'utilisateurs. Cette opération peut se réaliser à l'aide du playbook suivant : ```yaml --- # Playbook de création d'utilisateurs - name: "Création d'utilisateurs" hosts: all remote_user: root vars: - users_to_create: [ 'alain', 'gerard' ] tasks: - name: "Creation des utilisateurs" user: name={{item}} # Boucle sur les utilisateurs with_items: users_to_create ``` Maintenant, si nous voulons en profiter pour mettre à jour le fichier _bash profile_, nous allons devoir procéder avec une seconde boucle : ```yaml tasks: - name: "Creation des utilisateurs" user: name={{item}} # Boucle création utilisateurs with_items: users_to_create - name: "MAJ profile" template: src=profile.j2 dest=/home/{{item}}/.bash_profile # Boucle MAJ profile utilisateurs with_items: users_to_create ``` Bref, il manque une notion d'itération sur un ensemble d'instructions. Cette prochaine version devrait y répondre. Ci-dessous un exemple de ce que devrait pouvoir offrir la version 2 d'Ansible : ```yaml tasks: - include: instructions.yml user={{item}} with_items: users_to_create ``` Avec le fichier **instructions.yml** contenant les instructions suivantes : ```yaml - name: "Creation des utilisateurs" user: name={{item}} - name: "MAJ profile" template: src=profile.j2 dest=/home/{{item}}/.bash_profile ``` Ci-dessous un exemple de lancement avec la version actuelle d'Ansible v2 : ```yaml $ ansible-playbook test/bloque.yml -l localhost -c local PLAY: Création d'utilisateurs **************************************************** TASK [setup] ******************************************************************** ok: [localhost] TASK [include user={{item}}] **************************************************** {'item': u'alain', 'include': u'instructions.yml', 'include_variables': {u'user': u'alain'}, 'changed': True} {'item': u'gerard', 'include': u'instructions.yml', 'include_variables': {u'user': u'gerard'}, 'changed': True} included: instructions.yml for localhost TASK [Creation des utilisateurs] ************************************************ ok: [localhost] TASK [MAJ profile] ************************************************************** ok: [localhost] TASK [Creation des utilisateurs] ************************************************ ok: [localhost] TASK [MAJ profile] ************************************************************** ok: [localhost] PLAY RECAP ********************************************************************** localhost : ok=6 changed=1 unreachable=0 failed=0 ``` Gestion des exceptions ---------------------- Dans la même idée, Ansible devrait voir arriver la gestion des exceptions. Ci-dessous un exemple faisant appel à cette nouvelle notion : ```yaml --- # Playbook d'exception - name: "Test exception" hosts: "all" tasks: - block: - command: "/bin/false" - debug: msg="Ceci n'est pas lance" rescue: - debug: msg="Nous avons eu une exception" - command: "/bin/false" - debug: msg="Nous ne pouvons pas etre ici" always: - debug: msg="Dans tous les cas, nous passons ici" ``` Ci-dessous le résultat de l'exécution de ce playbook : ```yaml $ ansible-playbook exception.yml -l localhost -c local PLAY: Test exception ************************************************************ TASK [setup] ******************************************************************** ok: [localhost] TASK [command] ****************************************************************** fatal: [localhost]: FAILED! => {"changed": true, "end": "2015-04-05 10:44:04.632739", "stdout": "", "cmd": ["/bin/false"], "start": "2015-04-05 10:44:04.631280", "delta": "0:00:00.001459", "stderr": "", "rc": 1, "invocation": {"module_name": "command", "module_args": {"_raw_params": "/bin/false"}}, "stdout_lines": [], "warnings": []} TASK [debug msg=Nous avons eu une exception] ************************************ ok: [localhost] => { "msg": "Nous avons eu une exception", "changed": false } TASK [command] ****************************************************************** fatal: [localhost]: FAILED! => {"changed": true, "end": "2015-04-05 10:44:05.031018", "stdout": "", "cmd": ["/bin/false"], "start": "2015-04-05 10:44:05.028723", "delta": "0:00:00.002295", "stderr": "", "rc": 1, "invocation": {"module_name": "command", "module_args": {"_raw_params": "/bin/false"}}, "stdout_lines": [], "warnings": []} TASK [debug msg=Dans tous les cas, nous passons ici] **************************** ok: [localhost] => { "msg": "Dans tous les cas, nous passons ici", "changed": false } PLAY RECAP ********************************************************************** localhost : ok=3 changed=0 unreachable=0 failed=2 ```

AltStyle によって変換されたページ (->オリジナル) /