Ansible : Voyage au bout de la nuit
J’ai fait un gros crackage avec l’article précédent. Le sujet n’était d’ailleurs pas Ansible mais le nécessaire recul que doit avoir toute personne travaillant dans l’informatique pour « sentir » le prochain virage que prendra l’IT.
J’ai eu tout de même l’intelligence de ne pas placer ce billet dans la série Ansible for the win car il n’avait rien à y faire. Le présent article est dans la série Ansible for the win, j’ai donc la prétention de vous en apprendre un peu plus sur Ansible.
Mais d’abord je vais rendre hommage à quelqu’un : Cabernet138. Cabernet138 est un casse-couille fini. Cabernet, c’est quelqu’un que j’apprécie, vraiment. C’est un casse-couille et c’est la définition qu’utilisera la majorité pour le caractériser (et moi-même). C’est un Don Quichotte des temps modernes qui pourfend les erreurs, mensonges, contre-vérités. Le genre de personnes qui vient vous pourrir et qui ne vous lâche plus. Le genre de personnes dont il faut s’entourer car il ne vous fera pas de cadeaux, pas de pitié, pas de repos. C’est un gros travail d’assumer un mec comme ça, beaucoup de patience, beaucoup de travail mais à la fin il vous poussera à dire toute la vérité, à aller au bout de votre réflexion. Il vous rendra meilleur parce qu’il vous aura pousser dans vos derniers retranchements sans aucune concession. Si vous ne craquez pas avant. C’est la limite. La limite de son fonctionnement c’est notre propre patience, notre propre travail sur soi. Ça ne l’empêche pas d’avoir tort, ça ne l’empêche pas d’être de mauvaise foi mais il force à être honnête, il force à être dans le vrai.
J’ai passé 3 jours complets sur Ansible, entre 20 et 25h, c’est énorme, gigantesque. Je suis convaincu par ce nouvel usage l’automatisation, la gestion de configuration. Il se trouve que l’outil qui me convient (selon mes goûts et mes besoins) c’est Ansible. Alors j’ai affronté mon moulin, mes contradictions. Je me suis planté bien droit devant la montagne pour savoir si oui ou non, Ansible était digne d’être dans ma trousse à outils. Oui, il l’est. Partons maintenant au bout de la nuit.
J’ai trouvé un prétexte qui en est un sans en être un. J’ai décidé d’écrire un playbook de l’installation d’un DokuWiki. En soi, ça ne sert pas à grand-chose. Cependant, pour moi, la documentation c’est sacré et DokuWiki est l’outil principal de documentation au sein de mon entreprise. Et puis l’installation d’un DokuWiki, c’est une étape. La ligne d’arrivée, c’est à partir de ce premier playbook, arriver à la restauration d’un DokuWiki par Ansible. Qui dit restauration, dit tests possibles sur la prod et là on arrive à l’intérêt principal d’un outil comme Ansible, l’automatisation, la gestion de configuration. Réessayer à l’infini, des logiciels différents, des réglages différents. Là c’est plus sexy.
Voici ce que j’ai appris sur les bonnes et les mauvaises choses d’Ansible, croyez moi vous allez apprendre et comprendre des trucs si vous prenez le temps…
1) Le DokuWiki en prod actuellement est sur Apache avec les dépôts Dotdeb activés. Je suis parti sur Nginx pour mon playbook pour me mettre en danger, tester. J’ai appris que VirtualHost c’était un terme Apache. Pour Nginx, il faut dire Server Blocks. Étant donné que même dans leur documentation, ils utilisent le mot VirtualHost, je l’utiliserai aussi hi, hi, hi. Mais je trouve intéressant que vous le sachiez car jamais entendu parlé de ce terme.
2) notify (les handlers) sont probablement ce qui m’a fait le plus perdre mon temps. Ce sont des faux amis et pour moi (je parle donc en mon nom unique), c’est de la merde. Ce qu’il faut en retenir (source) :
a) Regardless of how many things notify a handler, it will run only once, after all of the tasks complete in a particular play
b) Handlers are best used to restart services and trigger reboots. You probably won’t need them for much else
Rendons à César ce qui est à César, j’ai sollicité et posé des questions à Deimos que je remercie particulièrement pour m’avoir fait cogiter et comprendre des trucs. Je me suis notamment basé sur son playbook Dotdeb sur lequel j’ai trouvé des erreurs et des incohérences que je lui ai remonté. Il voulait absolument un handler pour lancer le apt-get update. Ça n’a jamais voulu fonctionner dans mon playbook, ça ne fonctionnera jamais comme dit dans la doc, ça ne s’exécute qu’une seule fois et à la fin du playbook (voir point a). J’ai d’ailleurs vu des exemples sur le net mettant un notify derrière chaque task ou presque, ça ne sert à rien car peu importe le nombre de fois où vous l’appelez ça ne s’exécutera qu’une seule fois.
3) C’est noté noir sur blanc dans la documentation sur les bonnes pratiques, écrivez vos playbooks comme vous le voulez mais avec des rôles ainsi ce sera réutilisable via d’autres playbooks. Et faites simple.
4) On peut mettre des variables dans le nom des tasks (c’est-à-dire un truc comme name: Create {{ vhost_name }} vhost) mais ça ne fonctionne pas tout le temps. Le meilleur lien que j’ai trouvé comme explication : http://grokbase.com/t/gg/ansible-project/149g4mkqnk/having-variables-in-task-names. Je ne suis cependant pas d’accord avec ce qui est dit, d’après mes tests il n’y a que le role invocation qui fonctionne.
5) C’est sur le rôle dotdeb que j’ai perdu le plus de temps notamment à cause de notify mais également à cause de success et changed. Il y a 4 états principaux sur le when : success, changed, skipped, failed (source)
6) Sur le rôle nginx, quelle est la différence entre state=latest et state=present ? C’est simple à comprendre mais il faut un exemple.
a) On renseigne state=present dans la task Install Nginx (roles/nginx/tasks/main.yml) et dotdeb_repo: False dans install_dokuwiki.yml. On lance le playbook. Sans les dépôts Dotdeb vous allez avoir Nginx en version 1.2.1. Maintenant vous faites dotdeb_repo: True dans install_dokuwiki.yml puis vous relancez le playbook. Nginx est toujours en version 1.2.1, normal le paquet doit juste être présent (state=present)
b) On renseigne state=latest dans la task Install Nginx (roles/nginx/tasks/main.yml),et dotdeb_repo: False dans install_dokuwiki.yml. On lance le playbook. Sans les dépôts Dotdeb vous allez avoir Nginx en version 1.2.1. Maintenant vous faites dotdeb_repo: True dans install_dokuwiki.yml puis vous relancez le playbook. Nginx passe alors en version 1.6.1, la dernière version (latest) de Nginx.
7) Une erreur qui est revenue souvent et j’ai cru avoir atteint un problème sur Ansible concerne le rôle dotdeb. Insultes en rouge de Ansible car pas possible de faire un apt: update_cache=yes (un apt-get update quoi). Travaillant avec une VM de tests que je rebootais à chaque fois, je me suis rendu compte qu’elle lançait un apt-get update toute seule quelques minutes après démarrage. La VM verrouillait donc le update car elle était déjà en train d’en faire un.
8) Par rapport au point précédent et ne comprenant pas ce qui se passait, j’ai cherché et trouvé une méthode pour savoir quand était fait le dernier apt-get update sur une Debian et bien il semble qu’il n’y ait pas de commande pour cela. La seule réponse fiable et fonctionnelle c’est de regarder avec ls la date de modification du fichier /var/cache/apt/pkgcache.bin (source). Si quelqu’un a mieux je suis preneur, j’ai trouvé d’autres pistes mais toutes fausses.
9) Une grande satisfaction intellectuelle pour moi a été ce lien.
Avant je faisais ça.
cd /tmp && tar xf dokuwiki-stable.tgz mv /tmp/dokuwiki-date/* /var/www/dokuwiki/
Maintenant je fais ça.
tar xf /tmp/dokuwiki-stable.tgz -C /var/www/dokuwiki --strip-components=1
10) Grâce à Deimos, j’ai compris qu’on pouvait écrire when: dotdeb_repo qui est la même chose que when: dotdeb_repo == True
11) Je ne suis pas arrivé à prendre Ansible une seule fois en défaut. Les fois où il m’insultait étaient des erreurs de mon fait : variables oubliées ou avec une faute, rôles avec des erreurs etc. De mon point de vue, c’est un logiciel parfaitement stable et robuste. Le plus gros point noir pour moi, ce sont les résultats d’un playbook avec -v (verbose) et -vvv (more verbose) :
– Ça aide très peu à trouver son erreur
– C’est très illisible et peu compréhensible
– Spéciale dédicace au -vvv police bleue foncée sur fond noir, on ne voit rien !
Vous avez dit incompréhensible ?
Mon Bilan :
– Que l’on se comprenne bien, je n’ai pas mis plus de 20h à pondre ce playbook et ces rôles, j’ai mis 20h à tester dans tous les sens Ansible : rôles, tasks, vars, messages d’erreur, améliorations, limites, tests. Pour faire simple, je suis rentré dans l’outil, je l’ai mis à l’épreuve
– Mon chef m’invite souvent à faire des labos c’est-à-dire à tester une idée, un proof of concept en montant un pc ou quelques VM. C’est ce que j’ai fait ici, je vous invite à faire de même, il n’y a qu’en testant qu’on peut confronter son idée à la réalité. Je peux dire maintenant de manière sûre et publique que Ansible fait dorénavant partie de ma trousse à outils
– La veille informatique c’est bien mais si on ne s’arrête pas 20 mn pour tester ou participer, on n’en retire pas grand-chose
– Le seul vrai point noir qui est très important de mon point de vue, c’est que le debug (-v et -vvv) est particulièrement mauvais, illisible, peu compréhensible
– S’enfoncer dans un projet ou une idée, ça permet aussi de se remettre en cause et de faire des recherches qu’on aurait pas entreprises autrement, de découvrir de nouvelles choses. J’ai découvert les Server Blocks, ma nouvelle commande tar, la date du dernier update sur une Debian, j’ai envoyé des mails à Deimos (que je remercie encore une fois) bref j’ai ouvert une boîte de Pandore. Le bilan est donc extrêmement positif
Je vous mets une bonne fois pour toute, le résultat d’un ansible localhost -m setup afin de bien voir les variables avec lesquelles vous pouvez jouer directement. C’est sur un pc portable Xubuntu.
localhost | success >> { "ansible_facts": { "ansible_all_ipv4_addresses": [ "172.17.42.1", "192.168.1.19" ], "ansible_all_ipv6_addresses": [ "2a02:8420:6384:4400:d2df:9aff:feb8:72da", "fe80::d2df:9aff:feb8:72da", "2a02:8420:6384:4400:a800:813c:8d23:e065" ], "ansible_architecture": "x86_64", "ansible_bios_date": "01/01/2013", "ansible_bios_version": "A16", "ansible_cmdline": { "BOOT_IMAGE": "/boot/vmlinuz-3.16.0-29-generic", "cgroup_enable": "memory", "quiet": true, "ro": true, "root": "UUID=68e88de3-007f-467a-9b8b-00a3bd7fdce1", "splash": true, "swapaccount": "1", "vt.handoff": "7" }, "ansible_date_time": { "date": "2015-01-23", "day": "23", "epoch": "1422020798", "hour": "14", "iso8601": "2015-01-23T13:46:38Z", "iso8601_micro": "2015-01-23T13:46:38.808128Z", "minute": "46", "month": "01", "second": "38", "time": "14:46:38", "tz": "CET", "tz_offset": "+0100", "weekday": "Friday", "year": "2015" }, "ansible_default_ipv4": { "address": "192.168.1.19", "alias": "wlan0", "gateway": "192.168.1.1", "interface": "wlan0", "macaddress": "d0:df:6b:b8:23:da", "mtu": 1500, "netmask": "255.255.255.0", "network": "192.168.3.0", "type": "ether" }, "ansible_default_ipv6": { "address": "2a02:8420:6384:4400:a800:813c:8d23:e065", "gateway": "fe80::14c8:dfff:fe72:c8c4", "interface": "wlan0", "macaddress": "d0:df:6b:b8:23:da", "mtu": 1500, "prefix": "64", "scope": "global", "type": "ether" }, "ansible_devices": { "sda": { "holders": [], "host": "SATA controller: Intel Corporation 6 Series/C200 Series Chipset Family 6 port SATA AHCI Controller (rev 04)", "model": "OCZ-VERTEX4", "partitions": { "sda1": { "sectors": "122880000", "sectorsize": 512, "size": "58.59 GB", "start": "2048" }, "sda2": { "sectors": "20477952", "sectorsize": 512, "size": "9.76 GB", "start": "122882048" }, "sda3": { "sectors": "106708985", "sectorsize": 512, "size": "50.88 GB", "start": "143360000" } }, "removable": "0", "rotational": "0", "scheduler_mode": "deadline", "sectors": "250069680", "sectorsize": "512", "size": "119.24 GB", "support_discard": "512", "vendor": "ATA" }, "sr0": { "holders": [], "host": "SATA controller: Intel Corporation 6 Series/C200 Series Chipset Family 6 port SATA AHCI Controller (rev 04)", "model": "DVD+-RW TS-U633J", "partitions": {}, "removable": "1", "rotational": "1", "scheduler_mode": "deadline", "sectors": "2097151", "sectorsize": "512", "size": "1024.00 MB", "support_discard": "0", "vendor": "TSSTcorp" } }, "ansible_distribution": "Ubuntu", "ansible_distribution_major_version": "14", "ansible_distribution_release": "utopic", "ansible_distribution_version": "14.10", "ansible_docker0": { "active": false, "device": "docker0", "id": "8000.56847afe9799", "interfaces": [], "ipv4": { "address": "172.17.42.1", "netmask": "255.255.0.0", "network": "172.17.0.0" }, "macaddress": "56:84:7a:fe:97:99", "mtu": 1500, "promisc": false, "stp": false, "type": "bridge" }, "ansible_domain": "", "ansible_env": { "CDPATH": ".:/home/cascador:/etc:/var", "DBUS_SESSION_BUS_ADDRESS": "unix:abstract=/tmp/dbus-Xlr2RTUGJX", "DEFAULTS_PATH": "/usr/share/gconf/xubuntu.default.path", "DESKTOP_SESSION": "xubuntu", "DISPLAY": ":0.0", "GDMSESSION": "xubuntu", "GDM_LANG": "fr_FR", "GLADE_CATALOG_PATH": ":", "GLADE_MODULE_PATH": ":", "GLADE_PIXMAP_PATH": ":", "GNOME_KEYRING_CONTROL": "/run/user/1000/keyring-yMPdoh", "GNOME_KEYRING_PID": "3152", "GPG_AGENT_INFO": "/run/user/1000/keyring-yMPdoh/gpg:0:1", "HISTIGNORE": "[ t]*:his:ee:ff", "HOME": "/home/cascador", "IM_CONFIG_PHASE": "1", "INSTANCE": "", "JOB": "dbus", "LANG": "en_US.UTF-8", "LANGUAGE": "fr_FR", "LC_CTYPE": "en_US.UTF-8", "LOGNAME": "cascador", "LS_COLORS": "rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.axa=00;36:*.oga=00;36:*.spx=00;36:*.xspf=00;36:", "MANDATORY_PATH": "/usr/share/gconf/xubuntu.mandatory.path", "MARKPATH": "/home/cascador/.marks", "OLDPWD": "/home", "PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games", "PWD": "/home/cascador", "SESSION": "xubuntu", "SESSIONTYPE": "", "SESSION_MANAGER": "local/xubuntu:@/tmp/.ICE-unix/3411,unix/xubuntu:/tmp/.ICE-unix/3411", "SHELL": "/bin/bash", "SHLVL": "1", "SSH_AUTH_SOCK": "/run/user/1000/keyring-yMPdoh/ssh", "TERM": "xterm", "TEXTDOMAIN": "im-config", "TEXTDOMAINDIR": "/usr/share/locale/", "UPSTART_EVENTS": "started xsession", "UPSTART_INSTANCE": "", "UPSTART_JOB": "startxfce4", "UPSTART_SESSION": "unix:abstract=/com/ubuntu/upstart-session/1000/3172", "USER": "cascador", "XAUTHORITY": "/home/cascador/.Xauthority", "XDG_CONFIG_DIRS": "/etc/xdg/xdg-xubuntu:/usr/share/upstart/xdg:/etc/xdg:/etc/xdg", "XDG_CURRENT_DESKTOP": "XFCE", "XDG_DATA_DIRS": "/usr/share/xubuntu:/usr/share/xfce4:/usr/local/share/:/usr/share/:/usr/share", "XDG_GREETER_DATA_DIR": "/var/lib/lightdm-data/cascador", "XDG_MENU_PREFIX": "xfce-", "XDG_RUNTIME_DIR": "/run/user/1000", "XDG_SEAT": "seat0", "XDG_SEAT_PATH": "/org/freedesktop/DisplayManager/Seat0", "XDG_SESSION_DESKTOP": "xubuntu", "XDG_SESSION_ID": "c2", "XDG_SESSION_PATH": "/org/freedesktop/DisplayManager/Session0", "XDG_SESSION_TYPE": "x11", "XDG_VTNR": "7", "_": "/usr/local/bin/ansible" }, "ansible_eth0": { "active": false, "device": "eth0", "macaddress": "d4:be:d9:23:44:db", "module": "e1000e", "mtu": 1500, "promisc": false, "type": "ether" }, "ansible_fips": false, "ansible_form_factor": "Laptop", "ansible_fqdn": "xubuntu", "ansible_hostname": "xubuntu", "ansible_interfaces": [ "lo", "docker0", "wwan0", "wlan0", "eth0" ], "ansible_kernel": "3.16.0-29-generic", "ansible_lo": { "active": true, "device": "lo", "ipv4": { "address": "127.0.0.1", "netmask": "255.0.0.0", "network": "127.0.0.0" }, "ipv6": [ { "address": "::1", "prefix": "128", "scope": "host" } ], "mtu": 65536, "promisc": false, "type": "loopback" }, "ansible_lsb": { "codename": "utopic", "description": "Ubuntu 14.10", "id": "Ubuntu", "major_release": "14", "release": "14.10" }, "ansible_machine": "x86_64", "ansible_memfree_mb": 1276, "ansible_memtotal_mb": 3832, "ansible_mounts": [ { "device": "/dev/sda3", "fstype": "ext4", "mount": "/", "options": "rw,noatime,discard,errors=remount-ro", "size_available": 32212090880, "size_total": 53643206656 }, { "device": "/home/cascador/.Private", "fstype": "ecryptfs", "mount": "/home/cascador", "options": "ecryptfs_check_dev_ruid,ecryptfs_cipher=aes,ecryptfs_key_bytes=16,ecryptfs_unlink_sigs,ecryptfs_sig=c80567d4228e0d12,ecryptfs_fnek_sig=af31064d55abedb5", "size_available": 32212090880, "size_total": 53643206656 } ], "ansible_nodename": "xubuntu", "ansible_os_family": "Debian", "ansible_pkg_mgr": "apt", "ansible_processor": [ "GenuineIntel", "Intel(R) Core(TM) i5-2520M CPU @ 2.50GHz", "GenuineIntel", "Intel(R) Core(TM) i5-2520M CPU @ 2.50GHz", "GenuineIntel", "Intel(R) Core(TM) i5-2520M CPU @ 2.50GHz", "GenuineIntel", "Intel(R) Core(TM) i5-2520M CPU @ 2.50GHz" ], "ansible_processor_cores": 2, "ansible_processor_count": 1, "ansible_processor_threads_per_core": 2, "ansible_processor_vcpus": 4, "ansible_product_name": "Latitude E6420", "ansible_product_serial": "NA", "ansible_product_uuid": "NA", "ansible_product_version": "01", "ansible_python_version": "2.7.8", "ansible_selinux": false, "ansible_swapfree_mb": 0, "ansible_swaptotal_mb": 0, "ansible_system": "Linux", "ansible_system_vendor": "Dell Inc.", "ansible_user_id": "cascador", "ansible_userspace_architecture": "x86_64", "ansible_userspace_bits": "64", "ansible_virtualization_role": "host", "ansible_virtualization_type": "kvm", "ansible_wlan0": { "active": true, "device": "wlan0", "ipv4": { "address": "192.168.3.59", "netmask": "255.255.255.0", "network": "192.168.3.0" }, "ipv6": [ { "address": "2a02:8420:6384:4400:d2df:9aff:feb8:72da", "prefix": "64", "scope": "global" }, { "address": "fe80::d2df:9aff:feb8:72da", "prefix": "64", "scope": "link" }, { "address": "2a02:8420:6384:4400:a800:813c:8d23:e065", "prefix": "64", "scope": "global" } ], "macaddress": "d0:df:6b:b8:23:da", "module": "brcmsmac", "mtu": 1500, "promisc": false, "type": "ether" }, "ansible_wwan0": { "active": false, "device": "wwan0", "macaddress": "02:80:37:ab:01:00", "module": "cdc_ncm", "mtu": 1500, "promisc": false, "type": "ether" }, "module_setup": true }, "changed": false }
Les commentaires sont fermés.