<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Ansible on Zwindler's Reflection</title><link>https://blog.zwindler.fr/tags/ansible/</link><description>Recent content in Ansible on Zwindler's Reflection</description><generator>Hugo -- gohugo.io</generator><language>fr-fr</language><copyright>Licensed under CC BY-SA 4.0</copyright><lastBuildDate>Thu, 20 Apr 2023 18:00:00 +0200</lastBuildDate><atom:link href="https://blog.zwindler.fr/tags/ansible/index.xml" rel="self" type="application/rss+xml"/><item><title>Rudder - astuces en vrac</title><link>https://blog.zwindler.fr/2023/04/20/rudder-astuces-en-vrac/</link><pubDate>Thu, 20 Apr 2023 18:00:00 +0200</pubDate><guid>https://blog.zwindler.fr/2023/04/20/rudder-astuces-en-vrac/</guid><description>&lt;img src="https://blog.zwindler.fr/2023/03/rudder_logo.webp" alt="Featured image of post Rudder - astuces en vrac" /&gt;&lt;p&gt;Cet article fait partie d&amp;rsquo;une suite d&amp;rsquo;articles sur Rudder. La version qui est testée ici sera la version open source (car je n&amp;rsquo;ai pas les moyens de me payer une licence pour mon infra perso 😛) :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.zwindler.fr/2023/03/19/premiers-pas-avec-rudder-installation/" &gt;Présentation, installation du serveur et des agents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.zwindler.fr/2023/03/27/premiers-pas-avec-rudder-concept-configuration/" &gt;Concepts et configuration des groupes, des directives, des rules&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.zwindler.fr/2023/04/20/rudder-astuces-en-vrac/" &gt;Astuces en vrac&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Note : une fois de plus, je tiens à préciser qu&amp;rsquo;il ne s&amp;rsquo;agit PAS d&amp;rsquo;un article sponsorisé. J&amp;rsquo;ai eu des contacts (techniques) avec la team Rudder, mais je n&amp;rsquo;ai été influencé d&amp;rsquo;aucune manière, en particulier dans la rédaction de cet article.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="but-de-larticle"&gt;But de l&amp;rsquo;article
&lt;/h2&gt;&lt;p&gt;Dans les deux articles précédents, j&amp;rsquo;ai parlé des concepts de Rudder, de l&amp;rsquo;installation du serveur et des agents. Je suis allé un poil plus loin avec mon installation perso mais avec ce que j&amp;rsquo;ai déjà écrit, on est déjà en capacité de jouer avec Rudder et je pense pas qu&amp;rsquo;on ait besoin d&amp;rsquo;aller beaucoup plus loin.&lt;/p&gt;
&lt;p&gt;Cependant, au cours de mes péripéties avec Rudder, je suis tombé sur quelques petits &amp;ldquo;os&amp;rdquo;. Rien de bien poussé, qui ne justifierait un article seul. Je vais donc tout regrouper ici, en vrac ;-P.&lt;/p&gt;
&lt;h2 id="tuning-pour-les-petits-cpus-et-les-petites-rams"&gt;Tuning pour les petits CPUs et les petites RAMs
&lt;/h2&gt;&lt;p&gt;On l&amp;rsquo;a dit dans le premier article, Rudder est assez doux en termes de prérequis matériels. Quelques Mo de mémoire suffisent pour l&amp;rsquo;agent, et grosso modo pas besoin de machine de guerre pour le CPU.&lt;/p&gt;
&lt;p&gt;Cependant, comme je suis extrêmement économe (aka radin) dans la gestion de mon infra perso, je suis quasiment un anti pattern dans le cas de Rudder. J&amp;rsquo;ai un cluster Proxmox monté sur des machines louées à 6€/mois à base d&amp;rsquo;un antédiluvien Atom C2350 (des duals core de 2013 !).&lt;/p&gt;
&lt;p&gt;C&amp;rsquo;est d&amp;rsquo;ailleurs pour essayer de grappiller quelques % de CPU que j&amp;rsquo;avais publié les articles &lt;a class="link" href="https://blog.zwindler.fr/2020/07/06/optimisation-de-pfsense-dans-proxmox-ve/" &gt;Optimisation de PFsense dans Proxmox VE&lt;/a&gt; et &lt;a class="link" href="https://blog.zwindler.fr/2022/10/22/proxmox-tips-tricks/" &gt;Proxmox - Tips and tricks&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Ces machines ne supportant même pas les instructions VT-x, je ne fais donc pas de machines virtuelles mais uniquement des machines à bases de containers LXC. Je me retrouve donc à installer une dizaine d&amp;rsquo;agents (oui, je suis vraiment bourrin) sur un même Atom.&lt;/p&gt;
&lt;p&gt;Rudder a beau être économe, multiplier les agents sur une machine aussi faible avait un coût difficilement soutenable sur ma machine, comme en témoigne le graphique CPU ci-dessous (mon serveur ne fait quasiment rien, c&amp;rsquo;est donc quasiment exclusivement de la charge Rudder).&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2023/04/cpu_avant_tuning.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Il est très peu probable que vous ayez &lt;strong&gt;vous&lt;/strong&gt; une charge perceptible sur vos infras. Dans le cas de machines aussi peu puissantes, il est probable que la team Rudder recommanderait de ne pas installer d&amp;rsquo;agent sur mes containers LXC. Mais dans le cas de machines très très peu puissantes et/ou très très densifiées en termes de nombre de VMs, sachez qu&amp;rsquo;il est quand même possible de tuner le scheduling des runs de Rudder et ainsi limiter la casse.&lt;/p&gt;
&lt;p&gt;Dans le menu &lt;strong&gt;Administration&lt;/strong&gt; / &lt;strong&gt;Settings&lt;/strong&gt;, il existe une section &lt;strong&gt;Agent run schedule&lt;/strong&gt;. Par défaut, l&amp;rsquo;ordonnancement est prévu de la façon suivante : Rudder lance les vérifications / corrections sur sa liste d&amp;rsquo;agent sur une plage de 5 minutes, le premier tiré au hasard commençant à 0 minutes (directement donc) et le dernier jusqu&amp;rsquo;à 4 minutes (1 minute avant la fin de la fenêtre de tir).&lt;/p&gt;
&lt;p&gt;Note : au-delà de mon cas particulier, c&amp;rsquo;est quand même intéressant à savoir, car en fonction de ce qu&amp;rsquo;on lancera (scripts très longs par exemple, upgrades &lt;code&gt;apt&lt;/code&gt;, &amp;hellip;), il peut être intéressant de tuner ce dernier paramètre (Maximum delay) pour être certain que 2 jobs ne se chevauchent pas.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2023/04/agent_run_schedule.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;En modifiant le scheduling pour que les runs se lancent non pas sur 5 minutes mais sur 30, avec un dernier lancement à la 29 ème minute, j&amp;rsquo;ai pu retrouver une charge relativement acceptable sur mon infra.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2023/04/cpu_apres_tuning.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Deuxième astuce, si vous êtes limité en RAM, que vous avez moins de 50 machines et que vous n&amp;rsquo;utilisez pas beaucoup les plugins, il est possible de diminuer la taille de la JVM de rudder (par défaut à 1 Go) et ainsi un peu mieux respirer sur une machine à 2 Go (prérequis Rudder, un peu justes).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sed -i &lt;span class="s1"&gt;&amp;#39;s/JAVA_XMX=1024/JAVA_XMX=512/&amp;#39;&lt;/span&gt; /etc/default/rudder-jetty
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl restart rudder-server.service
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Merci Nicolas pour l&amp;rsquo;astuce :)&lt;/p&gt;
&lt;h2 id="changer-le-nom-des-machines-du-point-de-vue-de-rudder"&gt;Changer le nom des machines du point de vue de Rudder
&lt;/h2&gt;&lt;p&gt;Autre problème, je n&amp;rsquo;ai pas toujours la main sur le FQDN des machines que je loue. En particulier, le retour de la commande &lt;code&gt;hostname -f&lt;/code&gt; renvoit parfois le nom de la machine telle que l&amp;rsquo;appelle l&amp;rsquo;hébergeur.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2023/04/fqdn.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Je me retrouve avec un inventaire rudder plein de machines avec des noms du type &lt;code&gt;int.nua.ge&lt;/code&gt; et &lt;code&gt;randomid.dedibox.fr&lt;/code&gt;&amp;hellip;&lt;/p&gt;
&lt;p&gt;Dans certains cas, je peux le modifier moi-même et/ou feinter Rudder (en modifiant le fichier /etc/hosts par exemple) mais plutôt que de faire ça, j&amp;rsquo;ai préféré utiliser une technique détaillée dans la documentation officielle de Rudder appelée &lt;a class="link" href="https://docs.rudder.io/reference/7.2/usage/advanced_node_management.html#override-node-fqdn" target="_blank" rel="noopener"
&gt;override node fqdn&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;La méthode pour le faire à la main est la suivante. En tant que root, je crée un script qui renvoie un JSON contenant une variable avec le nom que je souhaite pour mon hôte :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;# mkdir -p /var/rudder/hooks.d/
# vi /var/rudder/hooks.d/override-hostname.sh
echo &amp;#39;{&amp;#34;rudder_override_hostname&amp;#34;: &amp;#34;myhost.example.org&amp;#34;}&amp;#39;
# chmod +x /var/rudder/hooks.d/override-hostname.sh
# rudder agent inventory
Rudder agent 7.2.4
Node uuid: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;C&amp;rsquo;est relativement simple, mais j&amp;rsquo;étais pas satisfait car ça nécessite une action manuelle et je suis super fainéant. Comme les noms courts de mes machines sont eux toujours bons, quel que soit le provider et que je connais le domaine par avance (zwindler.fr), j&amp;rsquo;ai créé une&lt;strong&gt;directive&lt;/strong&gt; Rudder pour le faire à ma place !&lt;/p&gt;
&lt;p&gt;A partir d&amp;rsquo;une technique de type &lt;strong&gt;Distribution files&lt;/strong&gt; / &lt;strong&gt;File content&lt;/strong&gt;, j&amp;rsquo;ai mis les valeurs suivantes :&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2023/04/file_content1.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2023/04/file_content2.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Et voilà le résultat, 5 minutes pour tard :)&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2023/04/fqdn2.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;h2 id="agent-qui-ne-démarre-pas-après-une-désinstallation--réinstallation"&gt;Agent qui ne démarre pas après une désinstallation / réinstallation
&lt;/h2&gt;&lt;p&gt;Au début, quand je commençais à jouer avec Rudder, je me suis un peu mélangé les pinceaux au point qu&amp;rsquo;il était plus simple de tout désinstaller et recommencer proprement.&lt;/p&gt;
&lt;p&gt;En théorie, quand on veut debug et/ou réinstaller un agent qui marche mal, on peut utiliser les commandes suivantes :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;rudder agent factory-reset
rudder agent inventory
rudder agent check
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Sauf que dans mon cas, j&amp;rsquo;avais désinstallé puis réinstallé l&amp;rsquo;agent. Et à chaque fois, tout avait l&amp;rsquo;air OK, mais rudder ne faisait rien et voici le message d&amp;rsquo;erreur que j&amp;rsquo;avais :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;rudder agent check
INFO: No disable file detected and no agent executor process either. Restarting agent service... Done
WARNING: The file /var/rudder/cfengine-community/last_successful_inputs_update is older than twice 10 minutes, the agent is probably stuck. Purging the CFEngine lock database... Done
FINISH: Rudder agent check ran properly, please look at messages above to see if there has been any error.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Pour essayer de comprendre pourquoi Rudder ne se lançait jamais alors que la communication avec le serveur semblait OK, je suis allé voir ce que faisait le service systemd &lt;strong&gt;rudder-agent.service&lt;/strong&gt;.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;cat /lib/systemd/system/rudder-agent.service
[Unit]
Description=Rudder agent umbrella service
Documentation=man:rudder(8)
Documentation=https://docs.rudder.io
After=syslog.target
# Dependencies register themselves with a WantedBy/RequiredBy
# currently they are rudder-cf-serverd and rudder-cf-execd
[Service]
Type=oneshot
RemainAfterExit=yes
# This is required for the service to be able to
# pass these commands to its dependencies
ExecStart=/bin/true
ExecReload=/bin/true
[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Et là, c&amp;rsquo;était tout de suite plus clair. Le service rudder-agent est en réalité un simple service &amp;ldquo;chapeau&amp;rdquo;, dont dépendent 2 sous services &lt;code&gt;rudder-cf-serverd&lt;/code&gt; et &lt;code&gt;rudder-cf-execd&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Or, par défaut, quand vous désinstallez &lt;strong&gt;rudder-agent&lt;/strong&gt; (à minima sur Debian/Ubuntu), les services &lt;code&gt;rudder-cf-serverd.service&lt;/code&gt; et &lt;code&gt;rudder-cf-execd.service&lt;/code&gt; sont &amp;ldquo;disabled&amp;rdquo; mais pas re-&amp;ldquo;enabled&amp;rdquo; à la réinstallation.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;root@node01:/# systemctl status rudder-cf-serverd
● rudder-cf-serverd.service - CFEngine file server
Loaded: loaded (/lib/systemd/system/rudder-cf-serverd.service; disabled; vendor preset: enabled)
Active: inactive (dead)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Rudder ne faisait rien, tout simplement parce qu&amp;rsquo;il n&amp;rsquo;était juste pas activé :D. On règle ça en 4 commandes :-P&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;root@node01:/# systemctl enable rudder-cf-serverd
Created symlink /etc/systemd/system/multi-user.target.wants/rudder-cf-serverd.service → /lib/systemd/system/rudder-cf-serverd.service.
Created symlink /etc/systemd/system/rudder-agent.service.wants/rudder-cf-serverd.service → /lib/systemd/system/rudder-cf-serverd.service.
root@node01:/# systemctl start rudder-cf-serverd
root@node01:/# systemctl enable rudder-cf-execd
Created symlink /etc/systemd/system/multi-user.target.wants/rudder-cf-execd.service → /lib/systemd/system/rudder-cf-execd.service.
Created symlink /etc/systemd/system/rudder-agent.service.requires/rudder-cf-execd.service → /lib/systemd/system/rudder-cf-execd.service.
root@node01:/# systemctl start rudder-cf-serverd
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note : je ne suis pas convaincu que ce soit un comportement voulu, j&amp;rsquo;ai remonté le point à la team Rudder, je vous tiendrais au courant.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;Voilà les quelques petits trucs rigolos que j&amp;rsquo;ai eu avec Rudder pour l&amp;rsquo;instant.&lt;/p&gt;
&lt;p&gt;Je ne sais pas quand je ferai le prochain article sur Rudder. La version 8 approche doucement, ça sera peut-être à cette occasion (il y a une fonctionnalité que j&amp;rsquo;attends dedans).&lt;/p&gt;
&lt;p&gt;On a pas non plus parlé des plugins. Ca pourrait valoir le coup (il en existe certains uniquement utilisables avec la version entreprise que je n&amp;rsquo;ai pas, mais aussi des gratuits).&lt;/p&gt;
&lt;p&gt;A voir. En attendant, have fun !&lt;/p&gt;</description></item><item><title>Premiers pas avec Rudder - concepts et configuration</title><link>https://blog.zwindler.fr/2023/03/24/premiers-pas-avec-rudder-concept-configuration/</link><pubDate>Fri, 24 Mar 2023 06:30:00 +0200</pubDate><guid>https://blog.zwindler.fr/2023/03/24/premiers-pas-avec-rudder-concept-configuration/</guid><description>&lt;img src="https://blog.zwindler.fr/2023/03/rudder_logo.webp" alt="Featured image of post Premiers pas avec Rudder - concepts et configuration" /&gt;&lt;p&gt;Cet article fait partie d&amp;rsquo;une suite d&amp;rsquo;articles sur Rudder. La version qui est testée ici sera la version open source (car je n&amp;rsquo;ai pas les moyens de me payer une licence pour mon infra perso 😛) :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.zwindler.fr/2023/03/19/premiers-pas-avec-rudder-installation/" &gt;Présentation, installation du serveur et des agents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.zwindler.fr/2023/03/27/premiers-pas-avec-rudder-concept-configuration/" &gt;Concepts et configuration des groupes, des directives, des rules&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.zwindler.fr/2023/04/20/rudder-astuces-en-vrac/" &gt;Astuces en vrac&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Note : une fois de plus, je tiens à préciser qu&amp;rsquo;il ne s&amp;rsquo;agit PAS d&amp;rsquo;un article sponsorisé. J&amp;rsquo;ai eu des contacts (techniques) avec la team Rudder, mais je n&amp;rsquo;ai été influencé d&amp;rsquo;aucune manière, en particulier dans la rédaction de cet article.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="on-reprend-où-on-en-était"&gt;On reprend où on en était
&lt;/h2&gt;&lt;p&gt;Dans l&amp;rsquo;article précédent, je m&amp;rsquo;étais arrêté après l&amp;rsquo;installation du serveur Rudder sur une machine Ubuntu, ainsi que d&amp;rsquo;un agent.&lt;/p&gt;
&lt;p&gt;Dans cet article, je pars du principe qu&amp;rsquo;on est un peu plus loin dans le temps. On a installé des agents à droite à gauche.&lt;/p&gt;
&lt;p&gt;Dans mon cas, j&amp;rsquo;ai une dizaine de machines Debian et Ubuntu, provenant de VMs (chez nua.ge) et d&amp;rsquo;hyperviseurs/containers LXC (Proxmox VE).&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2023/03/rudder2_begin.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Pour le &amp;ldquo;fun&amp;rdquo;, j&amp;rsquo;aurais pu faire l&amp;rsquo;effort d&amp;rsquo;installer des machines Windows mais je n&amp;rsquo;ai pas eu le courage. Peut-être une autre fois.&lt;/p&gt;
&lt;h2 id="node-inventory"&gt;Node Inventory
&lt;/h2&gt;&lt;p&gt;C&amp;rsquo;est cool d&amp;rsquo;avoir une flotte de machines dans notre Rudder, mais qu&amp;rsquo;est-ce qu&amp;rsquo;on peut faire avec ?&lt;/p&gt;
&lt;p&gt;La première chose qu&amp;rsquo;on peut faire, c&amp;rsquo;est d&amp;rsquo;aller voir ce que l&amp;rsquo;agent remonte comme info au serveur. Un peu comme les &amp;ldquo;facts&amp;rdquo; d&amp;rsquo;Ansible. Ca nous sera utile par la suite.&lt;/p&gt;
&lt;p&gt;On va donc choisir un de nos &amp;ldquo;nodes&amp;rdquo; dans la liste et regarder l&amp;rsquo;onglet Inventory pour voir ce qu&amp;rsquo;on peut faire avec.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2023/03/rudder2_inventory.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Je n&amp;rsquo;ai pas creusé en détail pour savoir s&amp;rsquo;il y avait plus d&amp;rsquo;infos collectées que ce qui est affiché dans ce menu, mais on a déjà de quoi faire, en parcourant toutes les catégories.&lt;/p&gt;
&lt;p&gt;Grosso modo on va utiliser ces infos-là pour définir des groupes, puis l&amp;rsquo;état souhaité de nos machines.&lt;/p&gt;
&lt;h2 id="groups"&gt;Groups
&lt;/h2&gt;&lt;p&gt;Dans la terminologie Rudder, les serveurs sont des &amp;ldquo;nodes&amp;rdquo;, on en a déjà parlé. On va ensuite regrouper nos nodes par &amp;ldquo;groups&amp;rdquo; (avec une relation n-to-n).&lt;/p&gt;
&lt;p&gt;Il y a plusieurs catégories de groupes. On peut soit regrouper les nodes par groupes statiques (pas super efficace quand on passe à l&amp;rsquo;échelle), soit tirer parti des informations trouvées dans l&amp;rsquo;inventaire (cf section précédente) pour créer des groupes dynamiques.&lt;/p&gt;
&lt;p&gt;Enfin, il existe une dernière catégorie de groupes, dits &amp;ldquo;system&amp;rdquo; qui des groupes spéciaux gérés par Rudder.&lt;/p&gt;
&lt;p&gt;On retrouve ça dans l&amp;rsquo;interface de Rudder dans la partie &lt;strong&gt;Node management / Groups&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2023/03/rudder2_groups_menu.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;h2 id="créer-un-groupe-pour-tous-les-serveurs-ubuntu"&gt;Créer un groupe pour tous les serveurs Ubuntu
&lt;/h2&gt;&lt;p&gt;Comme j&amp;rsquo;ai beaucoup de serveurs Ubuntu, la première idée qui m&amp;rsquo;est venue à l&amp;rsquo;esprit a été de créer un groupe pour les regrouper.&lt;/p&gt;
&lt;p&gt;Dans ce menu, il existe 2 choses qu&amp;rsquo;on peut créer. Des &lt;strong&gt;Categories&lt;/strong&gt; et des &lt;strong&gt;Groups&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Je fais un mini aparté sur les &lt;strong&gt;Categories&lt;/strong&gt;, concept qu&amp;rsquo;on pourra retrouver ailleurs dans l&amp;rsquo;interface. Il s&amp;rsquo;agit de subdivisions logiques (des &amp;ldquo;dossiers&amp;rdquo;, en gros) qui permettent de faciliter le rangement de nos bidules.&lt;/p&gt;
&lt;p&gt;Comme j&amp;rsquo;aime bien que les choses soient bien rangées, j&amp;rsquo;ai commencé donc par créer une catégorie &amp;ldquo;By OS&amp;rdquo; pour regrouper tous mes groupes.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2023/03/rudder2_groups_categories.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Une fois que c&amp;rsquo;est fait, on peut rentrer dans le dur et créer un groupe dynamique pour nos serveurs Ubuntu.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2023/03/rudder2_group_byos_1.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Quand on clique sur &lt;em&gt;create&lt;/em&gt;, on peut ensuite ajouter les conditions pour déterminer si un hôte est dans le groupe ou pas.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2023/03/rudder2_group_byos_2.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Jusque-là, rien de bien foufou, on demande à Rudder de se baser sur les infos de l&amp;rsquo;inventaire et de matcher ou pas avec une regex.&lt;/p&gt;
&lt;p&gt;Je note la petite feature de preview, qui permet de voir d&amp;rsquo;un coup d&amp;rsquo;œil si ça matche bien les bons hosts. C&amp;rsquo;est très sympa :)&lt;/p&gt;
&lt;p&gt;Autre remarque, le menu déroulant est assez pratique à utiliser et assez fourni (c&amp;rsquo;est les infos de l&amp;rsquo;inventaire, donc assez riche).&lt;/p&gt;
&lt;p&gt;Un exemple de ce qu&amp;rsquo;on peut faire juste avec la partie &amp;ldquo;node summary&amp;rdquo; :&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2023/03/rudder2_group_byos_3.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Pour l&amp;rsquo;instant, rien de bien compliqué/original.&lt;/p&gt;
&lt;h2 id="un-peu-de-théorie"&gt;Un peu de théorie
&lt;/h2&gt;&lt;p&gt;C&amp;rsquo;est là où ça se corse (un peu, pas beaucoup). Avant d&amp;rsquo;aller plus loin, il va falloir comprendre quelques concepts propres à Rudder.&lt;/p&gt;
&lt;p&gt;Je pompe honteusement &lt;a class="link" href="https://docs.rudder.io/reference/7.2/usage/configuration_management.html" target="_blank" rel="noopener"
&gt;le schéma de la doc officielle&lt;/a&gt; pour faire un support visuel, puis j&amp;rsquo;explique ensuite :&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2023/03/rudder2_concepts.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;On a déjà parlé de notre relation &lt;em&gt;n-to-n&lt;/em&gt; avec nos nodes et nos groups. Si on se concentre sur la partie gauche, on découvre 3 nouveaux concepts :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;les techniques&lt;/li&gt;
&lt;li&gt;les directives&lt;/li&gt;
&lt;li&gt;les rules&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Les techniques, c&amp;rsquo;est les fonctionnalités que rudder va appliquer ou vérifier sur nos nodes. Les techniques sont configurables et c&amp;rsquo;est pour ça qu&amp;rsquo;on va créer des directives à partir de ces techniques (relation &lt;em&gt;1-to-n&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;Et pour finir, les rules sont ce qui va relier un certain nombre de groupes à un certain nombre de directives.&lt;/p&gt;
&lt;h2 id="premier-exemple-de-directive"&gt;Premier exemple de directive
&lt;/h2&gt;&lt;p&gt;Quand j&amp;rsquo;ai commencé à tester Rudder, une CVE (de plus) venait de sortir sur &lt;code&gt;sudo&lt;/code&gt;. Je voulais voir si Rudder était capable de vérifier si mes binaires sur mes ubuntu étaient patchés ou pas.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2023/03/rudder2_sudo.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Je suis donc allé dans le menu &lt;strong&gt;Directives&lt;/strong&gt;, cliqué dans la liste sur la technique &lt;strong&gt;Packages&lt;/strong&gt; et j&amp;rsquo;ai créé ma directive à partie de cette technique.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2023/03/rudder2_directive5.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Cette directive est relativement triviale, on fera des exemples plus complexes dans l&amp;rsquo;article suivant. Mais ça vous donne une idée du concept.&lt;/p&gt;
&lt;h2 id="première-rule"&gt;Première rule
&lt;/h2&gt;&lt;p&gt;Une fois la directive sauvegardée, on va créer une &lt;em&gt;rule&lt;/em&gt; qui va regrouper nos &lt;em&gt;groups&lt;/em&gt; et notre &lt;em&gt;directive&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2023/03/rudder2_rule_directive.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2023/03/rudder2_rule_group.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Comme vous pouvez le voir, c&amp;rsquo;est relativement trivial. Une fois que c&amp;rsquo;est validé, rudder passe sur tous nos nodes pour appliquer la nouvelle configuration.&lt;/p&gt;
&lt;p&gt;On peut retrouver sur le Dashboard principal pour voir ce qu&amp;rsquo;il se passe de manière globale&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2023/03/rudder2_directive_applied.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Ou aller sur un node en détail et aller lire l&amp;rsquo;historique&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2023/03/rudder2_node_logs.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;h2 id="et-si-je-ne-veux-pas-appliquer-"&gt;Et si je ne veux pas appliquer ?
&lt;/h2&gt;&lt;p&gt;Par défaut, le &amp;ldquo;Policy mode&amp;rdquo; est positionné sur &amp;ldquo;Enforce&amp;rdquo;, ce qui veut dire que Rudder va auditer nos systèmes, &lt;strong&gt;puis&lt;/strong&gt; les corriger s&amp;rsquo;ils ne sont pas conformes.&lt;/p&gt;
&lt;p&gt;Si on veut faire ça, il suffit de positionner la directive sur &amp;ldquo;Audit&amp;rdquo;, et utiliser l&amp;rsquo;option &amp;ldquo;This specific version&amp;rdquo; plutôt que &amp;ldquo;Latest available version&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2023/03/rudder2_audit.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Voici le résultat sur un node qui n&amp;rsquo;était pas à jour :&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2023/03/rudder2_non_compliant.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;Voilà pour une très brève introduction sur le système de configuration des nodes et des politiques de conformité avec Rudder.&lt;/p&gt;
&lt;p&gt;J&amp;rsquo;ai pris un exemple très très basique, mais il permet de se faire une idée rapide de la facilité d&amp;rsquo;utilisation de Rudder.&lt;/p&gt;</description></item><item><title>Premiers pas avec Rudder - Installation</title><link>https://blog.zwindler.fr/2023/03/19/premiers-pas-avec-rudder-installation/</link><pubDate>Sun, 19 Mar 2023 14:30:00 +0200</pubDate><guid>https://blog.zwindler.fr/2023/03/19/premiers-pas-avec-rudder-installation/</guid><description>&lt;img src="https://blog.zwindler.fr/2023/03/rudder_logo.webp" alt="Featured image of post Premiers pas avec Rudder - Installation" /&gt;&lt;p&gt;Cet article fait partie d&amp;rsquo;une suite d&amp;rsquo;articles sur Rudder. La version qui est testée ici sera la version open source (car je n&amp;rsquo;ai pas les moyens de me payer une licence pour mon infra perso 😛) :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.zwindler.fr/2023/03/19/premiers-pas-avec-rudder-installation/" &gt;Présentation, installation du serveur et des agents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.zwindler.fr/2023/03/27/premiers-pas-avec-rudder-concept-configuration/" &gt;Concepts et configuration des groupes, des directives, des rules&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.zwindler.fr/2023/04/20/rudder-astuces-en-vrac/" &gt;Astuces en vrac&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Note : une fois de plus, je tiens à préciser qu&amp;rsquo;il ne s&amp;rsquo;agit PAS d&amp;rsquo;un article sponsorisé. J&amp;rsquo;ai eu des contacts (techniques) avec la team Rudder, mais je n&amp;rsquo;ai été influencé d&amp;rsquo;aucune manière, en particulier dans la rédaction de cet article.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="mais-dabord-cest-quoi-rudder-"&gt;Mais d&amp;rsquo;abord, c&amp;rsquo;est quoi Rudder ?
&lt;/h2&gt;&lt;p&gt;&lt;a class="link" href="https://www.rudder.io/fr/" target="_blank" rel="noopener"
&gt;Rudder est un logiciel open-source de gestion de configuration et d&amp;rsquo;automatisation des systèmes&lt;/a&gt;, qui permet aux administrateurs système de contrôler et de gérer leurs infrastructures de manière industrialisée. Il est en grande majorité édité par la société Normation, qui vend du support et des fonctionnalités complémentaires (plugins).&lt;/p&gt;
&lt;p&gt;Pendant longtemps, j&amp;rsquo;ai un peu mis de côté Rudder, pensant que ce n&amp;rsquo;était pas pour moi, surtout que j&amp;rsquo;avais investi beaucoup de temps dans Ansible (vous trouverez mes &lt;a class="link" href="https://blog.zwindler.fr/recherche/?keyword=ansible" &gt;nombreux articles sur le sujet ici&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Cependant, ce qui fait à la fois la force et la faiblesse d&amp;rsquo;Ansible, c&amp;rsquo;est le fait qu&amp;rsquo;il soit &lt;em&gt;agentless&lt;/em&gt;. On peut lancer nos playbooks depuis n&amp;rsquo;importe quelle machine, mais ça veut aussi dire qu&amp;rsquo;il n&amp;rsquo;y a pas a priori de machine privilégiée pour garder notre infra conforme dans la durée.&lt;/p&gt;
&lt;p&gt;Alors oui, je sais qu&amp;rsquo;on va me rétorquer qu&amp;rsquo;il existe Tower/AWX, mais je n&amp;rsquo;ai pas du tout apprécié l&amp;rsquo;expérience le peu que je l&amp;rsquo;ai testé, au point de ne pas vouloir l&amp;rsquo;utiliser ni au travail, ni en perso. En particulier en perso, je trouve l&amp;rsquo;outil lourd et pas adapté pour mon objectif : garantir la conformité de mes quelques machines.&lt;/p&gt;
&lt;p&gt;Parallèlement à ça, Rudder fonctionne avec un agent, et il y a un serveur centralisé qui contrôle les serveurs enregistrés. On peut consulter le tout dans une interface web et on voit en un clin d&amp;rsquo;oeil si les serveurs sont conformes ou pas.&lt;/p&gt;
&lt;p&gt;Sans juger de l&amp;rsquo;expérience avec des centaines voire des milliers de serveurs, ça me parait adapté pour mon besoin perso (gérer une flotte de quelques machines voire dizaines de machines).&lt;/p&gt;
&lt;h2 id="installation-du-serveur"&gt;Installation du serveur
&lt;/h2&gt;&lt;p&gt;Je vais essayer de ne pas passer trop de temps sur cette partie. Elle a déjà été détaillée récemment par &lt;a class="link" href="https://blog.stephane-robert.info/post/introduction-rudder/" target="_blank" rel="noopener"
&gt;Stéphane Robert sur son blog&lt;/a&gt; et par &lt;a class="link" href="https://www.youtube.com/watch?v=NRb0Irk7OO8" target="_blank" rel="noopener"
&gt;Nidouille en stream&lt;/a&gt; la semaine dernière.&lt;/p&gt;
&lt;p&gt;Grosso modo, j&amp;rsquo;ai popé une VM chez nua.ge en Ubuntu 22.04 avec 2 CPU et 2 Go de RAM, &lt;a class="link" href="https://docs.rudder.io/reference/7.2/installation/quick_install.html" target="_blank" rel="noopener"
&gt;suivi la documentation officielle d&amp;rsquo;installation&lt;/a&gt; et basta.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;sudo apt update
sudo apt upgrade
sudo wget --quiet -O /etc/apt/trusted.gpg.d/rudder_apt_key.gpg &amp;#34;https://repository.rudder.io/apt/rudder_apt_key.gpg&amp;#34;
echo &amp;#34;deb http://repository.rudder.io/apt/7.2/ $(lsb_release -cs) main&amp;#34; | sudo tee /etc/apt/sources.list.d/rudder.list
(output) deb http://repository.rudder.io/apt/7.2/ jammy main
sudo apt update
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;em&gt;Note : j&amp;rsquo;étais assez content de voir &lt;a class="link" href="https://docs.rudder.io/reference/7.2/installation/requirements.html" target="_blank" rel="noopener"
&gt;dans la doc officielle&lt;/a&gt; que pour quelques nodes, 2 CPU / 2 Go de RAM suffisent (surtout qu&amp;rsquo;il y a une JVM, donc la conso RAM aurait pu être bien plus importante).&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;On nous conseille évidemment de vérifier que la clé correspond à celle indiquée dans la documentation officielle, puis on l&amp;rsquo;ajoute (affichée au moment de l&amp;rsquo;apt update) et on installe le package contenant le serveur.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;apt install rudder-server
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Une fois installé, la première chose à faire est de créer un utilisateur admin, puis on peut se connecter sur l&amp;rsquo;IP du serveur en HTTPS/443 (certificat autosigné par contre, faudra que je regarde comment ajouter du let&amp;rsquo;s encrypt).&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;ubuntu@rudder:~$ sudo rudder server create-user -u toto
New password:
Re-type new password:
User &amp;#39;toto&amp;#39; added, restarting the Rudder server
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Côté sécurité réseau, il va falloir ouvrir les ports 443 (obviously) mais aussi le 5309 pour que ça marche bien ( pour la partie &amp;ldquo;Fetch policy&amp;rdquo;, je vous &lt;a class="link" href="https://docs.rudder.io/reference/7.2/installation/requirements.html#configure-the-network" target="_blank" rel="noopener"
&gt;laisse aller lire la doc&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2023/03/rudder_login.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;h2 id="installation-des-agents"&gt;Installation des agents
&lt;/h2&gt;&lt;p&gt;Même topo pour les agents, j&amp;rsquo;ai &lt;a class="link" href="https://docs.rudder.io/reference/7.2/installation/agent/debian.html" target="_blank" rel="noopener"
&gt;suivi la doc&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;On peut donc installer simplement un agent sur nos serveurs en ajoutant la clé + le repo de rudder, puis en lançant la commande&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;sudo wget --quiet -O /etc/apt/trusted.gpg.d/rudder_apt_key.gpg &amp;#34;https://repository.rudder.io/apt/rudder_apt_key.gpg&amp;#34;
echo &amp;#34;deb http://repository.rudder.io/apt/7.2/ $(lsb_release -cs) main&amp;#34; | sudo tee /etc/apt/sources.list.d/rudder.list
sudo apt update &amp;amp;&amp;amp; sudo apt install rudder-agent
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A partir de là, l&amp;rsquo;agent ne sait pas où il doit s&amp;rsquo;enregistrer. On ajoute l&amp;rsquo;agent au serveur Rudder :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;sudo rudder agent policy-server &amp;lt;IP.Du.Rudder.Server&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Petite subtilité, je n&amp;rsquo;ai pas eu besoin de faire cette étape dans un premier temps, car il se trouve que par défaut, les agents cherchent sur le LAN une machine s&amp;rsquo;appelant &amp;ldquo;rudder&amp;rdquo;. J&amp;rsquo;étais donc un poil surpris de ne pas voir cette info dans la doc (il faut que je fasse une PR pour corriger ça).&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2023/03/rudder_tweet.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;On vérifie ensuite que tout fonctionne :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;sudo rudder agent inventory
Rudder agent 7.2.4
Node uuid: 72577f55-46b0-47e2-acd0-2eeb45353122
M| State Technique Component Key Message
E| compliant Common Compute inventory splay Scheduling rudder_run_inventory was correct
E| compliant Inventory Inventory The inventory has been successfully sent
info Rudder agent was run on a subset of policies - not all policies were checked
## Summary #####################################################################
2 components verified in 3 directives
=&amp;gt; 2 components in Enforce mode
-&amp;gt; 2 compliant
Execution time: 10.67s
################################################################################
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;em&gt;Note : On prendra évidemment garde à ne pas ouvrir les ports des serveur/agents Rudder sur Internet et à utiliser des IPs locales et/ou des firewalls pour filtrer proprement tout ça. On pourra aussi utiliser un VPN, mais il ne faudra pas oublier d&amp;rsquo;autoriser les IPs des machines distantes dans Rudder (on en reparlera).&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="accepter-les-machines-dans-notre-inventaire"&gt;Accepter les machines dans notre inventaire
&lt;/h2&gt;&lt;p&gt;Un point qui n&amp;rsquo;était pas super clair pour moi dans la doc de &lt;strong&gt;Quickstart d&amp;rsquo;installation des agents&lt;/strong&gt;, est qu&amp;rsquo;une fois que les agents étaient installés, il faut &lt;strong&gt;les autoriser&lt;/strong&gt; côté serveur. C&amp;rsquo;est logique (on ne veut pas autoriser n&amp;rsquo;importe qui) mais la doc méritera une petite suggestion de ma part je pense.&lt;/p&gt;
&lt;p&gt;Ce point est détaillé dans une autre partie de la documentation &lt;a class="link" href="https://docs.rudder.io/reference/7.2/usage/node_management.html#accept-new-nodes" target="_blank" rel="noopener"
&gt;Node management / Accept new nodes&lt;/a&gt;, mais même cette partie de la documentation ne semble pas à jour, car je n&amp;rsquo;ai pas vu de menu &amp;ldquo;Navigate to Node Management → Accept new Nodes.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;On a en revanche un menu &amp;ldquo;Pending nodes&amp;rdquo; où on retrouve une liste de tous les agents fraichement installés en attente.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2023/03/rudder_pending_nodes.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;h2 id="la-suite-"&gt;La suite ?
&lt;/h2&gt;&lt;p&gt;A partir de là, on a installé tout ce qu&amp;rsquo;on avait besoin d&amp;rsquo;installer pour commencer à jouer avec Rudder.&lt;/p&gt;
&lt;p&gt;Cependant, on est déjà à 8000 signes et entamer la partie configuration dans cet article me parait lourd.&lt;/p&gt;
&lt;p&gt;Je vais arrêter ici pour aujourd&amp;rsquo;hui et publier dans un prochain article (semaine prochaine surement) comment créer des groupes, ce que sont les rules et les directives en un seul et même bloc (ça sera plus cohérent).&lt;/p&gt;
&lt;p&gt;En attendant, have fun !&lt;/p&gt;</description></item><item><title>Ansible : subtilités avec « defined » et « skipped »</title><link>https://blog.zwindler.fr/2022/02/07/ansible-subtilite-defined-skipped/</link><pubDate>Mon, 07 Feb 2022 07:00:00 +0000</pubDate><guid>https://blog.zwindler.fr/2022/02/07/ansible-subtilite-defined-skipped/</guid><description>&lt;img src="https://blog.zwindler.fr/2018/10/ansible_logo.webp" alt="Featured image of post Ansible : subtilités avec « defined » et « skipped »" /&gt;&lt;h2 id="ansible-des-fois-cest-pénible"&gt;Ansible des fois c’est pénible
&lt;/h2&gt;&lt;p&gt;Je vous parle souvent d’Ansible car c’est vraiment un outil qui a changé ma vie d’Ops. Pour autant, des fois c’est up peu pénible à comprendre&amp;hellip;&lt;/p&gt;
&lt;p&gt;Dans cet article je vais vous parler d’un de ces moments où j’ai vraiment pesté contre les Devs (c’est pas la première fois&amp;hellip;). L’erreur initiale était mienne (&lt;em&gt;a priori&lt;/em&gt;) mais les workarounds que j’ai testés me paraissaient légitimes&amp;hellip;&lt;/p&gt;
&lt;p&gt;Et cet article va me permettre de vous illustrer 2 concepts utiles pour vos tâches et variables Ansible qui sont skipped et defined.&lt;/p&gt;
&lt;h2 id="cest-lhistoire-de-deux-tâches"&gt;C’est l’histoire de deux tâches
&lt;/h2&gt;&lt;p&gt;Pour remettre les choses dans le contexte : j’ai certaines actions que j’effectue ou non selon les cas sur un groupe d’hôtes donnés. Et c’était typiquement le cas ici…&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: le code Ansible en lui-même n’est pas impeccable, on peut (et d’ailleurs, on va) faire beaucoup plus propre ; ce n’est pas ce que je veux montrer ici.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Voilà les tâches incriminés :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Get some content from a command&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;command outputting something&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;changed_when&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;register&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;command_output&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;when&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;not_in_test_env&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Write command_output to file&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;{{ command_output_file_path }}&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;{{ command_output.stdout }}&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Le contenu des deux tâches importe peu, car c’est plus des limitations d’Ansible que je vais vous présenter ensuite qui importe. On pourra retrouver cette limitation dans d’autres cas plus pertinents.&lt;/p&gt;
&lt;p&gt;Ce qu’il faut retenir, c’est que :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;la première tâche génère un output texte et enregistre le contenu dans une variable &lt;strong&gt;&lt;em&gt;command_output&lt;/em&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;la seconde tâche copie le contenu de cette variable dans un fichier texte&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Cela pourrait donc être n’importe quelle variable qu’on collecte de n’importe quelle autre façon&amp;hellip; On peut donc légitimement appliquer ce genre d’opération dans d’autres contextes, avec certains nodes où on souhaite exécuter ces deux actions et certains autres, non.&lt;/p&gt;
&lt;h2 id="et-là-cest-le-drame"&gt;Et là, c’est le drame.
&lt;/h2&gt;&lt;p&gt;Lors du moment où j’ai eu mon erreur, &lt;strong&gt;j’étais persuadé d’avoir bien&lt;/strong&gt; &lt;strong&gt;mis un when: not_in_test_env&lt;/strong&gt; pour skip la 2ème tâche aussi si la première l’est. Visiblement ce n’est pas le cas puisque je n’ai pas pu reproduire&amp;hellip;&lt;/p&gt;
&lt;p&gt;Dans le premier cas qui nous intéresse donc, si &lt;em&gt;&lt;strong&gt;not_in_test_env&lt;/strong&gt;&lt;/em&gt; est positionné à &lt;strong&gt;true&lt;/strong&gt;, pas de problème.&lt;/p&gt;
&lt;p&gt;En revanche, si je saute la première partie grâce à la variable &lt;em&gt;&lt;strong&gt;not_in_test_env&lt;/strong&gt;&lt;/em&gt; positionnée à &lt;strong&gt;false&lt;/strong&gt; dans les fichiers de configuration de l’inventaire, vu qu’a priori j’ai fait une erreur et n’ai pas correctement écrit mon when dans la 2ème tâche, la première tâche est bien « Skipped » lors de l’exécution du playbook, mais la tâche suivante « Fail » misérablement.&lt;/p&gt;
&lt;p&gt;Et pour cause&amp;hellip; Ansible essaye à tout prix de résoudre la variable &lt;strong&gt;command_output.stdout&lt;/strong&gt; alors même que la tâche va être « Skipped ». Sur le moment, je n’arrivais pas à comprendre POURQUOI ça n’étais pas skip et j’ai donc essayer de trouver des parades.&lt;/p&gt;
&lt;h2 id="pile-tu-gagnes-"&gt;Pile, tu gagnes, &amp;hellip;
&lt;/h2&gt;&lt;p&gt;Ma première idée a été de tenter de feinter l’erreur en initialisant la variable dans le cas où elle n’est pas renseignée car la tâche est « Skipped ».&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Write command_output to file&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;{{ command_output_file_path }}&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;{{ command_output.stdout | default(&amp;#39;&amp;#39;) }}&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Manque de bol pour moi&amp;hellip; je suis encore sur une vieille version d’Ansible, le &lt;strong&gt;| default( »)&lt;/strong&gt; ne fonctionne pas sur les sous-variables (ici &lt;strong&gt;.stdout&lt;/strong&gt;).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Beginning in version 2.8, attempting to access an attribute of an Undefined value in Jinja will return another Undefined value, rather than throwing an error immediately. This means that you can now simply use a default with a value in a nested data structure (in other words, &lt;code&gt;{{ foo.bar.baz | default('DEFAULT') }}&lt;/code&gt;) when you do not know if the intermediate values are defined.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html" target="_blank" rel="noopener"
&gt;https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;| default&lt;/strong&gt; ou pas, Ansible essaye donc de résoudre &lt;strong&gt;command_output.stdout&lt;/strong&gt; ; la tâche continue donc de « Fail »&amp;hellip;&lt;/p&gt;
&lt;h2 id="-face-je-perd"&gt;&amp;hellip; Face, je perd
&lt;/h2&gt;&lt;p&gt;En passant en mode « debug », j’ai remarqué quelque chose que je ne savais pas. Quand on « Skip » une tâche avec un &lt;strong&gt;register:&lt;/strong&gt;, la variable pas enregistrée (car skip) n’est &lt;strong&gt;pas&lt;/strong&gt; vide. Mon problème est que &lt;strong&gt;command_output.stdout&lt;/strong&gt; n’existe effectivement pas, mais &lt;strong&gt;command_output&lt;/strong&gt; si.&lt;/p&gt;
&lt;p&gt;Plus précisément, Ansible injecte une sous entrée &lt;strong&gt;command_output.skipped&lt;/strong&gt; positionnée à « true ».&lt;/p&gt;
&lt;p&gt;Je me suis donc dit : banco ! Je vais modifier ma condition pour qu’elle empêche l’exécution de la tâche si &lt;strong&gt;command_output.skipped&lt;/strong&gt; existe (car ça veut dire que &lt;strong&gt;command_output.stdout&lt;/strong&gt; n’existe pas).&lt;/p&gt;
&lt;p&gt;Ça donne quelque chose comme ça :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Get some content from a command&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;command outputting something&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;changed_when&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;register&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;command_output&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;when&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;not_in_test_env&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Write command_output to file&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;{{ command_output_file_path }}&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;{{ command_output.stdout }}&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;when&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;not command_output.skipped&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Et là, dans le cas où &lt;strong&gt;not_in_test_env&lt;/strong&gt; est false, ça ne fail plus.&lt;/p&gt;
&lt;p&gt;Victoire ? Non bien sûr&amp;hellip; car &lt;strong&gt;command_output.skipped&lt;/strong&gt; n’existe pas (pas de skipped = false) quand la tâche est exécutée (c’est à dire « pas skipped »). Retour à la case départ : maintenant ça « Fail » dans le cas où on ne « Skip » plus&amp;hellip;&lt;/p&gt;
&lt;h2 id="sur-la-tranche-je-perds-aussi"&gt;Sur la tranche, je perds aussi
&lt;/h2&gt;&lt;p&gt;Dernière idée, on peut se dire que le plus simple / propre, c’est tout simplement de tester le fait qu’une variable (que ce soit &lt;strong&gt;command_output.stdout&lt;/strong&gt; ou &lt;strong&gt;command_output.skipped&lt;/strong&gt;) soit « defined » pour décider si on exécute ou pas la tâche.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Write command_output to file&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;{{ command_output_file_path }}&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;{{ command_output.stdout }}&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;when&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;command_output.stdout is defined&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Mais évidemment, pour que la blague soit complète, ce qu’il faut savoir c’est qu’une solution à base de &lt;strong&gt;command_output.skipped is defined&lt;/strong&gt;, ne peut pas marcher&amp;hellip;&lt;/p&gt;
&lt;p&gt;En fait, il n’est pas possible avec Ansible d’utiliser &lt;strong&gt;is defined&lt;/strong&gt; pour vérifier l’existence (ou non) d’un sous élément.&lt;/p&gt;
&lt;h2 id="solutions"&gt;Solutions
&lt;/h2&gt;&lt;p&gt;La solution la plus simple, que je croyais dur comme fer avoir implémenté (mais mes tests additionnels me prouvent que non) c’est simplement d’ajouter un &lt;strong&gt;when: not_in_test_env&lt;/strong&gt; à la 2ème tâche&amp;hellip;&lt;/p&gt;
&lt;p&gt;Dans la même veine, et qui permet en plus d’éviter d’initialiser pour rien &lt;strong&gt;&lt;em&gt;command_output.skipped&lt;/em&gt;&lt;/strong&gt;, il aurait pu être malin de simplement utiliser un &lt;strong&gt;block:&lt;/strong&gt; avec le when, englobant les deux tâches :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="nt"&gt;block&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Get some content from a command&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;command outputting something&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;changed_when&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;register&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;command_output&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Write command_output to file&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;{{ command_output_file_path }}&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;{{ command_output.stdout }}&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;when&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;not_in_test_env&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Et la vraie solution si jamais vous voulez absolument vérifier si la tâche précédente a été skipped ou pas, est d’utiliser la syntaxe suivante :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Write command_output to file&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;{{ command_output_file_path }}&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;{{ command_output.stdout }}&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;when&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;skipped&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;not in command_output&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Cette syntaxe n’est pas hyper évidente de prime abord, mais c’est la seule qui fonctionne dans Ansible pour faire ça&amp;hellip;&lt;/p&gt;
&lt;h2 id="bonus"&gt;Bonus
&lt;/h2&gt;&lt;p&gt;Un lecteur (&amp;lsquo;Jof) m&amp;rsquo;a fait remarquer qu&amp;rsquo;il y a une autre solution qui marche dans tous les cas :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="l"&gt;when command_output|skipped&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Merci à lui !&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;Le vrai problème ici est quand même que j’avais vraiment besoin de vacances pour ne pas avoir été capable de trouver l’erreur aussi basique ;-).&lt;/p&gt;
&lt;p&gt;La seconde est que j’utilise une version antédiluvienne d’Ansible et que ça irait beaucoup mieux avec des versions plus récentes (pour le &lt;strong&gt;| default&lt;/strong&gt; notamment).&lt;/p&gt;
&lt;p&gt;La dernière chose à retenir est que quand on veut vérifier la présence ou non d’un sous élément dans une variable, le mieux reste d’utiliser &lt;strong&gt;in&lt;/strong&gt; ou &lt;strong&gt;not in&lt;/strong&gt; plutôt que &lt;strong&gt;is defined&lt;/strong&gt; qui ne fonctionne pas dans tous les cas.&lt;/p&gt;
&lt;h2 id="sources-additionnelles"&gt;Sources additionnelles
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://stackoverflow.com/questions/56066503/i-am-having-ansible-issues-with-register-command-when-using-when-in-tasks" target="_blank" rel="noopener"
&gt;stackoverflow.com/questions/56066503/i-am-having-ansible-issues-with-register-command-when-using-when-in-tasks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="github.com/ansible/ansible/issues/17500#issuecomment-246370020" &gt;github.com/ansible/ansible/issues/17500#issuecomment-246370020&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="github.com/ansible/ansible/issues/4297#issuecomment-356427588" &gt;github.com/ansible/ansible/issues/4297#issuecomment-356427588&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Ma plateforme de travail collaboratif Nextcloud en 5 minutes</title><link>https://blog.zwindler.fr/2020/04/27/ma-plateforme-de-travail-collaboratif-nextcloud-en-5-minutes/</link><pubDate>Mon, 27 Apr 2020 06:45:00 +0000</pubDate><guid>https://blog.zwindler.fr/2020/04/27/ma-plateforme-de-travail-collaboratif-nextcloud-en-5-minutes/</guid><description>&lt;img src="https://blog.zwindler.fr/2020/04/Nextcloud_Logo.svg_.webp" alt="Featured image of post Ma plateforme de travail collaboratif Nextcloud en 5 minutes" /&gt;&lt;h2 id="cest-quoi-nextcloud-"&gt;C’est quoi Nextcloud ?
&lt;/h2&gt;&lt;p&gt;Par où commencer ? Nextcloud, c’est tellement de choses ;-)&lt;/p&gt;
&lt;p&gt;Historiquement, &lt;a class="link" href="https://nextcloud.com/" target="_blank" rel="noopener"
&gt;Nextcloud&lt;/a&gt; est un fork du projet &lt;a class="link" href="https://owncloud.org/" target="_blank" rel="noopener"
&gt;Owncloud&lt;/a&gt;, qui visait à fournir un service en ligne de stockage de fichier via une interface web. Un peu comme Dropbox, mais hébergé par vous, chez vous, et donc respectueux de votre vie privée !&lt;/p&gt;
&lt;p&gt;Mais aujourd’hui, Nextcloud c’est bien plus que ça. C’est une véritable plateforme de travail collaboratif avec certes un service de gestion, de partage et de synchronisation de fichiers entre plusieurs devices/utilisateurs, mais aussi un serveur libreoffice/collabora de l’édition de fichiers collaboratifs (Office 365/Google Docs), un serveur Talk (visioconférence), calendrier, gestion de notes et bien plus encore.&lt;/p&gt;
&lt;p&gt;La solution s’est même nettement étoffée depuis la version 18 qui vient de sortir, avec de très nombreuses extensions.&lt;/p&gt;
&lt;h2 id="pourquoi-attendre-si-longtemps-pour-en-parler-"&gt;Pourquoi attendre si longtemps pour en parler ?
&lt;/h2&gt;&lt;p&gt;La première fois que j’ai testé Owncloud, c’était en 2014 lorsque j’ai créé (sans succès) une entreprise d’infogérence spécialisée dans les outils open source. Le but était de fournir, entre autre, un service autohébergé pour justement remplacer Dropbox. Cette annecdote fera peut être sourire certains de mes lecteurs, très impliqués dans Nextcloud :-p.&lt;/p&gt;
&lt;p&gt;A l’époque, je n’avais pas du tout aimé Owncloud, que j’avais trouvé moyen en terme d’ergonomie, très lent, etc.&lt;/p&gt;
&lt;p&gt;Récemment cependant, Nextcloud a gagné beaucoup de traction et c’est tant mieux, car ça m’a forcé à rester l’outil, qui a vraiment évolué pour le mieux.&lt;/p&gt;
&lt;h2 id="et-comment-on-va-faire-pour-le-déployer-si-vite-"&gt;Et comment on va faire pour le déployer si vite ?
&lt;/h2&gt;&lt;p&gt;Ahah ! En voilà une bonne question&amp;hellip; Grosse surprise, on va déployer tout ça avec un playbook, bien sûr !&lt;/p&gt;
&lt;p&gt;Pour ceux qui ne savent pas, à chaque fois que j’installe un soft, j’automatise ça avec ansible, car j’automatise TOUT avec ansible.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2020/04/automate_all_the_things.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Je vous met à disposition sur &lt;a class="link" href="https://github.com/zwindler/ansible-nextcloud" target="_blank" rel="noopener"
&gt;Github&lt;/a&gt; cet ensemble de playbooks qui vous permettront d’installer un serveur Nextcloud de A à Z en partant d’un serveur Ubuntu 18.04 sur lequel vous avez un accès SSH.&lt;/p&gt;
&lt;p&gt;Et pour ceux qui n’ont pas de serveur à disposition, je vous ai également mis un playbook permettant de déployer une machine sur le cloud provider français Scaleway.&lt;/p&gt;
&lt;p&gt;Pourquoi Scaleway ? Pas parce que j’ai le moindre partenariat avec eux (pas pour l’instant en tout cas), mais parce :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ils sont français&lt;/li&gt;
&lt;li&gt;ils ont une super API et des modules Ansible qui marchent très très bien (j’ai même fais un live coding avec) avec l’inventaire ansible dynamique (ce que d’autres n’ont pas forcément)&lt;/li&gt;
&lt;li&gt;ils proposent des instances à moins de 4€ par mois, payable à l’heure, ce qui est parfait pour des petits tests&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Mais n’importe quelle autre machine sous Ubuntu 18.04 fera l’affaire !&lt;/p&gt;
&lt;h2 id="créer-une-vm-sur-scaleway"&gt;Créer une VM sur Scaleway
&lt;/h2&gt;&lt;p&gt;Je ne détaillerai pas l’instanciation de la VM sur Scaleway si vous choisissez cette option, car tout est expliqué en détail sur le &lt;a class="link" href="https://github.com/zwindler/ansible-nextcloud/blob/master/README.md" target="_blank" rel="noopener"
&gt;README.md du dépôt Github&lt;/a&gt;. J’ai également abondamment parlé de &lt;a class="link" href="https://blog.zwindler.fr/recherche/?keyword=scaleway" &gt;ce sujet à l’occasion d’autres articles&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2020/04/scaleway_vm.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;h2 id="préparer-linstallation-de-nextcloud"&gt;Préparer l’installation de Nextcloud
&lt;/h2&gt;&lt;p&gt;Avant de pouvoir installer Nextcloud sur notre nouveau serveur Ubuntu 18.04 tout neuf, il est important de choisir un nom de domaine pour notre futur service Nextcloud. Ajoutez un CNAME permettant de mapper ce nom de domaine sur l’adresse IP de votre machine virtuelle Ubuntu.&lt;/p&gt;
&lt;p&gt;Maintenant que le serveur Ubuntu 18.04 est disponible et que le futur service est résolvable, on dispose de 2 méthodes pour installer Nextcloud. Soit on lance le playbook ansible en local sur la machine Nextcloud, soit on l’installe à distance via ansible.&lt;/p&gt;
&lt;h3 id="si-on-le-lance-en-local"&gt;Si on le lance en local
&lt;/h3&gt;&lt;p&gt;Récupérer le repository &lt;a class="link" href="https://github.com/zwindler/ansible-nextcloud" target="_blank" rel="noopener"
&gt;zwindler/ansible-nextcloud&lt;/a&gt; directement sur le serveur via un &lt;code&gt;git clone&lt;/code&gt;. Nous utiliserons le paramètre &amp;ldquo;-i hosts_local&amp;rdquo; quand on exécutera &lt;code&gt;ansible-playbook&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="si-on-a-généré-une-vm-avec-scaleway"&gt;Si on a généré une VM avec Scaleway
&lt;/h3&gt;&lt;p&gt;Dans ce cas, on profitera de la fonctionnalité d’inventaire dynamique fournie par Scaleway. Nous utiliserons le paramètre &amp;ldquo;-i dynamic_inventory.yml&amp;rdquo; quand on exécutera &lt;code&gt;ansible-playbook&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="si-vous-voulez-installer-à-distance"&gt;Si vous voulez installer à distance
&lt;/h3&gt;&lt;p&gt;Il sera nécessaire de pouvoir se connecter en SSH à la machine distante mais aussi de générer un inventaire pour qu’ansible sache sur quel machine il doit se connecter. Pour le faire on créera un fichier texte avec l’IP du serveur distant, et nous utiliserons &amp;ldquo;-i hosts_distant&amp;rdquo; quand on exécutera &lt;code&gt;ansible-playbook&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;echo &amp;#34;IP_of_the_server&amp;#34; &amp;gt; hosts_distant
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="installer-nextcloud"&gt;Installer Nextcloud
&lt;/h2&gt;&lt;p&gt;En partant du principe que vous avez utilisé la méthode Scaleway avec l’inventaire dynamique, voilà ce que vous devrez faire :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;ansible-playbook -i dynamic_inventory.yml -u root nextcloud_install.yml
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Le playbook vous promptera pour renseigner un certain nombre de variables qui correspondent à votre installation :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;MariaDB root password: awesome_mariadb_password
Nextcloud MariaDB password: awesome_mariadb_user_password
Your domain: example.org
Nextcloud HTTPS port [8443]: 443
Nextcloud instance name (URL will be https://thisvalue.example.org) [nextcloud]: nextcloudscw
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2020/04/nextcloud_ansible_prompt.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;A l’issue du playbook, vous devriez pouvoir vous connecter sur votre instance pour finaliser l’installation. Lorsque vous vous connecterez pour la première fois, vous tomberez sur un écran qui vous demandera une partie des informations rentrées préalablement&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2020/04/nextcloudscw.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;h2 id="et-la-sécurité-"&gt;Et la sécurité ?
&lt;/h2&gt;&lt;p&gt;Et oui, la sécurité pour un outil aussi sensible que vos documents, c’est important.&lt;/p&gt;
&lt;p&gt;Heureusement, Nextcloud est un outil bien packagé et un projet pour lequel la sécurité est au cœur du développement.&lt;/p&gt;
&lt;p&gt;Nextcloud met à disposition un scan de sécurité (&lt;a class="link" href="https://scan.nextcloud.com/" target="_blank" rel="noopener"
&gt;scan.nextcloud.com&lt;/a&gt;) qui vous permettra de vous tester contre un certain nombre d’attaques connues.&lt;/p&gt;
&lt;p&gt;Comme vous pouvez le voir, ça se passe plutôt bien ;-)&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2020/04/nextcloudscw_scan.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;De même, la configuration nginx (le frontal de notre serveur) coté TLS est elle aussi sécurisée. Par défaut, je désactive TLS 1.0 et 1.1, ce qui peut poser des soucis pour les plus vieux appareil mais permet d’obtenir un joli A+ chez SSL Labs&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2020/04/nextcloudscw_ssllabs_a.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Dans le cas où vous souhaiteriez les réactiver, c’est possible (il y a un flag dans le playbook) mais dans ce cas là la note sera rétrogradée à B.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2020/04/nextcloudscw_ssllabs.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Et c’est fini ! Have fun !&lt;/p&gt;</description></item><item><title>Déployer en 5 minutes un cluster Kubernetes sur ARM avec k3s et Ansible</title><link>https://blog.zwindler.fr/2019/03/21/deployer-en-5-minutes-un-cluster-kubernetes-sur-arm-avec-k3s-et-ansible/</link><pubDate>Thu, 21 Mar 2019 13:00:14 +0000</pubDate><guid>https://blog.zwindler.fr/2019/03/21/deployer-en-5-minutes-un-cluster-kubernetes-sur-arm-avec-k3s-et-ansible/</guid><description>&lt;img src="https://blog.zwindler.fr/2019/03/k3s_scaleway.webp" alt="Featured image of post Déployer en 5 minutes un cluster Kubernetes sur ARM avec k3s et Ansible" /&gt;&lt;h2 id="cest-quoi-k3s-"&gt;C’est quoi k3s ?
&lt;/h2&gt;&lt;p&gt;Il y a quelques jours, vous avez peut être vu passer dans vos fil d’actus &lt;strong&gt;k3s&lt;/strong&gt;, ce nouveau projet &lt;a class="link" href="https://k3s.io/" target="_blank" rel="noopener"
&gt;open sourcé par Rancher&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Lightweight Kubernetes. 5 less than k8s.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Il s’agit d’une version réduite de Kubernetes, sans pour autant être minimaliste, qui nous vante la possibilité de monter des clusters Kubernetes avec très peu de ressources nécessaires. On parle de moins de 512 Mo de RAM pour un master, encore moins pour un worker, tout dans un binaire de 40 Mo, support de &lt;strong&gt;armhf&lt;/strong&gt;, et &lt;strong&gt;arm64&lt;/strong&gt;, &amp;hellip;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Great for Edge, IoT, CI, ARM, and situations where a PhD in k8s clusterology is infeasible&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Cerise sur le gâteau, comme tout est regroupé dans un seul et même binaire, l’installation est ultra simple et se résume en :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Récupérer un binaire&lt;/li&gt;
&lt;li&gt;Lancer le binaire sur le master&lt;/li&gt;
&lt;li&gt;Lancer le binaire sur le worker avec l’URL du master et un token&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="mais-cest-génial-"&gt;Mais c’est génial !
&lt;/h2&gt;&lt;p&gt;Nécessairement, pour arriver à ça, il a fallut faire quelques concessions mais pour l’instant je ne les trouves pas très gênantes. Parmi les modifications notables, on retrouve :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Suppression des features alpha, legacy et non standard&lt;/li&gt;
&lt;li&gt;Suppression de tous les add-on des cloud providers&lt;/li&gt;
&lt;li&gt;Remplacement de etcd3 par sqlite3 (même si etcd3 peut être toujours être utilisé)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="jen-veux-un-"&gt;J’en veux un !
&lt;/h2&gt;&lt;p&gt;Autant dire que pas mal de bidouilleurs du dimanche se sont jetés dessus.&lt;/p&gt;
&lt;p&gt;Le premier exemple qui vient à l’esprit est de monter un cluster Kubernetes sur Raspberry pi. Nombre de personnes ont déjà installé Docker sur un raspberry qui traînait dans un tiroir (moi compris), et le nombre d’articles avec Docker Swarm sur plusieurs Raspberry pullule sur le web.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.zwindler.fr/2016/09/05/geekerie-du-week-end-installer-docker-sur-un-raspberry-pi/" &gt;Geekerie du week end : installer Docker sur un Raspberry Pi&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Tout ça c’est très bien mais jusqu’à présent, il était difficile d’installer Kubernetes sur ce genre de machines, assez limités en CPU/RAM. Du coup, vous vous en doutez, je ne suis pas le premier à parler de ça.&lt;/p&gt;
&lt;p&gt;Vincent RABAT aka &lt;strong&gt;itwars&lt;/strong&gt; (que vous connaissez sûrement si vous écumez les meetups sur Bordeaux) m’a coiffé au poteau en &lt;a class="link" href="https://github.com/itwars/k3s-ansible" target="_blank" rel="noopener"
&gt;releasant un playbook Ansible pour installer k3s&lt;/a&gt;, et notamment sur un Raspberry Pi (mais pas que).&lt;/p&gt;
&lt;p&gt;A charge de revanche, Vincent ;-).&lt;/p&gt;
&lt;h2 id="un-peu-différent"&gt;Un peu différent
&lt;/h2&gt;&lt;p&gt;Du coup, pour me démarquer, je vous propose aujourd’hui quelque chose d’un peu différent. N’ayant pas suffisament de raspberry sous la main, j’ai voulu faire un PoC de k3s en me basant sur des machines ARM créées chez un cloud provider.&lt;/p&gt;
&lt;p&gt;L’idée étant que si ça marche sur des machines de faible puissance en ARM, ça marchera partout (x64, raspberry, etc).&lt;/p&gt;
&lt;p&gt;Ça fait plusieurs fois que j’utilise &lt;a class="link" href="https://www.scaleway.com/" target="_blank" rel="noopener"
&gt;Scaleway&lt;/a&gt; comme hébergeur pour des petits projets, et en particulier pour déployer rapidement des machines car leur API est pas trop mal fichues et surtout ils disposent de modules Ansible très bien fait, notamment avec la feature « inventaire dynamique », ce que beaucoup ne font pas.&lt;/p&gt;
&lt;p&gt;Pour ceux que ça intéresse, mon talk sur BDX.IO était basé sur le même principe :&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://www.youtube.com/watch?v=WPRE1_f0pyg" target="_blank" rel="noopener"
&gt;https://www.youtube.com/watch?v=WPRE1_f0pyg&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Dans ce tuto, je vais donc vous montrer comment en quelque commande, monter un cluster k3s sur des machines ARM créées à la volée chez Scaleway, le tout avec Ansible (vous l’aurez compris).&lt;/p&gt;
&lt;h2 id="quelques-prérequis"&gt;Quelques prérequis
&lt;/h2&gt;&lt;p&gt;La première chose à faire est de cloner sur votre machine les playbooks Ansible que j’ai mis à disposition sur Github à l’adresse suivante : &lt;a class="link" href="https://github.com/zwindler/ansible-scaleway-k3s" target="_blank" rel="noopener"
&gt;github.com/zwindler/ansible-scaleway-k3s&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Pour réaliser ce tuto, je pars du principe que vous avec déjà un compte sur Scaleway, que vous avez un clé SSH qu’on déposera à la racine du projet, appelée &lt;strong&gt;admin.pub&lt;/strong&gt; (c’est original).&lt;/p&gt;
&lt;p&gt;Il faudra également installer les package &lt;strong&gt;pipy&lt;/strong&gt; suivant sur la machine locale :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;pip install jinja2 PyYAML paramiko cryptography packaging
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Ensuite, vous devrez installer Ansible depuis les sources (&amp;gt;= 2.8devel) et éventuellement le binaire &lt;strong&gt;jq&lt;/strong&gt; pour requêter dans les output JSON (ça c’est juste pour se faciliter la vie)&lt;/p&gt;
&lt;p&gt;Dans la console Scaleway, vous devrez créer un token sur le site de Scaleway pour les accès distants et le stocker dans un fichier &lt;strong&gt;scaleway_token&lt;/strong&gt;&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;export SCW_API_KEY=&amp;#39;aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Et enfin sourcer le fichier pour avoir la variable dans votre environnement&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;source scaleway_token
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="ok-on-est-prêt"&gt;OK, on est prêt
&lt;/h2&gt;&lt;p&gt;La première étape de cette procédure va être de générer des instances ARM pour héberger le master et le worker Kubernetes. Pour ça, le playbook Ansible &lt;strong&gt;create_arm_vms.yaml&lt;/strong&gt; va :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;récupérer l’ID d’organisation du compte Scaleway&lt;/li&gt;
&lt;li&gt;récupérer un ID d’image compatible debian Stretch&lt;/li&gt;
&lt;li&gt;ajouter si nécessaire la clé SSH de l’admin&lt;/li&gt;
&lt;li&gt;créer autant de machines que nécessaire&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On lance la commande suivante :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;ansible-playbook create_arm_vms_scaleway.yml
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;ET C’EST TOUT !&lt;/p&gt;
&lt;p&gt;Normalement en 2 minutes, vous devriez avoir 2 instances ARM sur le datacenter de Paris (par1).&lt;/p&gt;
&lt;p&gt;A noter, il se peut qu’elles ne soient pas accessibles tout de suite en SSH. J’adapterai peut être le playbook pour qu’il ne rende pas la main tant que les machines ne sont pas accessibles, et qu’on enchaîne automatiquement sur l’étape suivante (TODO).&lt;/p&gt;
&lt;h2 id="inventaire-automatique"&gt;Inventaire automatique
&lt;/h2&gt;&lt;p&gt;Je le disais plus haut, la grande force de Scaleway avec Ansible est le fait qu’ils ont fait l’effort de coder &lt;strong&gt;l’inventaire dynamique&lt;/strong&gt;. Vous n’avez pas besoin de renseigner à la main les IPs des instances que vous venez de créer dans un fichier ansible/hosts. Grâce à l’API, la découverte se fait automatiquement. On va dont pouvoir enchaîner sur la suite directement.&lt;/p&gt;
&lt;p&gt;De base, voici le contenu de mon fichier d’inventaire (inventory.yml) :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;plugin: scaleway
regions:
- par1
tags:
- k3smaster
- k3sworker
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Maintenant que les machines sont UP, on peut vérifier ce que nous renvoie Scaleway avec la commande &lt;strong&gt;ansible-inventory&lt;/strong&gt;. Ça devrait ressembler à ça :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;ansible-inventory --list -i inventory.yml
{
&amp;#34;_meta&amp;#34;: {
&amp;#34;hostvars&amp;#34;: {
&amp;#34;x.x.x.x&amp;#34;: {
&amp;#34;arch&amp;#34;: &amp;#34;arm64&amp;#34;,
&amp;#34;commercial_type&amp;#34;: &amp;#34;ARM64-2GB&amp;#34;,
&amp;#34;hostname&amp;#34;: &amp;#34;k3smaster1&amp;#34;,
[...]
&amp;#34;k3sworker&amp;#34;: {
&amp;#34;hosts&amp;#34;: [
&amp;#34;x.x.x.x&amp;#34;,
&amp;#34;y.y.y.y&amp;#34;,
&amp;#34;z.z.z.z&amp;#34;
]
}
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="ssh-fingerprints"&gt;SSH fingerprints
&lt;/h2&gt;&lt;p&gt;Une étape qui est souvent fastidieuse, surtout quand on ajoute beaucoup de serveur, est l’étape de vérification de l&amp;rsquo;empreinte SSH des nouveaux serveurs lors de la première connexion. Cette authentification est très importante et il &lt;b&gt;n’est pas du tout conseillé&lt;/b&gt; (comme je le vois parfois) d’ajouter l’option &lt;strong&gt;ANSIBLE_HOST_KEY_CHECKING=False&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Ici, je vous propose de scanner automatiquement &lt;em&gt;la première fois&lt;/em&gt; l&amp;rsquo;empreinte, et l’ajouter dans votre known_hosts. Ainsi, si l&amp;rsquo;empreinte change en cours de route, vous serez prévenus. Cependant, ce n’est à utiliser que dans le cas de notre bidouille, pas en production.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;ansible-inventory --list -i inventory.yml | jq -r &amp;#39;.k3smaster.hosts | .[]&amp;#39; | xargs ssh-keyscan &amp;gt;&amp;gt; ~/.ssh/known_hosts
ansible-inventory --list -i inventory.yml | jq -r &amp;#39;.k3sworker.hosts | .[]&amp;#39; | xargs ssh-keyscan &amp;gt;&amp;gt; ~/.ssh/known_hosts
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Vous pouvez maintenant ajouter votre clé SSH dans le ssh-agent, et vérifier qu’on vous pouvez vous connecter à tous les serveurs via Ansible :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;eval `ssh-agent`
ssh-add myprivate.key
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="préparation-du-serveur"&gt;Préparation du serveur
&lt;/h2&gt;&lt;p&gt;C’était facile, non ?&lt;/p&gt;
&lt;p&gt;Et bien la suite l’est encore plus, à l’exception de cette petite subtilité/piège =&amp;gt; Il se trouve que l’image ARM par défaut proposée par Scaleway ne dispose ni de python ni de sudo. Pour faire du Ansible, c’est très très handicapant.&lt;/p&gt;
&lt;p&gt;J’ai donc écris un petit playbook, à n’exécuter la première fois, qui installe les prérequis non présents sur l’image de base (&lt;strong&gt;python&lt;/strong&gt; et &lt;strong&gt;sudo&lt;/strong&gt;)&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;ansible-playbook -i inventory.yml -u root install_prerequisites_scaleway.yml
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Si vous n’êtes pas sur ce genre de machines, vous n’aurez pas à faire cette étapes.&lt;/p&gt;
&lt;p&gt;Maintenant qu’on a des machines avec python et sudo, on peut installer k3s normalement avec Ansible :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;ansible-playbook -i inventory.yml -u root install_k3s_scaleway.yml
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Une fois de plus : ET C’EST TOUT !&lt;/p&gt;
&lt;p&gt;Le cluster est maintenant opérationnel. Les serveurs ont Kubernetes installé et fonctionnel. Le ou les workers ont rejoint le master et font parti d’un même cluster. Un Ingress controller Treafik est créé, on peut jouer dessus directement :-)&lt;/p&gt;
&lt;h2 id="bonus--accéder-au-cluster"&gt;Bonus : accéder au cluster
&lt;/h2&gt;&lt;p&gt;Bon je vous ai un peu feinté.&lt;/p&gt;
&lt;p&gt;Certes, on peut se connecter en SSH sur le master et le piloter avec les commandes &lt;strong&gt;kubectl&lt;/strong&gt; classique (mais il faut rajouter k3s devant car le binaire kubectl est intégré à k3s). Mais c’est quand même plus simple si on peut y accéder à distance, depuis votre machine locale.&lt;/p&gt;
&lt;p&gt;Là encore, j’ai donc fais un petit playbook qui va aspirer la configuration &lt;strong&gt;kubectl&lt;/strong&gt; et la coller dans le fichier &lt;strong&gt;~/.kube/config.k3smaster&lt;/strong&gt;&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;ansible-playbook -i inventory.yml -u root configure_kubeconfig.yml
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A partir de là, vous devriez pouvoir tester votre nouveau cluster depuis votre PC :-)&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;cd
kubectl --kubeconfig=.kube/config.k3smaster get nodes
NAME STATUS ROLES AGE VERSION
k3smaster1 Ready &amp;lt;none&amp;gt; 2d v1.13.3-k3s.6
k3sworker1 Ready &amp;lt;none&amp;gt; 2d v1.13.3-k3s.6
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Enjoy !!!&lt;/p&gt;</description></item><item><title>Migration mongoDB à chaud depuis une 3.4 vers une 4.0 (replicaSet)</title><link>https://blog.zwindler.fr/2019/02/05/migration-mongodb-a-chaud-depuis-une-3-4-vers-une-4-0-replicaset/</link><pubDate>Tue, 05 Feb 2019 11:45:02 +0000</pubDate><guid>https://blog.zwindler.fr/2019/02/05/migration-mongodb-a-chaud-depuis-une-3-4-vers-une-4-0-replicaset/</guid><description>&lt;img src="https://blog.zwindler.fr/2019/01/mongo_upgrade-1.webp" alt="Featured image of post Migration mongoDB à chaud depuis une 3.4 vers une 4.0 (replicaSet)" /&gt;&lt;h2 id="mongodb"&gt;MongoDB
&lt;/h2&gt;&lt;p&gt;Depuis peu, j’administre des bases de données MongoDB !&lt;/p&gt;
&lt;p&gt;Pour le fun (et ceux qui ne connaissent pas), la définition qu’en donne &lt;a class="link" href="https://fr.wikipedia.org/wiki/MongoDB" target="_blank" rel="noopener"
&gt;Wikipedia&lt;/a&gt; :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;MongoDB&lt;/strong&gt; (de l’anglais &lt;em&gt;&lt;span class="lang-en" lang="en"&gt;&lt;a class="link" href="https://fr.wiktionary.org/wiki/humongous" target="_blank" rel="noopener"
&gt;humongous&lt;/a&gt;&lt;/em&gt; qui peut être traduit par « énorme ») est un &lt;a class="link" href="https://fr.wikipedia.org/wiki/Syst%C3%A8me_de_gestion_de_base_de_donn%C3%A9es" title="Système de gestion de base de données"
target="_blank" rel="noopener"
&gt;système de gestion de base de données&lt;/a&gt; &lt;a class="link" href="https://fr.wikipedia.org/wiki/Base_de_donn%C3%A9es_orient%C3%A9e_documents" title="Base de données orientée documents"
target="_blank" rel="noopener"
&gt;orientée documents&lt;/a&gt;, &lt;a class="link" href="https://fr.wikipedia.org/wiki/Scalability" title="Scalability"
target="_blank" rel="noopener"
&gt;répartissable sur un nombre quelconque d’ordinateurs&lt;/a&gt; et ne nécessitant pas de schéma prédéfini des données. Il est écrit en &lt;a class="link" href="https://fr.wikipedia.org/wiki/C%2B%2B" title="C&amp;#43;&amp;#43;"
target="_blank" rel="noopener"
&gt;C++&lt;/a&gt;. Le serveur et les outils sont distribués sous &lt;a class="link" href="https://fr.wikipedia.org/w/index.php?title=Server_Side_Public_License_%28SSPL%29&amp;amp;action=edit&amp;amp;redlink=1" title="Server Side Public License (SSPL) (page inexistante)"
target="_blank" rel="noopener"
&gt;licence AGPL&lt;/a&gt;{.new}, les pilotes sous &lt;a class="link" href="https://fr.wikipedia.org/wiki/Licence_Apache" title="Licence Apache"
target="_blank" rel="noopener"
&gt;licence Apache&lt;/a&gt; et la documentation sous &lt;a class="link" href="https://fr.wikipedia.org/wiki/Licence_Creative_Commons" title="Licence Creative Commons"
target="_blank" rel="noopener"
&gt;licence Creative Commons&lt;/a&gt;&lt;sup id="cite_ref-licensing_2-1" class="reference"&gt;&lt;/sup&gt;. Il fait partie de la mouvance &lt;a class="link" href="https://fr.wikipedia.org/wiki/NoSQL" title="NoSQL"
target="_blank" rel="noopener"
&gt;NoSQL&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Comme dans la vie de tout projets, on installe un logiciel, et sur le moment ça nous suffit, on est contents.&lt;/p&gt;
&lt;p&gt;Mais arrive le jour fatidique où le logiciel est obsolète et il faut mettre à jour la base. Sans aucun créneau de maintenance pour le faire bien entendu car elles sont évidemment utilisées en production ;-).&lt;/p&gt;
&lt;h2 id="prérequis"&gt;Prérequis
&lt;/h2&gt;&lt;p&gt;Bon point pour moi, je n’ai pas fais l’erreur d’avoir une base de données mongoDB « standalone ». Je dispose de cluster de bases de données MongoDB (replicaSet, dans la terminologie mongo), que je vais donc pouvoir mettre à jour à chaud. Ça sera juste un peu plus long ;-).&lt;/p&gt;
&lt;h2 id="34--40--pas-possible"&gt;3.4 =&amp;gt; 4.0 = pas possible
&lt;/h2&gt;&lt;p&gt;Bon quand même, fallait bien que je tombe dans le piège.&lt;/p&gt;
&lt;p&gt;La version majeure 3.4 (la dernière en date est la .19) ne peut pas directement être mise à jour vers une 4.0. Comme c’est une majeure, il faut passer d’abord par la majeure suivante, la 3.6 (c’est classique comme limitation).&lt;/p&gt;
&lt;h2 id="on-passe-en-36-alors"&gt;On passe en 3.6 alors
&lt;/h2&gt;&lt;p&gt;Pour me simplifier la vie, j’ai un mini playbook Ansible qui me permet d’ajouter les dépôts en fonction de la version que je veux.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;- name: Prepare mongodb upgrade
hosts: all
vars:
#mongo_repo : &amp;#34;deb [ arch=amd64,arm64 ] http://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/3.4 multiverse&amp;#34;
mongo_repo: &amp;#34;deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/3.6 multiverse&amp;#34;
#mongo_repo: &amp;#34;deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/4.0 multiverse&amp;#34;
tasks:
- name: &amp;#34;Add mongodb repository key&amp;#34;
apt_key:
keyserver: keyserver.ubuntu.com
id: &amp;#34;{{item}}&amp;#34;
state: present
loop:
- 0C49F3730359A14518585931BC711F9BA15703C6
- 2930ADAE8CAF5059EE73BB4B58712A2291FA4AD5
- 9DA31620334BD75D9DCB49F368818C72E52529D4
- name: &amp;#34;Add mongodb repository&amp;#34;
apt_repository:
repo: &amp;#34;{{ mongo_repo }}&amp;#34;
state: present
update_cache: yes
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Clairement, on peut faire plus propre. Ici, c’est du oneshot sur quelques machines Ubuntu, donc je n’ai pas automatisé plus que ça. Mais si vous avez plus de machines à faire que moi, je vous invite à le raffiner un peu, par exemple en ajoutant le repo et la clé en fonction de la version donnée en paramètre.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;ansible-playbook -i inventory/prod/mongo-prod prepare_mongo_upgrade.yml -b
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="mise-à-jour-des-binaires-vers-la-36"&gt;Mise à jour des binaires vers la 3.6
&lt;/h2&gt;&lt;p&gt;Maintenant qu’on a bien les dépôts pour la version 3.6, on peut mettre à jour nos serveurs. Idéalement si on avait de nombreux clusters à migrer, il faudrait le faire avec un playbook Ansible (ou autre) qui passe sur les serveurs, uns par uns, les mets à jour et les redémarre.&lt;/p&gt;
&lt;p&gt;Cependant, pour simplifier l’explication, je vais laisser la procédure telle quelle et tout faire à la main.&lt;/p&gt;
&lt;p&gt;Sur les SECONDARY, &lt;strong&gt;uns par uns&lt;/strong&gt;, mettre à jour manuellement le serveur, puis le redémarrer :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;apt-get update
apt-get upgrade mongodb-org-server mongodb-org-shell
[...]
Unpacking mongodb-org-server (3.6.10) over (3.4.19) ...
[...]
systemctl restart mongod
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Vérifier que tout fonctionne à nouveau après redémarrage&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;mongo
MongoDB shell version v3.6.10
connecting to: mongodb://127.0.0.1:27017/?gssapiServiceName=mongodb
Implicit session: session { &amp;#34;id&amp;#34; : UUID(&amp;#34;17272d4e-ecd0-49c2-a672-8804227ab0e4&amp;#34;) }
MongoDB server version: 3.6.10
rs:SECONDARY&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Puis, se connecter sur le PRIMARY, et lui passer la commande suivante (qui va avoir pour effet de le rétrograder proprement et temporairement en tant que SECONDARY) :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$&amp;gt; mongo
rs:PRIMARY&amp;gt; use admin
switched to db admin
rs:PRIMARY&amp;gt; db.auth(&amp;#39;myadmin&amp;#39;, &amp;#39;myawesomepassword&amp;#39;)
1
rs:PRIMARY&amp;gt; rs.stepDown(180)
[...]
2019-01-25T15:23:02.520+0000 I NETWORK [thread1] trying reconnect to 127.0.0.1:27017 (127.0.0.1) failed
2019-01-25T15:23:02.548+0000 I NETWORK [thread1] reconnect 127.0.0.1:27017 (127.0.0.1) ok
rs:SECONDARY&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Et enfin, mettre à jour manuellement le serveur de la même manière que les autres :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;apt-get update
apt-get upgrade mongodb-org-server mongodb-org-shell
systemctl restart mongod
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="compatibilité-36"&gt;Compatibilité 3.6
&lt;/h2&gt;&lt;p&gt;Ici, je viens de faire la migration de 3.4 vers 3.6. En réalité, ce n’est pas complètement terminé. L’ensemble des binaires ont beau être en version 3.6, la base elle, reste en « compatibilité » 3.4.&lt;/p&gt;
&lt;p&gt;Pour s’en assurer, une connexion sur n’importe quel serveur permet de le vérifier :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$&amp;gt; mongo
rs:SECONDARY&amp;gt; db.adminCommand( { getParameter: 1, featureCompatibilityVersion: 1 } )
{ &amp;#34;featureCompatibilityVersion&amp;#34; : { &amp;#34;version&amp;#34; : &amp;#34;3.4&amp;#34; }, &amp;#34;ok&amp;#34; : 1 }
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;On va donc aller sur le nœud PRIMARY pour passer la base en compatibilité 3.6 (le seul prérequis est qu’une majorité de serveurs aient leurs binaires mis à jour en 3.6 et redémarrés) :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$&amp;gt; mongo
MongoDB shell version v3.6.10
connecting to: mongodb://127.0.0.1:27017/?gssapiServiceName=mongodb
Implicit session: session { &amp;#34;id&amp;#34; : UUID(&amp;#34;aaa-aaa-aaa-aaa-aaa&amp;#34;) }
MongoDB server version: 3.6.10
rs:PRIMARY&amp;gt; db.adminCommand( { getParameter: 1, featureCompatibilityVersion: 1 } )
{ &amp;#34;featureCompatibilityVersion&amp;#34; : { &amp;#34;version&amp;#34; : &amp;#34;3.4&amp;#34; }, &amp;#34;ok&amp;#34; : 1 }
rs:PRIMARY&amp;gt; use admin
switched to db admin
rs:PRIMARY&amp;gt; db.auth(&amp;#39;myadmin&amp;#39;, &amp;#39;myawesomepassword&amp;#39;)
1
rs:PRIMARY&amp;gt; db.adminCommand( { setFeatureCompatibilityVersion: &amp;#34;3.6&amp;#34; } )
{ &amp;#34;ok&amp;#34; : 1 }
rs:PRIMARY&amp;gt; db.adminCommand( { getParameter: 1, featureCompatibilityVersion: 1 } )
{ &amp;#34;featureCompatibilityVersion&amp;#34; : { &amp;#34;version&amp;#34; : &amp;#34;3.6&amp;#34; }, &amp;#34;ok&amp;#34; : 1 }
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;OK ! on a fait la moitié du chemin&amp;hellip;&lt;/p&gt;
&lt;h2 id="mettre-à-jour-le-replicaset-dans-la-version-1"&gt;Mettre à jour le ReplicaSet dans la version 1
&lt;/h2&gt;&lt;p&gt;Différence notable entre la version 3.4 et la version 3.6, c’est la façon dont sont gérés les replicaSets. La version 3.6 introduit la version 1 du protocol des replicaSet, et empêche les précédents clusters déclarés de fonctionner.&lt;br&gt;
Il est donc nécessaire de mettre à jour le protocole &lt;strong&gt;avant&lt;/strong&gt; de mettre à jour en 4.0.&lt;/p&gt;
&lt;p&gt;Ouvrir un shell mongo sur le PRIMARY, et modifier la configuration du replicaset (rs) :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;cfg = rs.conf();
cfg.protocolVersion=1;
rs.reconfig(cfg);
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="mise-à-jour-des-binaires-vers-la-40"&gt;Mise à jour des binaires vers la 4.0
&lt;/h2&gt;&lt;p&gt;Bon, je ne vais pas vous refaire l’affront de vous copier coller la procédure de mise à jour : maintenant qu’on a mis à jour la version du replicaSet, c’est la même.&lt;/p&gt;
&lt;p&gt;Pour rappel :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Relancer le playbook pour ajouter le dépôt de la version 4.0 (décommenter la ligne 4.0)&lt;/li&gt;
&lt;li&gt;Mise à jour des serveurs SECONDARY puis redémarrage du serveur&lt;/li&gt;
&lt;li&gt;Step down du PRIMARY&lt;/li&gt;
&lt;li&gt;Mise à jour du PRIMARY devenu SECONDARY après le stepdown, puis redémarrage du serveur&lt;/li&gt;
&lt;li&gt;Mise à niveau de la compatibilité de la base en 4.0&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;rs:PRIMARY&amp;gt; db.adminCommand( { setFeatureCompatibilityVersion: &amp;#34;4.0&amp;#34; } )
{
&amp;#34;ok&amp;#34; : 1,
[...]
}
rs:PRIMARY&amp;gt; db.adminCommand( { getParameter: 1, featureCompatibilityVersion: 1 } )
{
&amp;#34;featureCompatibilityVersion&amp;#34; : {
&amp;#34;version&amp;#34; : &amp;#34;4.0&amp;#34;
},
[...]
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Et voilà, votre cluster est « up-to-date », après 2 upgrades de versions majeures et sans aucune interruption de service. Alors, comme on dit chez moi :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Encore une victoire de Canard !&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;source : &lt;a class="link" href="https://player.ina.fr/player/embed/PUB417943064/1/1b0bd203fbcd702f9bc9b10ac3d0fc21" target="_blank" rel="noopener"
&gt;player.ina.fr/player/embed/PUB417943064/1/1b0bd203fbcd702f9bc9b10ac3d0fc21&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="sources"&gt;Sources
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://docs.mongodb.com/manual/tutorial/install-mongodb-on-ubuntu/" target="_blank" rel="noopener"
&gt;La documentation d’installation de MongoDB&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://web.archive.org/web/20220705195701/https://www.mongodb.com/docs/manual/release-notes/3.6-upgrade-replica-set/" target="_blank" rel="noopener"
&gt;La documentation officielle de mise à jour d’un replicaSet MongoDB vers 3.6(lien mort, j&amp;rsquo;utilise Internet Archive)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://web.archive.org/web/20230325023508/https://www.mongodb.com/docs/manual/release-notes/4.0-upgrade-replica-set/" target="_blank" rel="noopener"
&gt;La documentation officielle de mise à jour d’un replicaSet MongoDB vers 4.0 (lien mort, j&amp;rsquo;utilise Internet Archive)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://docs.mongodb.com/manual/reference/method/rs.stepDown/#rs.stepDown" target="_blank" rel="noopener"
&gt;La documentation officielle de la commande stepdown&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Récupérer les informations sur la distribution avec les facts Ansible</title><link>https://blog.zwindler.fr/2018/11/21/recuperer-les-informations-sur-la-distribution-avec-les-facts-ansible/</link><pubDate>Wed, 21 Nov 2018 12:45:40 +0000</pubDate><guid>https://blog.zwindler.fr/2018/11/21/recuperer-les-informations-sur-la-distribution-avec-les-facts-ansible/</guid><description>&lt;img src="https://blog.zwindler.fr/2018/10/ansible_logo.webp" alt="Featured image of post Récupérer les informations sur la distribution avec les facts Ansible" /&gt;&lt;h2 id="les-facts-dansible"&gt;Les facts d’Ansible
&lt;/h2&gt;&lt;p&gt;Si vous suivez le blog, vous savez que &lt;a class="link" href="https://blog.zwindler.fr/recherche/?keyword=ansible" &gt;j’utilise énormément Ansible&lt;/a&gt;. J’ai même été faire un &lt;a class="link" href="https://blog.zwindler.fr/2018/11/09/bdx-i-o-2018-ami-developpeur-deviens-un-ops-sans-effort-avec-ansible" &gt;talk sur le sujet à BDX I/O 2018, à l’ENSEIRB&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Aujourd’hui, plutôt que de vous donner un playbook tout fait pour installer sans effort une des multiples applications dont j’ai automatisé le déploiement avec Ansible, je vous propose d’explorer une des features les plus importantes et utiles d’Ansible, les &lt;strong&gt;facts&lt;/strong&gt;, par le biais d’un petit exemple.&lt;/p&gt;
&lt;h2 id="cest-quoi-les-facts-dans-ansible"&gt;C’est quoi les facts dans Ansible
&lt;/h2&gt;&lt;p&gt;Si vous avez déjà lancé un playbook, vous avez sûrement remarqué lors de son exécution, que la première tâche qui est réalisée n’est pas une tâche que vous avez demandée explicitement.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;ansible-playbook -i inventory/prod/blop -u blop --private-key=blop.key configure-blop.yml
[...]]
TASK [Gathering Facts] *********************************************************
ok: [blop-vm1]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Cette tâche &lt;strong&gt;[Gathering Facts]&lt;/strong&gt;, qui ne fait à première vue rien, correspond en fait à la connexion à la ou les machines sur lesquelles seront exécutées les playbooks. Si la connexion échoue, le playbook échouera sur cette cible, et continuera éventuellement sur les autres (s’il en reste). Si l’hôte répond, alors Ansible en profite pour récupérer moult informations en rapport avec cette machines et les stockent dans une variable &lt;strong&gt;ansible_facts&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id="a-quoi-ça-ressemble-"&gt;A quoi ça ressemble ?
&lt;/h2&gt;&lt;p&gt;Comme je ne disais, à première vue, cette commande de fait rien. Si on ne sait pas qu’elle stocke des informations dans une variables, on ne peut rien en faire.&lt;/p&gt;
&lt;p&gt;Un bon moyen d’avoir un premier aperçu de ce qu’on peut en faire est de les afficher. On peut faire ça avec &lt;a class="link" href="http://docs.ansible.com/ansible/latest/collections/ansible/builtin/setup_module.html" target="_blank" rel="noopener"
&gt;le module « setup »&lt;/a&gt;, qui est en réalité le même module qui est appelé lors de l’étape &lt;strong&gt;[Gathering facts]&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Le retour se fera au format JSON, ce qui est certes peu digeste, mais facilitera grandement les manipulations de type « filtre » (via le flag filter intégré ou via l’utilitaire &lt;strong&gt;jq&lt;/strong&gt; par exemple).&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;ansible blop-vm1 i inventory/prod/blop -m setup
blop-vm1 | SUCCESS =&amp;gt; {
&amp;#34;ansible_facts&amp;#34;: {
&amp;#34;ansible_all_ipv4_addresses&amp;#34;: [
&amp;#34;10.1.1.10&amp;#34;
],
&amp;#34;ansible_apparmor&amp;#34;: {
&amp;#34;status&amp;#34;: &amp;#34;enabled&amp;#34;
},
[...]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Je ne vous copie-colle pas tout, rien que pour cette machine, j’ai 719 lignes&amp;hellip; La quantité d’informations disponible est impressionnante, et on peut même en venir à se demander à quoi ça va bien pouvoir nous servir.&lt;/p&gt;
&lt;h2 id="ok-on-en-fait-quoi-"&gt;Ok, on en fait quoi ?
&lt;/h2&gt;&lt;p&gt;Du coup je profite de cet article pour faire un tuto tout simple et qui peut être utile dans de nombreux cas, réaliser des opérations différentes en fonction de la distribution de la machine concernée.&lt;/p&gt;
&lt;p&gt;L’information qui va nous intéresser ici concerne donc les remontées en tant que « nom » ou « version » d’une distribution. Je vais vous économiser la lecture de nos 700+ lignes, et vous indiquer que vous pouvez trouver ces informations en filtrant sur les mots clés « ansible_distribution&amp;hellip; » et « ansible_os&amp;hellip; »&lt;/p&gt;
&lt;p&gt;Voilà ce qu’on pourrait obtenir sur une machine CentOS :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;ansible blop-vm1 -m setup -a &amp;#39;filter=ansible_distribution*&amp;#39;
blop-vm1 | SUCCESS =&amp;gt; {
&amp;#34;ansible_facts&amp;#34;: {
&amp;#34;ansible_distribution&amp;#34;: &amp;#34;CentOS&amp;#34;,
&amp;#34;ansible_distribution_major_version&amp;#34;: &amp;#34;7&amp;#34;,
&amp;#34;ansible_distribution_release&amp;#34;: &amp;#34;Core&amp;#34;,
&amp;#34;ansible_distribution_version&amp;#34;: &amp;#34;7.2.1511&amp;#34;
},
&amp;#34;changed&amp;#34;: false
}
ansible blop-vm1 -m setup -a &amp;#39;filter=ansible_os_family&amp;#39;
blop-vm1 | SUCCESS =&amp;gt; {
&amp;#34;ansible_facts&amp;#34;: {
&amp;#34;ansible_os_family&amp;#34;: &amp;#34;RedHat&amp;#34;
},
&amp;#34;changed&amp;#34;: false
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Et sur une machine Ubuntu :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;ansible blop-vm2 -m setup -a &amp;#39;filter=ansible_distribution*&amp;#39;
ansible blop-vm2 | SUCCESS =&amp;gt; {
&amp;#34;ansible_facts&amp;#34;: {
&amp;#34;ansible_distribution&amp;#34;: &amp;#34;Ubuntu&amp;#34;,
&amp;#34;ansible_distribution_file_parsed&amp;#34;: true,
&amp;#34;ansible_distribution_file_path&amp;#34;: &amp;#34;/etc/os-release&amp;#34;,
&amp;#34;ansible_distribution_file_variety&amp;#34;: &amp;#34;Debian&amp;#34;,
&amp;#34;ansible_distribution_major_version&amp;#34;: &amp;#34;16&amp;#34;,
&amp;#34;ansible_distribution_release&amp;#34;: &amp;#34;xenial&amp;#34;,
&amp;#34;ansible_distribution_version&amp;#34;: &amp;#34;16.04&amp;#34;
},
&amp;#34;changed&amp;#34;: false
}
ansible blop-vm2 -m setup -a &amp;#39;filter=ansible_os*&amp;#39;
blop-vm2 | SUCCESS =&amp;gt; {
&amp;#34;ansible_facts&amp;#34;: {
&amp;#34;ansible_os_family&amp;#34;: &amp;#34;Debian&amp;#34;
},
&amp;#34;changed&amp;#34;: false
}
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="utiliser-les-facts-dans-un-playbook"&gt;Utiliser les facts dans un playbook
&lt;/h2&gt;&lt;p&gt;Pour continuer dans l’exemple, on va faire une chose qu’il ne faut jamais faire : désactiver le firewall et SELinux.&lt;/p&gt;
&lt;p&gt;Imaginons que vous souhaitiez quand même le faire. Si vous lancez ce playbook sur tout votre parc et qu’il n’est pas homogène, vous allez tomber sur des groupes de machines Ubuntu, CentOS 5, 6, 7&amp;hellip;&lt;/p&gt;
&lt;p&gt;Un moyen de gérer les cas particuliers pourrait être de les classer tous vos hosts dans des groupes, et de créer un playbook pour chaque OS/version, restreint à un groupe de machines uniquement.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;---
- hosts: rhelcentos6only
tasks:
[…]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Ce n’est d’ailleurs pas une mauvaise idée, surtout pour gérer les distrib aussi différentes que RHEL et Ubuntu par exemple.&lt;/p&gt;
&lt;p&gt;En revanche, dans le cas de RHEL, on a des différences qui apparaissent à partir de la RHEL 7, notamment la gestion du Firewall qui passe de IPtables à firewalld.&lt;/p&gt;
&lt;p&gt;On va donc faire appel à la clause &lt;strong&gt;when:&lt;/strong&gt; de ansible, qui conditionne l’exécution d’une tâche (ou d’un rôle ou d’un block) à une validation. Et ça donnerait quelque chose comme ça :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;---
- hosts: rhelcentos6only
tasks:
- name: &amp;#34;shutdown and disable IPtables for RHEL &amp;lt;= 6&amp;#34;
service:
name=iptables
state=stopped
enabled=no
when: (ansible_distribution == &amp;#34;CentOS&amp;#34; or ansible_distribution == &amp;#34;RedHat&amp;#34;) and
(ansible_distribution_major_version &amp;lt;= &amp;#34;6&amp;#34;)
- name: &amp;#34;shutdown and disable firewalld for RHEL &amp;gt;= 7&amp;#34;
service:
name=firewalld
state=stopped
enabled=no
when: (ansible_distribution == &amp;#34;CentOS&amp;#34; or ansible_distribution == &amp;#34;RedHat&amp;#34;) and
(ansible_distribution_major_version &amp;gt;= &amp;#34;7&amp;#34;)
- name: &amp;#34;disable selinux&amp;#34;
selinux:
state=disabled
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Dans cet exemple, lors de l’exécution du playbook, on aura donc, pour chaque VM CentOS ou RHEL, une tâche qui sera exécutée, et l’autre qui sera marquée « skipping » (en bleu clair) et le playbook marchera pour toutes les RHEL, quelque soit leur version.&lt;/p&gt;
&lt;h2 id="aller-plus-loin"&gt;Aller plus loin
&lt;/h2&gt;&lt;p&gt;Il existe une page spécifique sur la documentation d’Ansible pour parler des conditions dans les playbooks, accessible à l’adresse suivante : &lt;a class="link" href="https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_conditionals.html" target="_blank" rel="noopener"
&gt;https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_conditionals.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Vous pouvez aussi utiliser les facts dans les options des tasks, et dans les templates, en encadrant le nom de la variable souhaitée avec des « doubles curly braces » : &lt;code&gt;{{ xxx }}&lt;/code&gt;&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;- command: &amp;#34;echo {{ ma_super_variable }}&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="sources"&gt;Sources
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="http://docs.ansible.com/ansible/latest/collections/ansible/builtin/setup_module.html" target="_blank" rel="noopener"
&gt;docs.ansible.com/ansible/latest/collections/ansible/builtin/setup_module.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_conditionals.html" target="_blank" rel="noopener"
&gt;docs.ansible.com/ansible/latest/playbook_guide/playbooks_conditionals.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>