<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Dashboard on Zwindler's Reflection</title><link>https://blog.zwindler.fr/tags/dashboard/</link><description>Recent content in Dashboard on Zwindler's Reflection</description><generator>Hugo -- gohugo.io</generator><language>fr-fr</language><copyright>Licensed under CC BY-SA 4.0</copyright><lastBuildDate>Mon, 03 Jan 2022 07:00:00 +0000</lastBuildDate><atom:link href="https://blog.zwindler.fr/tags/dashboard/index.xml" rel="self" type="application/rss+xml"/><item><title>Des visualiations ultra personnalisés sur Kibana avec Vega</title><link>https://blog.zwindler.fr/2022/01/03/visualisations-personalises-kibana-vega/</link><pubDate>Mon, 03 Jan 2022 07:00:00 +0000</pubDate><guid>https://blog.zwindler.fr/2022/01/03/visualisations-personalises-kibana-vega/</guid><description>&lt;img src="https://blog.zwindler.fr/2021/12/okiwi_bubble_chart.webp" alt="Featured image of post Des visualiations ultra personnalisés sur Kibana avec Vega" /&gt;&lt;h2 id="et-si-on-faisait-un-nuage-de-points-avec-kibana-"&gt;Et si on faisait un nuage de points avec Kibana ?
&lt;/h2&gt;&lt;p&gt;Si vous me suivez sur les réseaux sociaux que je fréquente (Twitter et LinkedIn) vous ne pouvez pas avoir loupé le fait que je participe à l&amp;rsquo;organisation du sondage annuel sur les salaires dans la tech Girondine de l&amp;rsquo;association Okiwi. (OUI, je vous ai bassiné avec ça, mais c&amp;rsquo;est pour la bonne cause :chatpotté:).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Aujourd&amp;rsquo;hui, j&amp;rsquo;en parle pas, promis ;-p&lt;/strong&gt; (&lt;em&gt;&lt;a class="link" href="https://blog.zwindler.fr/2021/12/13/okiwi-grille-de-salaires-anonyme-dans-la-tech-girondine-2021-2022" &gt;ou alors, juste un tout petit mini lien ?&lt;/a&gt;&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;Cet article est juste un retour d&amp;rsquo;expérience suite à un problème que j&amp;rsquo;ai eu, dans le cadre de ce sondage.&lt;/p&gt;
&lt;p&gt;Frédéric Camblor m&amp;rsquo;a fait remarquer que pour visualiser les différents types de salaires en fonction de certaines catégories, un nuage de points pouvait être une visualisation efficace.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2021/12/twitter_fred_camblor.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Or, pas de bol, on ne peut pas nativement faire ce type de visualisation dans Kibana&amp;hellip; Et vous allez voir que la solution est tout sauf triviale&amp;hellip;&lt;/p&gt;
&lt;h2 id="déjà-comment-le-savoir-"&gt;Déjà, comment le savoir ?
&lt;/h2&gt;&lt;p&gt;Pour en avoir le coeur net, j&amp;rsquo;ai épluché toutes les possibilités offertes par la page &amp;ldquo;Visualisation&amp;rdquo; de mon Kibana.&lt;/p&gt;
&lt;p&gt;D&amp;rsquo;abord, il y a &lt;strong&gt;Lens&lt;/strong&gt;, a priori c&amp;rsquo;est relativement récent car ça ne me disait rien, mais c&amp;rsquo;est une façon assez pratique, user friendly et intuitive de créer des visualisations.&lt;/p&gt;
&lt;p&gt;On drag&amp;rsquo;n&amp;rsquo;drop la métrique qu&amp;rsquo;on veut représenter et Kibana nous propose une ou plusieurs visualisation avec un type d&amp;rsquo;aggregation (count, avg, median) déterminé automatiquement.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2021/12/twitter_elastic_lens.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Pour l&amp;rsquo;avoir utilisé pour la première fois pour le sondage des salaires de cette année, c&amp;rsquo;est hyper bien pensé et j&amp;rsquo;ai gagné beaucoup de temps par rapport à l&amp;rsquo;ancien éditeur de visualisation.&lt;/p&gt;
&lt;p&gt;Bravo Elastic pour ça.&lt;/p&gt;
&lt;p&gt;Ensuite, il y a l&amp;rsquo;ancienne façon de faire des visualisations, et là encore, pas de nuage de points&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2021/12/new_viz.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Enfin, il y a les TSVB mais c&amp;rsquo;est pour les timeseries (donc là ça va pas le faire), et&amp;hellip; Vega&amp;hellip;&lt;/p&gt;
&lt;h2 id="cest-quoi-vega-"&gt;C&amp;rsquo;est quoi, Vega ?
&lt;/h2&gt;&lt;blockquote&gt;
&lt;p&gt;Nouveauté dans Kibana 6.2 : vous pouvez désormais construire des visualisations riches Vega et Vega-Lite avec vos données Elasticsearch.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Vega-Lite is a high-level grammar of interactive graphics. It provides a concise, declarative JSON syntax to create an expressive range of visualizations for data analysis and presentation.
&amp;ndash; &lt;a class="link" href="https://vega.github.io/vega-lite/" target="_blank" rel="noopener"
&gt;https://vega.github.io/vega-lite/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Grosso modo, pour tout ce que Kibana ne propose pas nativement comme type de visualisation, on vous propose de vous orienter vers le langage Vega, qui permet de déclarer, en JSON, des graphiques assez complexes et spécifiques à votre besoin.&lt;/p&gt;
&lt;p&gt;Cool ! Reste plus qu&amp;rsquo;à trovuer comment ça fonctionne ce biniou, et surtout, comment ça s&amp;rsquo;interface avec les données dans mon ElasticSearch&amp;hellip;&lt;/p&gt;
&lt;p&gt;La première chose que j&amp;rsquo;ai cherché, c&amp;rsquo;est donc des exemples de nuages de points en Vega. Sauf que&amp;hellip; je n&amp;rsquo;avais aucune idée de comment dire &amp;ldquo;nuages de points&amp;rdquo; en anglais (la recherche en Français n&amp;rsquo;ayant rien donné). Et non, ce n&amp;rsquo;est pas &amp;ldquo;cloud dots&amp;rdquo;&amp;hellip;&lt;/p&gt;
&lt;p&gt;Une fois que je savais que c&amp;rsquo;était un &amp;ldquo;bubble chart&amp;rdquo;, c&amp;rsquo;était tout de suite beaucoup plus simple ;-) et j&amp;rsquo;ai trouvé un tuto qui commençait à ressembler à ce que je voulais faire (même si au final ce que j&amp;rsquo;ai fais ne ressemble pas du tout).&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://blog.bigdataboutique.com/2021/04/bubble-chart-in-kibana-with-vega-5il1u2" target="_blank" rel="noopener"
&gt;Bubble Chart in Kibana with Vega&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="ca-ressemble-à-quoi-vega-"&gt;Ca ressemble à quoi, Vega ?
&lt;/h2&gt;&lt;p&gt;Si vous cliquez sur &amp;ldquo;Custom Visualization&amp;rdquo;, vous allez tomber sur cette page dans votre Kibana :&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2021/12/vega1.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;A gauche, un aperçu de ce que vous êtes en train de faire, et à droite, le code JSON associé.&lt;/p&gt;
&lt;p&gt;Il y a pas mal de gloubiboulga mais vous allez vite vous rendre compte que c&amp;rsquo;est surtout des commentaires pour vous aider à comprendre ce que vous êtes censé écrire pour que ça fonctionne.&lt;/p&gt;
&lt;p&gt;Si vous enlevez tous les commentaires, vous devriez tomber sur ça :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-JSON" data-lang="JSON"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;$schema:&lt;/span&gt; &lt;span class="err"&gt;https:&lt;/span&gt;&lt;span class="c1"&gt;//vega.github.io/schema/vega-lite/v5.json
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;title:&lt;/span&gt; &lt;span class="err"&gt;Event&lt;/span&gt; &lt;span class="err"&gt;counts&lt;/span&gt; &lt;span class="err"&gt;from&lt;/span&gt; &lt;span class="err"&gt;all&lt;/span&gt; &lt;span class="err"&gt;indexes&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;data:&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;url:&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;%context%:&lt;/span&gt; &lt;span class="err"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;%timefield%:&lt;/span&gt; &lt;span class="err"&gt;@timestamp&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;index:&lt;/span&gt; &lt;span class="err"&gt;_all&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;body:&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;aggs:&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;time_buckets:&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;date_histogram:&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;field:&lt;/span&gt; &lt;span class="err"&gt;@timestamp&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;interval:&lt;/span&gt; &lt;span class="err"&gt;{%autointerval%:&lt;/span&gt; &lt;span class="err"&gt;true&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;extended_bounds:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;min:&lt;/span&gt; &lt;span class="err"&gt;{%timefilter%:&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;min&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;max:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;%timefilter%:&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;max&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;min_doc_count:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;size:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;format:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;property:&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;aggregations.time_buckets.buckets&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;mark:&lt;/span&gt; &lt;span class="err"&gt;line&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;encoding:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;x:&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;field:&lt;/span&gt; &lt;span class="err"&gt;key&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;type:&lt;/span&gt; &lt;span class="err"&gt;temporal&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;axis:&lt;/span&gt; &lt;span class="err"&gt;{title:&lt;/span&gt; &lt;span class="err"&gt;false&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;y:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;field:&lt;/span&gt; &lt;span class="err"&gt;doc_count&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;type:&lt;/span&gt; &lt;span class="err"&gt;quantitative&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;axis:&lt;/span&gt; &lt;span class="err"&gt;{title:&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;Document count&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;La difficulté de faire du Vega dans Kibana a été pour moi que je découvrais 2 choses en même temps :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;requêter elastic depuis Kibana avec des notions d&amp;rsquo;aggregations, buckets, etc que je ne maitrisais pas du tout&lt;/li&gt;
&lt;li&gt;découvrir le langage Vega&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2021/12/as-you-can-see-i-totally-know-what-im-doing.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;h2 id="choisir-lindex"&gt;Choisir l&amp;rsquo;index
&lt;/h2&gt;&lt;p&gt;La première chose qu&amp;rsquo;on remarque, c&amp;rsquo;est que les données récupérées d&amp;rsquo;ElasticSearch et donné à manger à Kibana proviennent de TOUS nos index car on a la ligne &lt;code&gt;index: _all&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-JSON" data-lang="JSON"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;data:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;url:&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;%context%:&lt;/span&gt; &lt;span class="err"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;%timefield%:&lt;/span&gt; &lt;span class="err"&gt;@timestamp&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;index:&lt;/span&gt; &lt;span class="err"&gt;_all&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Clairement, ça va pas le faire ;-) du coup on restreint à l&amp;rsquo;index qui nous intéresse (okiwi-2022 dans mon cas)&lt;/p&gt;
&lt;h2 id="requêter-elastic-avec-des-aggrégations"&gt;Requêter Elastic avec des aggrégations
&lt;/h2&gt;&lt;p&gt;Le deuxième gros morceau à comprendre, c&amp;rsquo;est la partie interrogation de notre ElasticSearch&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-JSON" data-lang="JSON"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;body:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;aggs:&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;time_buckets:&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;date_histogram:&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;field:&lt;/span&gt; &lt;span class="err"&gt;@timestamp&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;interval:&lt;/span&gt; &lt;span class="err"&gt;{%autointerval%:&lt;/span&gt; &lt;span class="err"&gt;true&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;extended_bounds:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;min:&lt;/span&gt; &lt;span class="err"&gt;{%timefilter%:&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;min&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;max:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;%timefilter%:&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;max&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;min_doc_count:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;size:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ici, l&amp;rsquo;exemple fait un graphique en fonction du temps (x-axis). Les données sont regroupées avec une aggrégation de type &lt;code&gt;date_histogram&lt;/code&gt;, avec un intervalle automatique et en se basant sur le timestamp de nos entrée dans ElasticSearch.&lt;/p&gt;
&lt;p&gt;Ce que j&amp;rsquo;ai mis un bon moment à comprendre (car la doc est pas ouf non plus et j&amp;rsquo;ai perdu beaucoup de temps dessus) c&amp;rsquo;est qu&amp;rsquo;on a pas forcément BESOIN de faire des aggrégations.&lt;/p&gt;
&lt;p&gt;Les aggregations possibles, si vous, vous en avez besoin, sont disponibles ici&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket.html" target="_blank" rel="noopener"
&gt;https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-pipeline.html" target="_blank" rel="noopener"
&gt;https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-pipeline.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Typiquement, dans mon cas, je ne voulais pas afficher un point (genre la moyenne) pour un groupe de personne de mon sondage, mais bien un point par personne !&lt;/p&gt;
&lt;h2 id="requêter-elastic-sans-aggrégation"&gt;Requêter Elastic sans aggrégation
&lt;/h2&gt;&lt;p&gt;Pas besoin d&amp;rsquo;aggrégation donc&amp;hellip;&lt;/p&gt;
&lt;p&gt;Il m&amp;rsquo;a fallu un bon moment pour trouver car tous les exemples que j&amp;rsquo;ai trouvé sur Internet passaient TOUS par des aggrégations préalables, mais j&amp;rsquo;ai fini par trouver comment juste faire une requête à ElasticSearch depuis Kibana de la façon suivante (depuis les &amp;ldquo;Dev Tools&amp;rdquo; ou bien un cURL) :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-JSON" data-lang="JSON"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;GET&lt;/span&gt; &lt;span class="err"&gt;okiwi&lt;/span&gt;&lt;span class="mi"&gt;-2022&lt;/span&gt;&lt;span class="err"&gt;/_search&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&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;&amp;#34;size&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&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;&amp;#34;_source&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Nombre d&amp;#39;années d&amp;#39;expérience&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Salaire *partie fixe BRUT* (€) annuel (ex_ 30000)_ &amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Type d&amp;#39;entreprise&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ici, je requête l&amp;rsquo;index okiwi-2022 et je lui demande 10 résultats contenant les propriétés &amp;ldquo;Nombre d&amp;rsquo;années d&amp;rsquo;expérience&amp;rdquo;, &amp;ldquo;Salaire &lt;em&gt;partie fixe BRUT&lt;/em&gt; (€) annuel (ex_ 30000)_ &amp;ldquo;, &amp;ldquo;Type d&amp;rsquo;entreprise&amp;rdquo; de mes documents.&lt;/p&gt;
&lt;p&gt;Une fois que je savais ça, j&amp;rsquo;ai juste testé dans Vega&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-JSON" data-lang="JSON"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;body:&lt;/span&gt; &lt;span class="p"&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;&amp;#34;size&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&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;&amp;#34;_source&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Nombre d&amp;#39;années d&amp;#39;expérience&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Salaire *partie fixe BRUT* (€) annuel (ex_ 30000)_ &amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Type d&amp;#39;entreprise&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Bon ok, ça marchait toujours pas 😭😭😭 mais j&amp;rsquo;avais progressé.&lt;/p&gt;
&lt;p&gt;La &lt;em&gt;blague&lt;/em&gt;, c&amp;rsquo;est que le JSON renvoyé par Elasticsearch est un poil différent en fonction du fait que vous utilisiez ou non une aggregation.&lt;/p&gt;
&lt;p&gt;Dans le premier cas, les données sont encapsulées dans &lt;code&gt;aggregations.time_buckets.buckets&lt;/code&gt;, d&amp;rsquo;où la ligne suivante, en dessous du bloc &amp;ldquo;data&amp;rdquo;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-JSON" data-lang="JSON"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;format:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;property:&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;aggregations.time_buckets.buckets&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Dans le cas où vous avez une requête sans aggrégation, il suffit &amp;ldquo;juste&amp;rdquo; de remplacer par&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-JSON" data-lang="JSON"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;format:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;property:&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;hits.hits&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="le-graphique-en-lui-même"&gt;Le graphique en lui même
&lt;/h2&gt;&lt;p&gt;OK, maintenant, on a les données, on peut faire du Vega purement cosmétique.&lt;/p&gt;
&lt;p&gt;La première chose que j&amp;rsquo;ai voulu changer, c&amp;rsquo;est que dans l&amp;rsquo;exemple donné quand on créé le graphique, on a des traits. Or, moi je veux des points.&lt;/p&gt;
&lt;p&gt;Ca, c&amp;rsquo;est géré par la ligne :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-JSON" data-lang="JSON"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;mark:&lt;/span&gt; &lt;span class="err"&gt;line&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;On remplace ça par :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-JSON" data-lang="JSON"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;mark:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;point&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Et puis pour que le graphique soit plus &amp;ldquo;user friendly&amp;rdquo;, on peut activer les &lt;code&gt;tooltips&lt;/code&gt; pour voir les détails du point quand on passe la souris dessus&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-JSON" data-lang="JSON"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;mark:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;point&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;tooltip&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Et puis comme les points sont trop petits (30 de base), on peut grossir un peu&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-JSON" data-lang="JSON"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;mark:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;point&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;tooltip&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;size:&lt;/span&gt; &lt;span class="err"&gt;80&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="les-axes"&gt;Les axes
&lt;/h2&gt;&lt;p&gt;Normalement notre graphique est pété depuis nos premières modifs car l&amp;rsquo;exemple donné par Kibana se base sur des axes temporels et des données récupérées depuis une aggrégation et qu&amp;rsquo;on a viré tout ça.&lt;/p&gt;
&lt;p&gt;Il est donc grand temps de rebosser sur les axes et surtout où on place nos points (en fonction de quels axes).&lt;/p&gt;
&lt;p&gt;Cette partie est gérée par le bloc &lt;code&gt;encoding&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-JSON" data-lang="JSON"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;encoding:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;x:&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;field:&lt;/span&gt; &lt;span class="err"&gt;key&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;type:&lt;/span&gt; &lt;span class="err"&gt;temporal&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;axis:&lt;/span&gt; &lt;span class="err"&gt;{title:&lt;/span&gt; &lt;span class="err"&gt;false&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;y:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;field:&lt;/span&gt; &lt;span class="err"&gt;doc_count&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;type:&lt;/span&gt; &lt;span class="err"&gt;quantitative&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;axis:&lt;/span&gt; &lt;span class="err"&gt;{title:&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;Document count&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Dans mon exemple, je veux afficher le salaire des gens en fonction de leur expérience. Mon axe des X est donc le nombre d&amp;rsquo;années d&amp;rsquo;expérience et celui des y est donc le salaire.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-JSON" data-lang="JSON"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;x:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;field:&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;_source.Nombre d&amp;#39;années d&amp;#39;expérience&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;quantitative&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;axis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;title:&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;Années d&amp;#39;expériences&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;y:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;field:&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;_source.Salaire *partie fixe BRUT* (€) annuel (ex_ 30000)_ &amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;quantitative&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;axis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;title:&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;Salaire brut&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Pour faire joli, je leur ai donné un titre avec &lt;code&gt;axis: {title: &amp;quot;&amp;quot;}&lt;/code&gt;, c&amp;rsquo;est quand même plus sympa que le nom dégueulasse que j&amp;rsquo;ai importé du google forms en CSV ;-). Dans les deux cas, mes axes sont &amp;ldquo;quantitatifs&amp;rdquo; et non plus &amp;ldquo;temporels&amp;rdquo;. La documentation Vega sur les axes &lt;a class="link" href="https://vega.github.io/vega/docs/axes/" target="_blank" rel="noopener"
&gt;est disponible ici&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Une fois que tout ça est fait, ça devrait donner un truc qui ressemble grosso modo à ça (la requête à droite est pas la bonne mais le résultat est le même):&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2021/12/bubble_chart_1.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;h2 id="rendre-ça-joli"&gt;Rendre ça joli
&lt;/h2&gt;&lt;p&gt;On n&amp;rsquo;est plus très loin de ce que Frédéric m&amp;rsquo;a proposé en début d&amp;rsquo;aticle. Reste maintenant à discriminer visuellement les données en fonction du type de société dans laquelle le salarié est embauché.&lt;/p&gt;
&lt;p&gt;Pour ça, on va changer la forme ET la couleur du point en fonction d&amp;rsquo;une 3ème donnée : son type d&amp;rsquo;entreprise.&lt;/p&gt;
&lt;p&gt;Bon, je vais le dire tout de suite, j&amp;rsquo;ai passé pas mal de temps sur cette partie et je ne suis pas réussi à faire ce que je voulais malgré plusieurs essais&amp;hellip;&lt;/p&gt;
&lt;p&gt;La méthode pour faire ça est d&amp;rsquo;utiliser les valeurs &lt;code&gt;shape&lt;/code&gt; et &lt;code&gt;color&lt;/code&gt; dans &lt;code&gt;encoding&lt;/code&gt;. Là où Vega est plutôt malin, c&amp;rsquo;est que si vous mettez la même valeur comme &lt;code&gt;field&lt;/code&gt; dans &lt;code&gt;shape&lt;/code&gt; et &lt;code&gt;color&lt;/code&gt;, les deux sont &amp;ldquo;merge&amp;rdquo; dans la légende, comme le montre cet exemple de la doc de Vega&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2021/12/vega_ordinal_scales.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a class="link" href="https://vega.github.io/vega-lite/docs/scale.html#ordinal" target="_blank" rel="noopener"
&gt;https://vega.github.io/vega-lite/docs/scale.html#ordinal&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-JSON" data-lang="JSON"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;shape:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;field&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;_source.Type d&amp;#39;entreprise&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;nominal&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;color:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;field&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;_source.Type d&amp;#39;entreprise&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;nominal&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Sauf que les couleurs choisies par Kibana (qui utilise sa propre scale) sont plus que bof&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2021/12/bublle_couleur_par_defaut.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;En relisant la doc, on voit qu&amp;rsquo;on devrait pouvoir choisir notre propre scale&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The range of an ordinal scale can be an array of desired output values, which are directly mapped to elements in the domain. Both domain and range array can be re-ordered to specify the order and mapping between the domain and the output range. For ordinal color scales, a custom scheme can be set as well.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Sauf que tous mes tests se sont révélés être des échecs :-( Au mieux, j&amp;rsquo;ai réussi à le faire, mais en pétant la légende.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-JSON" data-lang="JSON"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;color:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;mark:&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;point&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;tooltip&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;size:&lt;/span&gt; &lt;span class="err"&gt;60&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;nominal&amp;#34;&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;scale&amp;#34;&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;domain:&lt;/span&gt; &lt;span class="err"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;Association&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;ESN/SSII&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;Editeur&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;Entreprise non numérique&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;Public&amp;#34;&lt;/span&gt;&lt;span class="err"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;range&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;rgb(230,230,31)&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;rgb(255,0,0)&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;rgb(0,191,0)&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;rgb(31,31,255)&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;rgb(191,31,191)&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2021/12/bublle_couleur_ameliore.PNG"
loading="lazy"
&gt;&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;Ce &amp;ldquo;besoin&amp;rdquo; de faire un graphique non prévu &amp;ldquo;out of the box&amp;rdquo; dans Kibana m&amp;rsquo;aura permi de découvrir plusieurs choses. D&amp;rsquo;abord, les différentes façon de requêter ElasticSearch car je n&amp;rsquo;avais jamais eu besoin de le faire (au delà de sortir les documents entier avec un bête &lt;code&gt;index/_search&lt;/code&gt; sans filtres).&lt;/p&gt;
&lt;p&gt;Ensuite, j&amp;rsquo;ai réussi à faire un graphique approchant de ce que je voulais faire.&lt;/p&gt;
&lt;p&gt;Et enfin, ça m&amp;rsquo;a surtout permis de voir que je pouvais totalement me passer de Kibana pour faire des visulisations jolies.&lt;/p&gt;
&lt;p&gt;Car beaucoup de gens qui font du Vega utilisent tout simplement une URL pointant vers un CSV et c&amp;rsquo;est totalement suffisant pour afficher des graphiques sur Internet&amp;hellip; On perd le côté interactif bien sûr, mais pour les cas où j&amp;rsquo;ai besoin de quelque chose de simple, ça me suffira largement.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Mise à jour mars 2026&lt;/strong&gt; : le serveur Kibana a été décommissionné. Les dashboards interactifs ne sont plus disponibles. Les captures d&amp;rsquo;écran et les exports de données sont consultables dans &lt;a class="link" href="https://blog.zwindler.fr/2026/03/25/decomission-kibana-zwindler-fr-sondages-okiwi" &gt;l&amp;rsquo;article dédié à la décommission&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Have fun :-)&lt;/p&gt;
&lt;h2 id="sources"&gt;Sources
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://www.elastic.co/fr/blog/getting-started-with-vega-visualizations-in-kibana" target="_blank" rel="noopener"
&gt;https://www.elastic.co/fr/blog/getting-started-with-vega-visualizations-in-kibana&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://vega.github.io/vega-lite/examples/circle_bubble_health_income.html" target="_blank" rel="noopener"
&gt;https://vega.github.io/vega-lite/examples/circle_bubble_health_income.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.elastic.co/fr/blog/custom-vega-visualizations-in-kibana" target="_blank" rel="noopener"
&gt;https://www.elastic.co/fr/blog/custom-vega-visualizations-in-kibana&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.elastic.co/guide/en/kibana/6.7/vega-querying-elasticsearch.html" target="_blank" rel="noopener"
&gt;https://www.elastic.co/guide/en/kibana/6.7/vega-querying-elasticsearch.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://discuss.elastic.co/t/bubble-chart-with-ratios/185128" target="_blank" rel="noopener"
&gt;https://discuss.elastic.co/t/bubble-chart-with-ratios/185128&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Découvrir Prometheus et Grafana par l’exemple</title><link>https://blog.zwindler.fr/2020/04/13/decouvrir-prometheus-et-grafana-par-lexemple/</link><pubDate>Mon, 13 Apr 2020 06:40:00 +0000</pubDate><guid>https://blog.zwindler.fr/2020/04/13/decouvrir-prometheus-et-grafana-par-lexemple/</guid><description>&lt;img src="https://blog.zwindler.fr/2020/01/20200102_084825-2.webp" alt="Featured image of post Découvrir Prometheus et Grafana par l’exemple" /&gt;&lt;h2 id="grafana-et-prometheus"&gt;Grafana et Prometheus
&lt;/h2&gt;&lt;p&gt;Ça fait plusieurs articles que je vous parle de &lt;a class="link" href="https://blog.zwindler.fr/recherche/?keyword=Prometheus" &gt;Prometheus et de Grafana&lt;/a&gt;, notamment pour l’installer. Mais je n’avais pas encore pris le temps de faire un article pour vous montrer comment les utiliser (et pourquoi ces deux outils sont géniaux) !&lt;/p&gt;
&lt;p&gt;Typiquement, ça va nous permettre de réaliser ce genre de dashboard, qui permettra aux équipes (production, dev, voire même équipes fonctionnelles) de voir en un coup d’œil si tout va bien ou au contraire, ce qui va mal.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2020/01/20200102_084825-2-1.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;h2 id="un-cas-utile"&gt;Un cas utile
&lt;/h2&gt;&lt;p&gt;Et tant qu’à présenter les outils, je me suis dis que j’allais utiliser un des exemples que j’avais eu à mettre en place &lt;em&gt;dans la vraie vie&lt;/em&gt; : tester que mes déploiements Kubernetes respectent bien l’anti-affinité.&lt;/p&gt;
&lt;p&gt;Cet exemple est volontairement &amp;ldquo;un peu complexe&amp;rdquo;, car il a vocation à vous permettre d’appréhender d’un seul coup plusieurs concepts qui seront utiles pour bien débuter dans Grafana et Prometheus. Notamment, la sélection de la bonne métrique, le langage PromQL, l’ajout de variables dans les dashboards, etc.&lt;/p&gt;
&lt;p&gt;Note : Pour ceux qui ne l’ont pas, dans l’orchestrateur de containers Kubernetes, il est possible d’indiquer à l’outil que 2 replicas d’une même application (pour la redondance) ne doivent pas être exécutée sur la même ressource. Ça permet entre autre de garantir qu’il n’y a pas un SPOF au niveau de l’hôte qui exécute les containers alors qu’on croit avoir une application redondante.&lt;/p&gt;
&lt;h2 id="trouver-les-métriques-dans-prometheus"&gt;Trouver les métriques dans Prometheus
&lt;/h2&gt;&lt;p&gt;Je vais partir du principe que vous avez déjà une plateforme opérationnelle, équipée d’un Prometheus et d’un Grafana. Si ce n’est pas le cas, je vous invite à faire une rapide recherche sur votre moteur de recherche préféré ou de &lt;a class="link" href="https://blog.zwindler.fr/recherche/?keyword=prometheus" &gt;consulter mes précédents articles sur le sujet&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;La première étape avant de commencer à essayer de monter de beaux dashboards consiste à chercher la métrique qui nous intéresse. Car, il faut bien l’admettre, généralement Prometheus en collecte BEAUCOUP !&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2019/12/prometheus_timeseries01.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Rien que Prometheus lui même expose et stocke plus de 700 métriques&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;On va donc se connecter sur notre serveur Prometheus, puis requêter l’ensemble des métriques disponibles pour en trouver une qui permette de répondre simplement à notre problème.
Pour reprendre l’exemple que j’ai choisi, je veux :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;pour tous les Pods&lt;/li&gt;
&lt;li&gt;m’assurer que plusieurs Pods d’un même déploiement ne sont pas exécuté sur le même serveur&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pour ça, j’ai donc besoin d’avoir une métrique qui me permettre d’avoir tous les Pods, un information sur le Deploiement concerné, ainsi que le Node sur lequel le Pod est exécuté.&lt;/p&gt;
&lt;h2 id="container_last_seen"&gt;container_last_seen
&lt;/h2&gt;&lt;p&gt;Il y a probablement plusieurs façon de répondre à cette interrogation, mais une des solutions qui marchent plutôt bien pour moi est d’utiliser la métrique &lt;em&gt;container_last_seen&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;En recherchant des métriques dans la console de Prometheus, j’ai pu remarquer que cette métrique donne, pour tous les containers, la date à laquelle il a été vu pour la dernière fois, ainsi qu’un certain nombre d’information sur chaque container.&lt;/p&gt;
&lt;h2 id="on-valide-sur-un-cas-particulier"&gt;On valide sur un cas particulier
&lt;/h2&gt;&lt;p&gt;Pour vérifier que la métrique que je vous indique répond bien à notre problème, je vous propose de tester avec un exemple.&lt;/p&gt;
&lt;p&gt;La requête PromQL suivante permet d’afficher les containers responsable de la résolution DNS interne de mon Kubernetes (déployé dans le namespace &lt;em&gt;kube-system&lt;/em&gt;) :&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2018/09/prometheus1-1.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;container_last_seen{container_name=~&amp;#34;.*dns.*&amp;#34;,namespace=~&amp;#34;kube-system&amp;#34;}
container_last_seen{[...],container_name=&amp;#34;dns&amp;#34;,instance=&amp;#34;node1&amp;#34;,namespace=&amp;#34;kube-system&amp;#34;,pod_name=&amp;#34;coredns-xxxxxxxx-yyyyy&amp;#34;,[...]} 1577569793
container_last_seen{[...],container_name=&amp;#34;dns&amp;#34;,instance=&amp;#34;node2&amp;#34;,namespace=&amp;#34;kube-system&amp;#34;,pod_name=&amp;#34;coredns-xxxxxxxx-zzzzz&amp;#34;,[...]} 1577566730
container_last_seen{[...],container_name=&amp;#34;dns&amp;#34;,instance=&amp;#34;node3&amp;#34;,namespace=&amp;#34;kube-system&amp;#34;,pod_name=&amp;#34;coredns-xxxxxxxx-aaaaa&amp;#34;,[...]} 1577569313
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Pour ceux qui débutent en PromQL, il s’agit du langage de requêtage de Prometheus, et qui permet entre autre de filtrer les timeseries affichées en fonction de critères (listés dans la partie entre les accolades).&lt;/p&gt;
&lt;p&gt;Dans les résultats de la requête, on a bien :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;le nom de notre Deployment (container_name)&lt;/li&gt;
&lt;li&gt;le nom de l’hôte qui héberge le container (instance)&lt;/li&gt;
&lt;li&gt;le nom du Pod (pod_name)&lt;/li&gt;
&lt;li&gt;le namespace&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="créer-notre-visualisation-dans-grafana"&gt;Créer notre visualisation dans Grafana
&lt;/h2&gt;&lt;p&gt;Maintenant qu’on a trouvé la métriques qui nous intéresse, on peut aller dans Grafana et créer un nouveau Dashboard&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2019/12/grafana_reate_dashboard.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Puis un nouveau Panel dans notre Dashboard fraîchement créé :&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2019/12/grafana_nouveau_panel.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;A partir de là, on pourrait directement afficher les données de notre métrique, mais ça ne serait pas très informatif. On serait noyé sous une masse de conteneurs, chacun avec leurs variables.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2019/12/grafana_query.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Bref, on va devoir restreindre tout ça, notamment via des variables, pour que ça devienne exploitable !&lt;/p&gt;
&lt;h2 id="il-y-en-a-beaucoup-trop"&gt;Il y en a beaucoup trop
&lt;/h2&gt;&lt;p&gt;Dans la capture que j’ai faite juste avant, j’ai quand même été obligé de restreindre à un seul namespace, pour ne pas noyer Prometheus et Grafana. Et encore, c’est (toujours) parfaitement inexploitable.&lt;/p&gt;
&lt;p&gt;Clairement, on ne va pas vouloir se contenter d’une sélection &amp;ldquo;statique&amp;rdquo; des namespaces, au risque de devoir faire un &lt;em&gt;graphique&lt;/em&gt; par &lt;em&gt;namespace&lt;/em&gt; Kubernetes (et vous en avez peut être beaucoup).&lt;/p&gt;
&lt;p&gt;On va donc récupérer la liste des namespaces disponibles dans Prometheus et l’afficher dans une liste déroulante dans notre Dashboard. Cette liste permettra de filtrer les données par namespace pour ne pas faire planter Prometheus.&lt;/p&gt;
&lt;h2 id="les-variables-dans-le-dashboard"&gt;Les variables dans le Dashboard
&lt;/h2&gt;&lt;p&gt;Pour se faire, on va devoir de nouveau trouver la valeur la plus adaptée dans notre source de données Prometheus.&lt;/p&gt;
&lt;p&gt;Je ne vais pas vous faire retourner dans Prometheus pour ça, celle que moi j’utilise, c’est celle ci :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;kube_namespace_labels{namespace!~&amp;#34;kube-.*|default&amp;#34;}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Cette requête PromQL permet de lister l’ensemble des namespaces présents dans votre cluster Kubernetes, puis, entre les accolades, de filtrer pour retirer les namespaces respectant les regexp &amp;ldquo;kube-.*&amp;rdquo; et &amp;ldquo;default&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2018/09/variables2.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Pour autant, on est pas encore complètement sorti d’affaire puisqu’on doit maintenant récupérer uniquement la partie rouge dans ma capture d’écran, qui est la liste des noms des namespaces.&lt;/p&gt;
&lt;p&gt;Arrive alors Grafana, qui fournit une fonction supplémentaire label_values, et qui permet, à partir d’une liste des timeseries Prometheus, de récupérer la valeur d’un seul champ de la timeserie :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;label_values(kube_namespace_labels{namespace!~&amp;#34;kube-.*|default&amp;#34;},namespace)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Et tant qu’a y être, on va également se garder sous le coude une autre requête, quasiment identique, mais qui va nous permettre d’avoir la liste des déploiements présents dans votre cluster pour un (ou plusieurs) namespaces donnés :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;label_values(kube_deployment_labels{namespace=~&amp;#34;$k8snamespace&amp;#34;}, deployment)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A partir de là, je peux ajouter des Variables dans mon Dashboard. Pour le faire, on doit cliquer sur la roue crantée en haut à droite de notre Dashboard :&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2019/12/grafana_roue_crantee.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Puis ouvrir le menu &amp;ldquo;Variables&amp;rdquo; et ajouter les variables&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2019/12/grafana_variable_3.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;A partir du moment où votre source de données et votre requête est entrée dans les champs &lt;em&gt;Data source&lt;/em&gt; et &lt;em&gt;query&lt;/em&gt;, Grafana va vous donner un aperçu des valeurs qui seront disponibles (tout en bas du formulaire). Un bon moyen de vérifier que tout est bon avant de passer à l’étape suivante.&lt;/p&gt;
&lt;h2 id="dans-le-dashboard-nos-variables-apparaissent-"&gt;Dans le dashboard, nos variables apparaissent !
&lt;/h2&gt;&lt;p&gt;On sauvegarde et on retourne dans notre Dashboard.&lt;/p&gt;
&lt;p&gt;Si tout s’est bien passé, on a maintenant, en haut de notre Dashboard, plusieurs variables avec des menus déroulant pour les sélectionner.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2018/09/variables3.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Mais ce n’est pas magique pour autant&amp;hellip;&lt;/p&gt;
&lt;p&gt;Point un peu pénible, on va devoir maintenant modifier toutes nos visualisations (graphiques) pour prendre en compte le fait qu’on ajoute une variable.&lt;/p&gt;
&lt;p&gt;Je ne saurai donc que trop vous conseiller de bien réfléchir à &lt;em&gt;comment&lt;/em&gt; vous allez variabiliser vos Dashboard &lt;em&gt;avant&lt;/em&gt; d’avoir trop de visualisation statiques&amp;hellip;&lt;/p&gt;
&lt;h2 id="ajout-de-la-variable-dans-notre-visualisation"&gt;Ajout de la variable dans notre visualisation
&lt;/h2&gt;&lt;p&gt;On va donc ajouter la variable de manière à rendre nos graphiques dynamiques en fonction des valeurs sélectionnées dans le Dashboard, en modifiant la requête précédente par celle ci :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;container_last_seen{namespace=~&amp;#34;$k8snamespace&amp;#34;,container_name=~&amp;#34;$k8sdeployment.*&amp;#34;,container_name!=&amp;#34;POD&amp;#34;}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Qu’est ce qui a changé ?&lt;/p&gt;
&lt;p&gt;Déjà, votre requête devrait répondre beaucoup plus vite ! Et pour cause, on vient non seulement de restreindre à un seul namespace (celui correspondant à la variable Grafana $k8snamespace et non plus la valeur fixe &amp;ldquo;kube-system&amp;rdquo;) mais aussi à un seul Deployment donné (via $k8sdeployment).&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: on a également dégagé les containers s’appelant &amp;ldquo;POD&amp;rdquo;, qui vont fausser les statistiques.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Pour autant, notre graphique n’est toujours pas exploitable&amp;hellip; On voit bien qu’il existe 2 Pods pour mon Deployment, et si on cherche bien on pourra voir dans la timeserie sur quel Node chaque replica tourne, mais c’est fastidieux&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2019/12/grafana_var1.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;La valeur de chaque timeserie monte de manière constante. C’est normal, c’est un « uptime »&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="compter-le-nombre-de-pod-par-node"&gt;Compter le nombre de Pod par Node
&lt;/h2&gt;&lt;p&gt;On va s’en sortir en tirant parti d’une autre variable présente dans nos timeseries (qu’on a justement remarqué plus haut) : instance, ainsi que d’une fonction d’aggrégation du PromQL : count.&lt;/p&gt;
&lt;p&gt;Cette variable permet, dans notre requête de savoir sur quel Node Kubernetes se situe notre Pod.&lt;/p&gt;
&lt;p&gt;Voilà ce qu’on obtiendra en modifiant la requête de notre visualisation :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;count(container_last_seen{namespace=~&amp;#34;$k8snamespace&amp;#34;,container_name=~&amp;#34;$k8sdeployment.*&amp;#34;,container_name!=&amp;#34;POD&amp;#34;}) by (instance)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2018/09/grafana1.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Avec restriction sur le déploiement&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Là, c’est encore fastidieux, mais on commence à entrevoir la réponse à notre question initiale.
Dans les derniers graphiques, les couleurs représentent les Nodes Kubernetes, et la valeur numérique le nombre de Pods qui tournent dessus pour un Namespace donné. On voit donc que globalement, pour cet exemple précis, les Pods semblent bien répartis (puisque qu’on sélectionne un seul Deployment, on a bien un Pod par Node).&lt;/p&gt;
&lt;h2 id="et-la-solution-"&gt;Et la solution ?
&lt;/h2&gt;&lt;p&gt;Comment on fait pour voir tous les déploiements d’un coup ? Là, ça commence à devenir un peu plus touchy.&lt;/p&gt;
&lt;p&gt;En vrai, la solution n’a plus vraiment d’intérêt en terme de découverte dans l’utilisation de Prometheus et de Grafana, dont j’ai montré les fonctionnalités lors des précédents paragraphes. Cependant, je ne vais pas vous laisser sur un cliffhanger ;-)&lt;/p&gt;
&lt;p&gt;Je ne vais &lt;em&gt;tout&lt;/em&gt; détailler, mais dans l’idée, la solution que j’ai trouvée (il y en a peut être de plus élégantes) est :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;de lister tous les couples nom du déploiement + nom de l’hôte qui l’héberge&lt;/li&gt;
&lt;li&gt;de regrouper &lt;em&gt;par déploiement&lt;/em&gt; les items de la liste précédente et d’en faire une somme pour chaque déploiement&lt;/li&gt;
&lt;li&gt;de diviser chacun des items de cette dernière liste par le nombre total de container par déploiement&lt;/li&gt;
&lt;li&gt;d’afficher la liste des valeurs inférieures à 1&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;1. count(container_last_seen{namespace=~&amp;#34;$k8snamespace&amp;#34;,container_name!~&amp;#34;^$|POD&amp;#34;}) by (container_name,instance))
2. count(count(container_last_seen{namespace=~&amp;#34;$k8snamespace&amp;#34;,container_name!~&amp;#34;^$|POD&amp;#34;}) by (container_name,instance)) by (container_name)
3. count(count(container_last_seen{namespace=~&amp;#34;$k8snamespace&amp;#34;,container_name!~&amp;#34;^$|POD&amp;#34;}) by (container_name,instance)) by (container_name) / count(container_last_seen{namespace=~&amp;#34;$k8snamespace&amp;#34;,container_name!~&amp;#34;^$|POD&amp;#34;}) by (container_name)
4. count(count(container_last_seen{namespace=~&amp;#34;$k8snamespace&amp;#34;,container_name!~&amp;#34;^$|POD&amp;#34;}) by (container_name,instance)) by (container_name) / count(container_last_seen{namespace=~&amp;#34;$k8snamespace&amp;#34;,container_name!~&amp;#34;^$|POD&amp;#34;}) by (container_name) &amp;lt; 1
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Ainsi, avec cette dernière formule, on peut lister l’ensemble des Deployments Kubernetes dont il existe un nombre de replica supérieur au nombre de Nodes qui les hébergent (et donc, de dénicher des soucis sur l’anti-affinités et par extension, de potentiels SPOF).&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2018/09/grafana6.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;CQFD :-D&lt;/p&gt;</description></item><item><title>ElasticStack : Visualisez vos données dans Kibana</title><link>https://blog.zwindler.fr/2017/10/17/elasticstack-afficher-les-donnees-dans-kibana/</link><pubDate>Tue, 17 Oct 2017 11:30:19 +0000</pubDate><guid>https://blog.zwindler.fr/2017/10/17/elasticstack-afficher-les-donnees-dans-kibana/</guid><description>&lt;img src="https://blog.zwindler.fr/2017/10/elastic_3.webp" alt="Featured image of post ElasticStack : Visualisez vos données dans Kibana" /&gt;&lt;h2 id="collecter-cest-bien--visualiser-cest-mieux"&gt;Collecter, c’est bien ; visualiser, c’est mieux
&lt;/h2&gt;&lt;p&gt;Cet article fait partie d’une suite d’articles sur ElasticStack, un produit &lt;a class="link" href="https://www.elastic.co/fr/" target="_blank" rel="noopener"
&gt;elastic.co&lt;/a&gt;, composé des produits ElasticSearch, Logstash, Kibana et depuis plus récemment des agents Beats.&lt;/p&gt;
&lt;p&gt;Pour ceux qui auraient raté les articles précédents, j’explique plus en détail de quoi il s’agit dans 2 articles. Et bon DevOps que je suis, tout est bien entendu entièrement automatisé par des playbooks Ansible (et oui, vous n’avez rien à faire) :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.zwindler.fr/2017/10/03/elasticstack-kibana-elasticsearch-logstash-beats/" &gt;ElasticStack : Déployer Kibana, Elasticsearch, Logstash &amp;amp; Beats avec Ansible&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.zwindler.fr/2017/10/10/elasticstack-collecter-et-exploiter-des-logs-nginx/" &gt;ElasticStack : Collecter et exploiter des métriques provenant de nginx&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="on-en-est-où-"&gt;On en est où ?
&lt;/h2&gt;&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2017/10/elk-infrastructure.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Dans les deux articles précédents nous avons donc travaillé en amont d’ElasticSearch. Les données des serveurs ont été collectées, centralisées, traitées puis intégrées dans ElasticSearch. On sait faire des requêtes simples dessus via des curl.&lt;/p&gt;
&lt;p&gt;C’est bien beau tout ça, mais on en fait quoi maintenant ? Et bien maintenant on va aller en aval et travailler avec Kibana !&lt;/p&gt;
&lt;h2 id="premiers-pas-avec-kibana"&gt;Premiers pas avec Kibana
&lt;/h2&gt;&lt;p&gt;Normalement si tout s’est bien passé, on peut maintenant se connecter à Kibana, et Kibana peut communiquer avec ElasticSearch.&lt;/p&gt;
&lt;p&gt;L’URL par défaut est &lt;code&gt;http://@IP_MON_SERVEUR_ELK:5601&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2017/10/kibana_log.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Joli logo qui change de couleur pendant le chargement&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;S’il y a un souci de connexion entre les deux logiciels qu’on vient de citer, vous aurez rapidement un message d’erreur plutôt explicite qui vous indiquera que ElasticSearch n’est pas joignable. Parfois c’est temporaire, par exemple lorsque vous démarrez ElasticSearch il arrive qu’il mette du temps à récupérer les index mais ça sera indiqué dans le message d’erreur.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2017/10/kibana_elastic_ko.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;h2 id="création-des-index-patterns"&gt;Création des Index Patterns
&lt;/h2&gt;&lt;p&gt;La première page sur laquelle vous devriez tomber est une page de Management pour la création des Index Pattern. Il s’agit ni plus ni moins d’indiquer à Kibana quels sont les données (index) dans ElasticSearch nous intéressent (on peut en avoir plusieurs) et ensuite de lister les différentes colonnes pour chaque information contenue dans l’index en question.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2017/10/elk1.1.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Attention il s’agit là d’une notion importante&lt;/strong&gt; : Plus les données présentes dans un même index seront différentes (différentes sources, différents champs), plus le nombre de champs dans votre « index pattern » sera important (logique).&lt;/p&gt;
&lt;p&gt;Ça compliquera d’autant plus la création de nouveau graphique et l’exploitation de l’outil en général. Si vous voulez ajouter un nouveau graphique, il faudra filtrer potentiellement beaucoup de données pour n’extraire que celle qui vous intéresse et EN PLUS, de nombreux champs ne vous serviront à rien du tout et compliqueront la conception des visualisation.&lt;/p&gt;
&lt;p&gt;Dans la mesure du possible, on s’astreindra donc à séparer en amont (lors de la configuration Logstash ou Beats) les données trop différentes dans des index distincts.&lt;/p&gt;
&lt;p&gt;Si vous avez suivi les 2 premiers tutos, ça devrait fonctionner tout seul puisque les playbooks utilisent l’index par défaut, &lt;strong&gt;logstash-&lt;/strong&gt;*. Sélectionnez @timestamp comme &lt;strong&gt;Time Filter&lt;/strong&gt; puis cliquez simplement sur &lt;strong&gt;Create&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id="discover"&gt;Discover
&lt;/h2&gt;&lt;p&gt;L’interface d’ELK est relativement simple une fois qu’on sait à quoi sert chaque menu. Maintenant que les index sont configurés, la conception des graphiques peut commencer, dans l’onglet Discover.&lt;/p&gt;
&lt;p&gt;Cette vue est la plus importante pour la conception des graphiques puisque c’est là que vous pourrez explorer puis filtrer vos données, pour en tirer des indicateurs pertinents à surveiller.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2017/10/elk_discover.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Cette vue se découpe en plusieurs parties que je vais décrire ici :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;En jaune, l’index courant et l’ensemble des champs qu’il possède. Dans mon cas, j’en ai pratiquement 200 sur cet index, d’où la nécessité de ne pas trop en rajouter !&lt;/li&gt;
&lt;li&gt;En vert, un rapide graphique permettant de visualiser le nombre d’occurrences correspondant aux filtres et à la plage horaire sélectionnée. Très pratique pour avoir une idée du volume de données récoltées avec le filtre en cours.&lt;/li&gt;
&lt;li&gt;En bleu, un extract des données en elles même. On peut ainsi analyser ce qui tombe dans notre filtre, et ainsi concevoir les graphiques en fonction.&lt;/li&gt;
&lt;li&gt;En violet, la plage horaire sélectionnée (ici 7 jours en relatif, on peut aussi définir un intervalle fixe avec des dates/heures).&lt;/li&gt;
&lt;li&gt;En rouge, la grande force de l’outil =&amp;gt; vous pouvez écrire à la main des requêtes pour filtrer en live vos données.&lt;/li&gt;
&lt;li&gt;En orange, les filtres en cours d’utilisation. Ici, j’ai cliqué sur la remote IP 80.74.x.x ce qui a eu pour effet d’immédiatement filtrer et restreindre à cette seule IP.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;La barre de recherche s’utilise de la façon suivante : on entre les infos qu’on souhaite voir (ou ne pas voir avec « -« ) et hop, ça filtre !&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2017/10/elk4.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Une fois qu’on est content de son filtre, on peut le sauvegarder grâce au menu &lt;strong&gt;Save&lt;/strong&gt; en haut à droite. Il suffit de lui donner un nom explicite, puis de confirmer en cliquant sur le bouton &lt;strong&gt;Save&lt;/strong&gt; Bleu.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2017/10/elk5.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;h2 id="visualize"&gt;Visualize
&lt;/h2&gt;&lt;p&gt;Le but du jeu avant de passer à cet étape est donc de créer un ensemble de requêtes plus ou moins complexes permettant de récupérer les données qui nous sont pertinentes.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2017/10/elk6.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;L’onglet Visualize sert donc à créer des graphiques à partir de ses sources de données pertinentes (aka filtrées). Dans mon cas, je vais donc directement m’appuyer sur la recherche que je viens de sauvegarder pour ne récuperer que les données dont le type est égale à « nginx_access ».&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2017/10/elk7.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;A partir de là, le 2ème gros travail commencer. Ce n’est pas si simple de trouver les champs pertinents pour visualiser nos données de manières efficaces.&lt;/p&gt;
&lt;p&gt;Comme j’ai un peu bossé pour vous dans l’article précédent, j’ai déjà ajouté dans la configuration logstash un champ qui n’est pas présent par défaut : à savoir &lt;strong&gt;geoip.ip&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2017/10/elk2.5.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Avec ce champ, je vais pouvoir me baser sur l’adresse IP pour déterminer plusieurs informations pour chacune des requêtes arrivant sur mon frontal nginx.&lt;/p&gt;
&lt;p&gt;Je tire donc partie de ce champ pour réaliser un comptage de toutes les adresses IP distinctes pour l’axe des ordonnées.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2017/10/elk8.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Pour l’axe des abscisse, dans la majorité des cas, on se contentera du temps via le timestamp.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2017/10/elk9.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Cliquez sur le petit logo « Play » en haut pour visualiser le résultat :&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2017/10/elk9.5.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Même principe que dans « Discover », on sauvegarde la visualisation en cliquant sur le menu Save en haut à droite, puis en validant avec le bouton Save bleu.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2017/10/elk10.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;h2 id="dautre-exemples-de-visualisations"&gt;D’autre exemples de visualisations
&lt;/h2&gt;&lt;p&gt;En sélectionnant un graphique de type « Pie Chart », avec comme Aggregation « Count » et en séparant les tranches via le terme response.keyword, on peut faire un camembert des codes retours HTML.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2017/10/elk11.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;En utilisant le module &lt;strong&gt;geoip&lt;/strong&gt;, dont j’ai parlé plus haut, on peut créer une carte des visiteurs d’un site web :&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2017/10/elk11.2.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Vous n’êtes donc maintenant (presque) limités que par votre imagination. Je dis « presque » parce qu’il n’est, à ma connaissance, pas facile voire impossible de réaliser des opérations mathématiques sur un graphique donné.&lt;/p&gt;
&lt;p&gt;Un exemple : si je souhaite afficher la RAM consommée par un container Docker en Mo et non pas en octets (ce qui est illisible), a priori je ne peux pas le faire (à moins d’utiliser une obscure fonction permettant de dupliquer un index en lui appliquant une formule mathématique, bref, c’est compliqué).&lt;/p&gt;
&lt;p&gt;Pour remédier à ça, elastic.co a sorti un outil appelé Timelion, intégré dans Kibana, mais ce n’est pas le sujet d’aujourd’hui ;-).&lt;/p&gt;
&lt;h2 id="dashboard"&gt;Dashboard
&lt;/h2&gt;&lt;p&gt;Le plus simple maintenant ! On peut créer des Dashboard dans l’onglet Dashboard, simplement en sélectionnant les graphiques précédemment créés puis en les réagençant avec des drags and drops. E-Z !&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2017/10/elk_dashboard.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Voilà quelques uns des dashboards que j’ai créés pour les besoins de cet article pour vous donner une idée, mais on peut bien entendu faire bien plus complexe !&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2017/10/elk15.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2017/10/elk16.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Have fun !&lt;/p&gt;</description></item></channel></rss>