<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Monitoring on Rémi Tech Notes</title><link>https://www.vrchr.fr/tags/monitoring/</link><description>Recent content in Monitoring on Rémi Tech Notes</description><generator>Hugo</generator><language>fr-fr</language><lastBuildDate>Fri, 29 Aug 2025 00:00:00 +0000</lastBuildDate><atom:link href="https://www.vrchr.fr/tags/monitoring/index.xml" rel="self" type="application/rss+xml"/><item><title>Récupérer les logs d'audit MKS avec Vector</title><link>https://www.vrchr.fr/posts/2025/08/29/vector-ldp-websocket/</link><pubDate>Fri, 29 Aug 2025 00:00:00 +0000</pubDate><guid>https://www.vrchr.fr/posts/2025/08/29/vector-ldp-websocket/</guid><description>Comment récupérer les logs d&amp;#39;audit de clusters MKS via la Logs Data Plaform OVHCloud avec Vector</description><content:encoded><![CDATA[<p>J'administre au quotidien des clusters managés OVHCloud. Parmi mes tâches, je dois gérer la stack d'observabilité, et notamment avoir les logs des applis et autres composants du cluster.</p>
<p>Autant, récupérer les logs des applis du cluster c'est simple à coup de Promtail/Alloy/Loki, ELK ou autre stack, mais pour récupérer les logs d'audit du control-plane ce n'était pas évident... Jusqu'à maintenant 😉</p>
<h2 id="ovhcloud-managed-kubernetes-service--logs-data-platform">OVHCloud Managed Kubernetes Service &amp; Logs Data Platform</h2>
<p>La <a href="https://www.ovhcloud.com/fr/identity-security-operations/logs-data-platform/">Logs Data Platform</a> (aka &quot;LDP&quot;) permet de centraliser les logs. Je ne ferai pas une présentation de cette plateforme, car j'utilise ma propre stack, et j'ai des besoins d'archivage/export de logs sur d'autres environnements, pour de l'archivage, SOC, etc.</p>
<p>Cette Logs Data Platform est notamment utilisée pour permettre l'envoi des logs d'audit du control-plane des clusters MKS, sur lesquels nous n'avons pas la main.</p>
<p>En effet, depuis l'interface du manager OVHCloud, on peut transférer les logs :</p>
<p><img alt="Logs MKS" class="zoomable" decoding="async" loading="lazy" src="/2025/08/2025-08-29-manager-ldp.webp"></p>
<p>Pour paramétrer l'envoi de ces logs, je vous renvoie sur la <a href="https://help.ovhcloud.com/csm/fr-public-cloud-kubernetes-forwarding-audit-logs?id=kb_article_view&amp;sysparm_article=KB0062284">doc officielle</a>.</p>
<h2 id="vector--websocket">Vector &amp; websocket</h2>
<p>OK, jusque là j'ai mes logs sur la &quot;LDP&quot;, mais je veux pouvoir les récupérer chez moi (ou pouvoir les envoyer ailleurs) !</p>
<p>A la suite d'articles comme <a href="https://blog.ovhcloud.com/enhancing-kubernetes-security-detecting-threats-in-ovhcloud-managed-kubernetes-cluster-mks-audit-logs-with-falco/">Falco</a>, j'ai découvert qu'il était possible de récupérer les logs via <code>websocket</code> ! Et si, par chance, il n'existerait pas un outil pour ça ?</p>
<h3 id="vector">Vector</h3>
<p>Vous l'aurez deviné, <a href="https://vector.dev/">Vector</a> est un outil qui permet de récupérer, transformer, envoyer des logs, métriques et traces, un très bon outil d'observabilité !</p>
<p>Parmi ses qualités, on notera :</p>
<ul>
<li>sa rapidité de traitement vs sa faible empreinte cpu/mémoire (oui, c'est écrit en rust)</li>
<li>son écosystème de plugins riches</li>
<li>sa facilité de déploiement.</li>
</ul>
<p><img alt="Architecture Vectordev" class="zoomable" decoding="async" loading="lazy" src="/2025/08/2025-08-29-vectordev-archi.webp"></p>
<p>Et, &quot;comme par hasard&quot;, la version <a href="https://vector.dev/releases/0.49.0/">v0.49.0</a> vient de sortir avec une fonctionnalité qui m'intéresse :</p>
<blockquote>
<p>&quot;A websocket source was introduced. A WebSocket source in Vector enables ingestion of real-time data from services that expose WebSocket APIs.&quot;</p>
</blockquote>
<p>Bon, ben y a plus qu'à !!</p>
<h3 id="paramétrage--test-local">Paramétrage &amp; test local</h3>
<p>Avant de déployer ça sur mes clusters, on va tester ça localement, pour éviter trop de <code>git push</code>, <code>rollout</code> et <code>CrashLoopBackoff</code> 😅. Etant sous MacOS, l'install se fait facilement avec <code>brew</code> :</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="ln">1</span><span class="cl">$ brew tap vectordotdev/brew &amp;&amp; brew install vector
</span></span><span class="line"><span class="ln">2</span><span class="cl">$ vector --version
</span></span><span class="line"><span class="ln">3</span><span class="cl">vector 0.49.0 (aarch64-apple-darwin dc7e792 2025-08-12 13:47:08.632326804)
</span></span></code></pre></div><p>Ensuite, il faut :</p>
<ol>
<li>
<p>Récupérer l'URL websocket</p>
<ul>
<li>Depuis l'interface OVH, allez dans l'onglet &quot;Flux de Données&quot;, &quot;Suivre en direct&quot;</li>
<li>Puis cliquer sur &quot;Actions&quot;, &quot;Copier l'adresse WebSocket&quot;</li>
<li>L'adresse sera de type <code>wss://gra1.logs.ovh.com/tail/?tk=abcdefghijklmnopqrstuvwxyz</code></li>
</ul>
</li>
<li>
<p>Paramétrer Vector, en éditant un fichier <code>vector.yaml</code></p>
<ul>
<li>Définir un objet <code>source</code> de type <code>websocket</code></li>
<li>Définir les <code>transforms</code> souhaitées et les <code>sinks</code> (destinations)</li>
</ul>
</li>
</ol>
<p>Pour l'exemple, on va simplement renvoyer sur la console :</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># vector-1.yaml</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nt">sources</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">  </span><span class="nt">ovh_ldp_source</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">    </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">websocket</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">    </span><span class="nt">uri</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;wss://gra1.logs.ovh.com/tail/?tk=abcdefghijklmnopqrstuvwxyz&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">    </span><span class="nt">tls</span><span class="p">:</span><span class="w"> </span><span class="c"># le websocket OVHCloud est en TLS, sinon vous aurez une erreur de connection ^^</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">      </span><span class="nt">enabled</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="nt">sinks</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">  </span><span class="nt">stdout_sink</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">    </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">console</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">    </span><span class="nt">inputs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">      </span>- <span class="l">ovh_ldp_source</span><span class="w">
</span></span></span></code></pre></div><p>On lance !</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="ln">1</span><span class="cl">$ vector -c vector-1.yaml
</span></span><span class="line"><span class="ln">2</span><span class="cl">2025-08-29T09:05:09.371791Z  INFO vector::app: Log level is enabled. <span class="nv">level</span><span class="o">=</span><span class="s2">&#34;info&#34;</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">2025-08-29T09:05:09.373257Z  INFO vector::app: Loading configs. <span class="nv">paths</span><span class="o">=[</span><span class="s2">&#34;vector-1.yaml&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">2025-08-29T09:05:09.575776Z  INFO vector::topology::running: Running healthchecks.
</span></span><span class="line"><span class="ln">5</span><span class="cl">2025-08-29T09:05:09.575874Z  INFO vector: Vector has started. <span class="nv">debug</span><span class="o">=</span><span class="s2">&#34;false&#34;</span> <span class="nv">version</span><span class="o">=</span><span class="s2">&#34;0.49.0&#34;</span> <span class="nv">arch</span><span class="o">=</span><span class="s2">&#34;aarch64&#34;</span> <span class="nv">revision</span><span class="o">=</span><span class="s2">&#34;dc7e792 2025-08-12 13:47:08.632326804&#34;</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">2025-08-29T09:05:09.575918Z  INFO vector::topology::builder: Healthcheck passed.
</span></span><span class="line"><span class="ln">7</span><span class="cl">2025-08-29T09:05:09.575929Z  INFO vector::app: API is disabled, <span class="nb">enable</span> by setting <span class="sb">`</span>api.enabled<span class="sb">`</span> to <span class="sb">`</span><span class="nb">true</span><span class="sb">`</span> and use commands like <span class="sb">`</span>vector top<span class="sb">`</span>.
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="o">{</span><span class="s2">&#34;message&#34;</span>:<span class="s2">&#34;{\&#34;message\&#34;:\&#34;{\\\&#34;_audit_apiVersion\\\&#34;:\\\&#34;audit.k8s.io/v1\\\&#34;,\\\&#34;_audit_objectNamespace\\\&#34;:\\\&#34;default\\\&#34;,\\\&#34;_audit_authorizationReason\\\&#34;:\\\&#34;\\\&#34;,\\\&#34;_audit_requestReceivedTimestamp_date\\\&#34;:\\\&#34;2025-08-29T09:05:13.486642Z\\\[...]&#34;</span><span class="o">}</span>
</span></span><span class="line"><span class="ln">9</span><span class="cl"><span class="o">{</span><span class="s2">&#34;message&#34;</span>:<span class="s2">&#34;{\&#34;message\&#34;:\&#34;{\\\&#34;_audit_apiVersion\\\&#34;:\\\&#34;audit.k8s.io/v1\\\&#34;,\\\&#34;_audit_objectNamespace\\\&#34;:\\\&#34;kyverno\\\&#34;,\\\&#34;_audit_authorizationReason\\\&#34;:\\\&#34;RBAC: allowed by RoleBinding \\\\\\\&#34;kyverno:reports-controller/kyverno\\\\\\\&#34; of Role[...]&#34;</span><span class="o">}</span>
</span></span></code></pre></div><p><strong>Cool</strong>, j'arrive à récupérer les logs, et vais pouvoir les envoyer sur ma propre stack et/ou ailleurs, au choix !! On va quand même faire un peu de ménage dans le format des messages, y a du redondant. Ce qui nous donne le fichier de conf suivant :</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># vector-2.yaml</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nt">sources</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">  </span><span class="nt">ovh_ldp_source</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">    </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">websocket</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">    </span><span class="nt">uri</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;wss://gra1.logs.ovh.com/tail/?tk=abcdefghijklmnopqrstuvwxyz&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">    </span><span class="nt">tls</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">      </span><span class="nt">enabled</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">    </span><span class="nt">decoding</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">      </span><span class="nt">codec</span><span class="p">:</span><span class="w"> </span><span class="l">json</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="nt">transforms</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">  </span><span class="nt">ovh_ldp_transform</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">    </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">remap</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">    </span><span class="nt">inputs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">      </span>- <span class="l">ovh_ldp_source</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">    </span><span class="nt">source</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="sd">      . = parse_json!(.message)
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="sd">      . = parse_json!(.short_message)</span><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="nt">sinks</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w">  </span><span class="nt">stdout_sink</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w">    </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">console</span><span class="w">
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w">    </span><span class="nt">inputs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="w">      </span>- <span class="l">ovh_ldp_transform</span><span class="w">
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="w">    </span><span class="nt">encoding</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="w">      </span><span class="nt">codec</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;json&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="w">      </span><span class="nt">json</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="w">        </span><span class="nt">pretty</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="ln"> 1</span><span class="cl">$ vector -c vector-2.yaml
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">2025-08-29T09:20:09.555364Z  INFO vector::app: Log level is enabled. <span class="nv">level</span><span class="o">=</span><span class="s2">&#34;info&#34;</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">2025-08-29T09:20:09.559014Z  INFO vector::app: Loading configs. <span class="nv">paths</span><span class="o">=[</span><span class="s2">&#34;vector-2.yaml&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">2025-08-29T09:20:09.787928Z  INFO vector::topology::running: Running healthchecks.
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">2025-08-29T09:20:09.789154Z  INFO vector::topology::builder: Healthcheck passed.
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">2025-08-29T09:20:09.789801Z  INFO vector: Vector has started. <span class="nv">debug</span><span class="o">=</span><span class="s2">&#34;false&#34;</span> <span class="nv">version</span><span class="o">=</span><span class="s2">&#34;0.49.0&#34;</span> <span class="nv">arch</span><span class="o">=</span><span class="s2">&#34;aarch64&#34;</span> <span class="nv">revision</span><span class="o">=</span><span class="s2">&#34;dc7e792 2025-08-12 13:47:08.632326804&#34;</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">2025-08-29T09:20:09.790055Z  INFO vector::app: API is disabled, <span class="nb">enable</span> by setting <span class="sb">`</span>api.enabled<span class="sb">`</span> to <span class="sb">`</span><span class="nb">true</span><span class="sb">`</span> and use commands like <span class="sb">`</span>vector top<span class="sb">`</span>.
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="o">{</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  <span class="s2">&#34;annotations&#34;</span>: <span class="o">{</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="s2">&#34;authorization.k8s.io/decision&#34;</span>: <span class="s2">&#34;allow&#34;</span>,
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="s2">&#34;authorization.k8s.io/reason&#34;</span>: <span class="s2">&#34;RBAC: allowed by RoleBinding \&#34;kyverno:background-controller/kyverno\&#34; of Role \&#34;kyverno:background-controller\&#34; to ServiceAccount \&#34;kyverno-background-controller/kyverno\&#34;&#34;</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">  <span class="o">}</span>,
</span></span><span class="line"><span class="ln">13</span><span class="cl">  <span class="s2">&#34;apiVersion&#34;</span>: <span class="s2">&#34;audit.k8s.io/v1&#34;</span>,
</span></span><span class="line"><span class="ln">14</span><span class="cl">  <span class="s2">&#34;auditID&#34;</span>: <span class="s2">&#34;f861cf50-a561-4ad7-b8f0-8bbb068152d3&#34;</span>,
</span></span><span class="line"><span class="ln">15</span><span class="cl">  <span class="s2">&#34;kind&#34;</span>: <span class="s2">&#34;Event&#34;</span>,
</span></span><span class="line"><span class="ln">16</span><span class="cl">  <span class="s2">&#34;level&#34;</span>: <span class="s2">&#34;Metadata&#34;</span>,
</span></span><span class="line"><span class="ln">17</span><span class="cl">  <span class="s2">&#34;objectRef&#34;</span>: <span class="o">{</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">    <span class="s2">&#34;apiGroup&#34;</span>: <span class="s2">&#34;coordination.k8s.io&#34;</span>,
</span></span><span class="line"><span class="ln">19</span><span class="cl">    <span class="s2">&#34;apiVersion&#34;</span>: <span class="s2">&#34;v1&#34;</span>,
</span></span><span class="line"><span class="ln">20</span><span class="cl">    <span class="s2">&#34;name&#34;</span>: <span class="s2">&#34;kyverno-background-controller&#34;</span>,
</span></span><span class="line"><span class="ln">21</span><span class="cl">    <span class="s2">&#34;namespace&#34;</span>: <span class="s2">&#34;kyverno&#34;</span>,
</span></span><span class="line"><span class="ln">22</span><span class="cl">    <span class="s2">&#34;resource&#34;</span>: <span class="s2">&#34;leases&#34;</span>,
</span></span><span class="line"><span class="ln">23</span><span class="cl">    <span class="s2">&#34;resourceVersion&#34;</span>: <span class="s2">&#34;37003755025&#34;</span>,
</span></span><span class="line"><span class="ln">24</span><span class="cl">    <span class="s2">&#34;uid&#34;</span>: <span class="s2">&#34;9fca9fef-d75f-4342-a69f-eada6f93de0f&#34;</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">  <span class="o">}</span>,
</span></span><span class="line"><span class="ln">26</span><span class="cl">  <span class="o">[</span>...<span class="o">]</span>
</span></span></code></pre></div><p>Ahh, c'est beaucoup mieux 👍 !</p>
<h2 id="la-suite">La suite</h2>
<p>On a pu voir comment récupérer les logs de la &quot;Logs Data Plaform&quot; d'OVHCloud via <code>websocket</code>, grâce notamment à l'outil Vector.</p>
<p>Ca marche très bien en local, maintenant à vous de configurer vos <code>transforms</code> et <code>sinks</code> pour traiter vos logs comme bon vous semble.</p>
<p>De mon côté, c'est déployé via chart Helm en mode <code>Aggregator</code> sur mes clusters, avec quelques <code>transforms</code> nécessaires pour coller avec d'autres logs envoyés dans <a href="https://grafana.com/docs/loki/latest/">Loki</a>.</p>
<p>Amusez-vous bien avec tous ces nouveaux logs !</p>]]></content:encoded></item><item><title>Quelques Exporters Prometheus pratiques</title><link>https://www.vrchr.fr/posts/2022/12/30/quelques-exporters-prometheus-pratiques/</link><pubDate>Fri, 30 Dec 2022 00:00:00 +0000</pubDate><guid>https://www.vrchr.fr/posts/2022/12/30/quelques-exporters-prometheus-pratiques/</guid><description>Quelques exporters prometheus pratiques pour administrer un cluster Kubernetes</description><content:encoded><![CDATA[<p>Suite à un mini thread <a href="https://twitter.com/rverchere/status/1606237656865771520">Twitter</a>, je mets par écrit ici quelques lignes sur les exporters Prometheus qui peuvent s'avérer pratiques pour administrer un cluster Kubernetes.</p>
<h2 id="x509-certificate-exporter">X509 Certificate Exporter</h2>
<p>Proposé par la société <a href="https://enix.io/fr/">Enix</a>, l'exporter <a href="https://github.com/enix/x509-certificate-exporter">x509-certificate-exporter</a> permet de suivre les certificats x509 de vos applications, et être notifié sur l'expiration de ceux-ci.</p>
<p>J'utilise en général <a href="https://cert-manager.io/">cert-manager</a> et <a href="https://letsencrypt.org/">Let's Encrypt</a>, qui permet d'automatiser la création et renouvellement de certificats, surtout au niveau ingress et HTTPS, mais cela n'empêche pas d'avoir des alertes sur le renouvellement des certificats :</p>
<ul>
<li>Le challenge Let's Encrypt qui échoue pour diverses raisons (principalement des erreurs réseaux).</li>
<li>Un secret Kubernetes qui contient un ancien certificat qui n'est pas nettoyé.</li>
</ul>
<p>Un dashboard est même fourni avec, que vous pourrez modifier selon vos envies :</p>
<p><img alt="x509 dashboard" class="zoomable" decoding="async" loading="lazy" src="https://raw.githubusercontent.com/enix/x509-certificate-exporter/main/docs/grafana-dashboard.jpg"></p>
<h2 id="helm-exporter">Helm Exporter</h2>
<p>J'utilise beaucoup de charts Helm pour déployer les applications sur Kubernetes, surtout les briques d'infrastructure (prometheus, nginx, cert-manager, velero, etc.).
G
Je souhaitais alors pouvoir suivre les versions de charts Helm sur l'ensemble des clusters administrés, et je suis tombé sur 2 outils:</p>
<ol>
<li>
<p><a href="https://nova.docs.fairwinds.com/">Nova</a> de la société Fairwinds, un outil en CLI qui permet de lister les charts dépréciés. L'outil est pratique, mais difficile d'avoir une visualisation globale, ainsi qu'un tableau de bord</p>
</li>
<li>
<p>L'exporter <a href="https://github.com/sstarcher/helm-exporter">helm-exporter</a> qui va générer des métriques Prometheus pour les charts installés au sein du cluster, avec les versions également dépréciées.</p>
</li>
</ol>
<p>Je me suis arrêté sur ce deuxième choix, car il est ainsi facile d'avoir un dashboard dans Grafana.</p>
<p>La configuration est un tout petit peu &quot;pénible&quot;, dans le sens où il faut définir la source de chaque chart (le tout est expliqué sur le dépôt GitHub).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="nt">config</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w">  </span><span class="nt">helmRegistries</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">    </span><span class="nt">override</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">      </span>- <span class="nt">registry</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">          </span><span class="nt">url</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;https://charts.jetstack.io&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">        </span><span class="nt">charts</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">          </span>- <span class="s2">&#34;cert-manager&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">        </span><span class="nt">allowAllReleases</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">      </span>- <span class="nt">registry</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">          </span><span class="nt">url</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;https://charts.gitlab.io&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">        </span><span class="nt">charts</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">          </span>- <span class="s2">&#34;gitlab-runner&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">        </span><span class="nt">allowAllReleases</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">      </span>- <span class="nt">registry</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">          </span><span class="nt">url</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;https://shanestarcher.com/helm-charts&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">        </span><span class="nt">charts</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">          </span>- <span class="s2">&#34;helm-exporter&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">        </span><span class="nt">allowAllReleases</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">      </span>- <span class="nt">registry</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">          </span><span class="nt">url</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;https://kubernetes.github.io/ingress-nginx&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w">        </span><span class="nt">charts</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w">          </span>- <span class="s2">&#34;ingress-nginx&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w">        </span><span class="nt">allowAllReleases</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="p">[</span><span class="l">...]</span><span class="w">
</span></span></span></code></pre></div><p>Ainsi, on peut voir que mes clusters ont bien le chart précédemment installé à jour ;)</p>
<p><img alt="helm exporter" class="zoomable" decoding="async" loading="lazy" src="/2022/12/2022-12-30-prom-exporters-helm.webp"></p>
<h2 id="blackbox-exporter">Blackbox Exporter</h2>
<p>On revient aux fondamentaux, le <a href="https://github.com/prometheus/blackbox_exporter">blackbox_exporter</a> reste un exporter très utile lorsqu'on doit monitorer des sites ou équipements distants, ne répondant qu'en HTTP(S), DNS, TCP.</p>
<p>Je l'utilise surtout pour avoir les temps de réponse des sites déployés, depuis un site tiers.</p>
<p><img alt="website metrics" class="zoomable" decoding="async" loading="lazy" src="/2022/12/2022-12-30-prom-exporters-blackbox.webp"></p>
<h2 id="harbor-exporter">Harbor Exporter</h2>
<p>Un petit dernier, quand on est en environnement conteneurs et qu'on gère une registry (Harbor notamment), on peut suivre son état avec celui-ci. Il existe l'exporter <a href="https://github.com/c4po/harbor_exporter">harbor_exporter</a>.</p>
<p>Je l'utilise assez peu, mais cela me permet de voir quels projets consomment le plus de place quand il faut y faire du ménage (il est si facile de créer des images Docker...).</p>
<p><img alt="harbor exporter" class="zoomable" decoding="async" loading="lazy" src="https://raw.githubusercontent.com/c4po/harbor_exporter/master/grafana/screenshot.png"></p>
<h2 id="prometheus-ms-teams">Prometheus MS Teams</h2>
<p>Ce n'est pas un exporter, mais je note quand même l'outil <a href="https://github.com/prometheus-msteams/prometheus-msteams">prometheus-msteams</a>, qui est un serveur web léger en Go qui permet de récupérer des alertes générées via webhook pour les transmettre à un canal MS Teams.</p>
<p><img alt="promteams" class="zoomable" decoding="async" loading="lazy" src="https://raw.githubusercontent.com/prometheus-msteams/prometheus-msteams/master/docs/promteams.png"></p>
<p>Testé chez moi avec succès !</p>
<h2 id="cabourotte--appclacks">Cabourotte &amp; Appclacks</h2>
<p>Si cela nous vous suffit pas, allez voir aussi ce que fait <a href="https://www.mcorbin.fr/">@mcorbin</a>, avec <a href="https://www.doc.appclacks.com/">Appclacks</a> et sa sonde health-check <a href="https://www.cabourotte.appclacks.com/">cabourotte</a>. Projet prometteur !</p>
<p>Les <em>probes</em> se pilotent via API au travers d'une CLI, et il existe même un provider <a href="https://github.com/appclacks/terraform-provider-appclacks">Terraform</a>. On peut soit utiliser les sondes fournies, soit ses propres sondes avec cabourotte, qui ira récupérer sa config auprès du serveur central.</p>
<h2 id="dautres-ressources">D'autres ressources</h2>
<p>Dernier petit lien, si vous devez administrer un serveur Mastodon (rien à voir avec Kubernetes, certes), allez voir <a href="https://medium.com/@dma42/observing-hachyderms-24e0f35529a5">ici</a>.</p>]]></content:encoded></item><item><title>Kubernetes Nodes Logs avec Promtail</title><link>https://www.vrchr.fr/posts/2022/06/30/promtail-daemonsets-nodes/</link><pubDate>Thu, 30 Jun 2022 00:00:00 +0000</pubDate><guid>https://www.vrchr.fr/posts/2022/06/30/promtail-daemonsets-nodes/</guid><description>Utilisez les daemonsets Promtail pour récupérer les logs des nodes Kubernetes</description><content:encoded><![CDATA[<p><a href="https://grafana.com/docs/loki/latest/clients/promtail/">Promtail</a> est un agent applicatif permettant de transmettre des logs vers une instance <a href="https://grafana.com/docs/loki/latest/">Loki</a>, outil de gestion de logs de la société Grafana Labs. L'outil fonctionne très bien en environnement Kubernetes, récupérant l'ensemble des logs des conteneurs, avec des notions d'auto-discovery et gestion de labels.</p>
<p>Il est très simple à installer en tant que daemonset dans un cluster, pour y récupérer les logs des conteneurs. Mais parfois on a besoin de récupérer également les logs des nodes du cluster, sur lesquels tournent ces conteneurs.</p>
<p>Ci-après une explication rapide pour la mise en place d'un tel setup.</p>
<p><img alt="Promtail logs with Loki &amp; Grafana" class="zoomable" decoding="async" loading="lazy" src="/2022/06/2022-06-30-promtail-logs.webp"></p>
<h2 id="installation-simple">Installation simple</h2>
<p><strong>Note</strong>: Cet article utilise Promtail v2.5.0, la configuration présentée peut différer selon la version que vous utiliserez.</p>
<p>Par défaut, l'installation de Promtail se fait avec un chart Helm, qui fonctionne &quot;out-of-the-box&quot;. On considère que Loki et Grafana sont déjà déployés dans le cluster.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="ln">1</span><span class="cl">$ helm repo add grafana https://grafana.github.io/helm-charts
</span></span><span class="line"><span class="ln">2</span><span class="cl">$ helm repo update
</span></span><span class="line"><span class="ln">3</span><span class="cl">$ helm --install --namespace observability --create-namespace promtail grafana/promtail
</span></span></code></pre></div><p>On peut voir maintenant que les agents Promtail tournent sur les nodes, et que les logs arrivent dans Loki. Grafana permet l'exploration des logs, tout est bon.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="ln">1</span><span class="cl">$ kubectl get pods -l app.kubernetes.io/name<span class="o">=</span>promtail
</span></span><span class="line"><span class="ln">2</span><span class="cl">NAME READY STATUS RESTARTS AGE
</span></span><span class="line"><span class="ln">3</span><span class="cl">promtail-47jwn 1/1 Running <span class="m">0</span> 13m
</span></span><span class="line"><span class="ln">4</span><span class="cl">promtail-mwkcb 1/1 Running <span class="m">0</span> 14m
</span></span></code></pre></div><p><img alt="Promtail basic logs" class="zoomable" decoding="async" loading="lazy" src="/2022/06/2022-06-30-promtail-basic-logs.webp"></p>
<h2 id="paramétrage">Paramétrage</h2>
<p>Pour l'instant, on a un setup simple, mais fonctionnel.</p>
<p>Cependant, pour les besoins exprimés cela ne suffit pas, car on doit transmettre les logs du répertoire <code>/var/log/</code> des nodes vers Loki, ici plus spécifiquement le fichier <code>/var/log/syslog</code>. Pourquoi ne pas utiliser ces daemonsets pour cela ? On va configurer Promtail en ce sens.</p>
<h3 id="montage-du-dossier-hôte">Montage du dossier hôte</h3>
<p>Tout d'abord, on doit monter le dossier de l'hôte <code>/var/log</code> sur le pod. On montera le dossier dans <code>/var/log/host</code> pour éviter tout problème d'écrasement / nommage.</p>
<p>On spécifie cela dans les paramètres du chart Helm, dans un fichier <code> custom-values.yaml</code> :</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># Mount folder /var/log from node</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nt">extraVolumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">  </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">node-logs</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">    </span><span class="nt">hostPath</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">      </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">/var/log</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nt">extraVolumeMounts</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">  </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">node-logs</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">    </span><span class="nt">mountPath</span><span class="p">:</span><span class="w"> </span><span class="l">/var/log/host</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">    </span><span class="nt">readOnly</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span></code></pre></div><p>On upgrade le chart helm, et le dossier <code>/var/log/host</code> est bien là, parfait !</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="ln">1</span><span class="cl">$ helm upgrade --install --namespace observability promtail grafana/promtail -f custom-values.yaml
</span></span><span class="line"><span class="ln">2</span><span class="cl">$ kubectl <span class="nb">exec</span> -it promtail-mwkcb --mount <span class="p">|</span> grep /var/log/host
</span></span><span class="line"><span class="ln">3</span><span class="cl">/dev/sda1 on /var/log/host <span class="nb">type</span> ext4 <span class="o">(</span>ro,relatime,data<span class="o">=</span>ordered<span class="o">)</span>
</span></span></code></pre></div><p>Avant d'aller plus loin, voyons si l'on peut lire les fichiers à l'intérieur :</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="ln">1</span><span class="cl">$ kubectl <span class="nb">exec</span> -it promtail-mwkcb --tail /var/log/host/syslog
</span></span><span class="line"><span class="ln">2</span><span class="cl">tail: cannot open ‘/var/log/host/syslog’ <span class="k">for</span> reading: Permission denied
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="nb">command</span> terminated with <span class="nb">exit</span> code <span class="m">1</span>
</span></span></code></pre></div><p>Mmmm, il y a quelque chose d'incorrect ici, voyons les permissions des fichiers :</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="ln">1</span><span class="cl">$ kubectl <span class="nb">exec</span> -it promtail-mwkcb — ls -l /var/log/host/syslog
</span></span><span class="line"><span class="ln">2</span><span class="cl">-rw-r---- <span class="m">1</span> <span class="m">102</span> adm <span class="m">72327736</span> Jun <span class="m">29</span> 20:01 /var/log/host/syslog
</span></span></code></pre></div><p>Le fichier appartient à l'utilisateur avec l'ID 102 et groupe &quot;adm&quot;. Avec <code>getent</code> on a un peu plus d'infos sur ce groupe :</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="ln">1</span><span class="cl">$ kubectl <span class="nb">exec</span> -it promtail-mwkcb -- getent group adm
</span></span><span class="line"><span class="ln">2</span><span class="cl">adm:x:4:
</span></span></code></pre></div><p>Donc, si l'on veut que promtail puisse lire ce fichier, il faut ajouter des contextes de sécurité avec l'option <code>fsGroup</code>, que l'on définit dans les custom values du chart Helm :</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln">1</span><span class="cl"><span class="c"># Set fsGroup to allow syslog file reading</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nt">podSecurityContext</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">  </span><span class="nt">fsGroup</span><span class="p">:</span><span class="w"> </span><span class="m">4</span><span class="w">
</span></span></span></code></pre></div><p>On upgrade le chart Helm, et on vérifie l'accès au fichier :</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="ln">1</span><span class="cl">$ helm upgrade promtail grafana/promtail -f custom-values.yaml
</span></span><span class="line"><span class="ln">2</span><span class="cl">$ kubectl <span class="nb">exec</span> -it promtail-2dbh9 -- tail /var/log/host/syslog
</span></span><span class="line"><span class="ln">3</span><span class="cl">Jun <span class="m">29</span> 20:16:28 worker-pool-node-d5353e kubelet<span class="o">[</span>1596<span class="o">]</span>: I0629 20:16:28.374480 <span class="m">1596</span> clientconn.go:897<span class="o">]</span> blockingPicker: the picked transport is not ready, loop back to repick
</span></span><span class="line"><span class="ln">4</span><span class="cl">Jun <span class="m">29</span> 20:16:28 worker-pool-node-d5353e docker<span class="o">[</span>1888<span class="o">]</span>: I0629 20:16:28.375267 <span class="m">1</span> utils.go:81<span class="o">]</span> GRPC call: /csi.v1.Node/NodeGetVolumeStats
</span></span></code></pre></div><p>Cela semble mieux ! Maintenant on peut lire les fichiers du node depuis notre pod Promtail.</p>
<h2 id="configuration-promtail">Configuration Promtail</h2>
<p>On va ensuit configurer Promtail pour lire (scrape) ce nouveau fichier, avec un simple &quot;File Target Discovery&quot; (voir <a href="https://grafana.com/docs/loki/latest/clients/promtail/scraping/#file-target-discovery">ici</a>). On y rajoute quelques labels pour les retrouver facilement dans Loki plus tard. Toujours dans les custom values :</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># Scrape config to read syslog file from node</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nt">config</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">  </span><span class="nt">snippets</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">    </span><span class="nt">extraScrapeConfigs</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="sd">      # Add an additional scrape config for syslog
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="sd">      - job_name: node-syslog
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="sd">        static_configs:
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="sd">        - targets:
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="sd">          - localhost
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="sd">          labels:
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="sd">            job: node/syslog
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="sd">            __path__: /var/log/host/syslog</span><span class="w">
</span></span></span></code></pre></div><p>On upgrade une fois de plus le chart, et on peut maintenant voir les logs syslog du node, en plus des logs des conteneurs, être transmis dans Loki, nickel !</p>
<p><img alt="Nodes logs with Promtail" class="zoomable" decoding="async" loading="lazy" src="/2022/06/2022-06-30-promtail-nodes-logs.webp"></p>
<h2 id="ajout-de-labels-dynamiques">Ajout de labels dynamiques</h2>
<p>Les logs sont là, mais avoir des labels supplémentaires ne serait pas de refus, comme le nom du node.</p>
<p>On va alors utiliser l'option <code>-config.expand-env</code> qui récupère les variables d'environnement, et on va les rajouter dans la configuration de Promtail :</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># Allow environment variables usage</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nt">extraArgs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">  </span>- -<span class="l">config.expand-env=true</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c"># Scrape config to read syslog file from node</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="nt">config</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">  </span><span class="nt">snippets</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">    </span><span class="nt">extraScrapeConfigs</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="sd">      # Add an additional scrape config for syslog
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="sd">      - job_name: node-syslog
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="sd">        static_configs:
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="sd">        - targets:
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="sd">          - localhost
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="sd">          labels:
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="sd">            job: node/syslog
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="sd">            __path__: /var/log/host/syslog
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="sd">            node_name: &#39;${HOSTNAME}&#39;</span><span class="w">
</span></span></span></code></pre></div><p>On upgrade une dernière fois le chart, on vérifie les logs dans Grafana... Et voilà ! On a bien les logs des nodes, avec les labels qui vont bien !</p>
<p><img alt="Labelled nodes logs with Promtail" class="zoomable" decoding="async" loading="lazy" src="/2022/06/2022-06-30-promtail-nodes-logs-labels.webp"></p>
<h2 id="récapitulatif">Récapitulatif</h2>
<p>Pour résumer, afin d'avoir les logs des nodes dans Loki en utilisant Promtail, il &quot;suffit&quot; de :</p>
<ol>
<li>Monter le dossier <code>/var/log</code> du node dans le pod Promtail</li>
<li>Utiliser le bon <code>fsGroup</code> pour pouvoir lire les fichiers</li>
<li>Ajouter une configuration de scraping <code>static_configs</code></li>
<li>Utiliser les variabels d'environnement pour ajouter des labels</li>
</ol>
<p>On retrouvera le fichier <code>custom-values.yaml</code> pour déployer le chart Helm dans mon <a href="https://gitlab.com/rverchere/promtail-host-logs">dépôt GitLab</a>.</p>
<p>On peut maintenant fouiner dans les logs, vérifier ce qu'il ne va pas sur les nodes, et plus encore... Bonne recherche !</p>]]></content:encoded></item><item><title>Requests &amp; Limits avec VPA, Goldilocks et Grafana</title><link>https://www.vrchr.fr/posts/2022/05/16/vpa-goldilocks/</link><pubDate>Mon, 16 May 2022 00:00:00 +0000</pubDate><guid>https://www.vrchr.fr/posts/2022/05/16/vpa-goldilocks/</guid><description>Utilisez les Vertical Pod Autoscaler, Goldilocks et Grafana pour optimiser les ressources d&amp;#39;un cluster Kubernetes</description><content:encoded><![CDATA[<p>Lorsqu'on développe des applications Cloud Native qui seront hébergées sur Kubernetes, on doit toujours paramétrer et optimiser l'utilisation du CPU et RAM des déploiements.</p>
<p>Si cela n'est pas fait, on peut arriver dans des situations délicates, comme ne pas avoir assez de ressources disponibles si un serveur (<em>node</em>) est indisponible, quelques process <em>OOMKilled</em> à cause de limites de mémoire trop restrictives, des pods non schedulés, etc. De plus, par défaut, certaines valeurs de déploiements ne sont pas toujours alignées avec l'utilisation que l'on en a (comme certains charts Helm publics).</p>
<p>Alors, comment valider nos déploiements, et adapter les Requests &amp; Limits pour coller au mieux à l'utilisation que l'on en a ? Cela peut être fait avec un mix d'outils Open Source, que l'on verra dans cet article.</p>
<h1 id="vertical-pod-autoscaler">Vertical Pod Autoscaler</h1>
<p>Kubernetes Vertical Pod Autoscaler permet de suivre l'utilisation CPU et RAM des pods d'un cluster, et les adapter automatiquement si besoin. On retrouvera plus d'information ici : <a href="https://github.com/kubernetes/autoscaler/tree/master/vertical-pod-autoscaler">https://github.com/kubernetes/autoscaler/tree/master/vertical-pod-autoscaler</a></p>
<p>Le VPA a 3 composants: le <strong>Recommender</strong>, l'<strong>Updater</strong> et l'<strong>Admission Plugin</strong>. Dans notre cas, on ne souhaite avoir que des recommendations, alors on n'utilisera que le <strong>Recommender</strong>.</p>
<p>L'installation peut se faire de plusieurs façons, mais on utilisera ici le chart Helm fourni par <a href="https://github.com/FairwindsOps/charts/tree/master/stable/vpa">Fairwinds</a>.</p>
<p>Comme l'on utilise Prometheus comme outil de monitoring, on s'en servira plus tard de source pour récupérer les métriques VPA. On active cela dans les valeurs du chart Helm :</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="ln">1</span><span class="cl">$ helm repo add fairwinds-stable https://charts.fairwinds.com/stable
</span></span><span class="line"><span class="ln">2</span><span class="cl">$ helm repo update
</span></span><span class="line"><span class="ln">3</span><span class="cl">$ helm install vpa fairwinds-stable/vpa --namespace vpa --create-namespace -f vpa-custom-values.yaml # See below
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="nt">admissionController</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w">  </span><span class="nt">enabled</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nt">recommender</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">  </span><span class="nt">enabled</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">  </span><span class="nt">extraArgs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">    </span><span class="nt">prometheus-address</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="sd">      http://prometheus-operated.observability.svc.cluster.local:9090</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">    </span><span class="nt">storage</span><span class="p">:</span><span class="w"> </span><span class="l">prometheus</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="nt">updater</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">  </span><span class="nt">enabled</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span></span></span></code></pre></div><p>Comme dit plus haut, Prometheus est utilisé ici comme fournisseur de métriques, pour avoir des informations plus pertinentes, et permettant d'avoir un historique plus grand. On ne couvrira pas l'installation de l'outil dans cet article.</p>
<p>Maintenant qu'on a le VPA fonctionnel (du moins, le recommendeur), on peut créer une configuration VPA. Un exemple est disponible <a href="https://github.com/kubernetes/autoscaler/tree/master/vertical-pod-autoscaler#example-vpa-configuration">ici</a>.</p>
<p>La définition d'un VPA est assez simple, mais souhaitant plus d'automatisation, on va utiliser <a href="https://www.fairwinds.com/goldilocks">Goldilocks</a> !</p>
<h2 id="goldilocks">Goldilocks</h2>
<p>Goldilocks est un utilitaire de la société <a href="https://www.fairwinds.com/">Fairwinds</a>, qui va permettre de gérer les requêtes et limites simplement, basé sur les VPA, comme précédemment installé.</p>
<p>L'outil va vérifier les déploiements, et automatiquement créer les VPA en fonction de labels. Donc si l'on souhaite activer les VPA sur un déploiement, un namespace ou une autre resource Kubernetes, il suffit d'y ajouter un label, et Goldilocks fera le reste !</p>
<p>L'installation se fait avec un chart Helm :</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="ln">1</span><span class="cl">$ <span class="c1"># Goldilocks Helm repository is already there</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">$ helm install goldilocks --namespace vpa fairwinds-stable/goldilocks
</span></span></code></pre></div><p>Une fois Goldilocks déployé, on retrouve 3 pods :</p>
<ol>
<li>Goldilocks <strong>controller</strong>, qui scrute les labels et créer les VPA en conséquence.</li>
<li>Goldilocks <strong>dashboard</strong>, qui permet d'avoir un dashboard standalone pour voir les reommendations VPA.</li>
<li>VPA <strong>recommender</strong>, qui calcule les recommendations sur les Requests et Limits CPU et RAM.</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="ln">1</span><span class="cl">$ kubectl get pods
</span></span><span class="line"><span class="ln">2</span><span class="cl">NAME                          READY   STATUS    RESTARTS   AGE
</span></span><span class="line"><span class="ln">3</span><span class="cl">goldilocks-controller-5bf99   1/1     Running   <span class="m">0</span>          49d
</span></span><span class="line"><span class="ln">4</span><span class="cl">goldilocks-dashboard-699f4    1/1     Running   <span class="m">0</span>          49d
</span></span><span class="line"><span class="ln">5</span><span class="cl">vpa-recommender-74fdc         1/1     Running   <span class="m">0</span>          67d
</span></span></code></pre></div><p>Maintenant, il suffit de rajouter le label qui va bien (<code>goldilocks.fairwinds.com/enabled=true</code>) sur un namespace pour voir la magie opérer : on ajoute ici les labels sur le namespace &quot;velero&quot;, et les VPA seront automatiquement créés :</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="ln">1</span><span class="cl">$ kubectl label ns velero goldilocks.fairwinds.com/enabled<span class="o">=</span><span class="nb">true</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">$ kubectl get namespace velero --show-labels                  
</span></span><span class="line"><span class="ln">3</span><span class="cl">NAME     STATUS   AGE    LABELS
</span></span><span class="line"><span class="ln">4</span><span class="cl">velero   Active   112d   goldilocks.fairwinds.com/enabled<span class="o">=</span>true,name<span class="o">=</span>velero
</span></span><span class="line"><span class="ln">5</span><span class="cl">
</span></span><span class="line"><span class="ln">6</span><span class="cl">$ kubectl get vpa -n velero                 
</span></span><span class="line"><span class="ln">7</span><span class="cl">NAME                MODE   CPU   MEM         PROVIDED   AGE
</span></span><span class="line"><span class="ln">8</span><span class="cl">goldilocks-restic   Off    15m   <span class="m">183046954</span>   True       67d
</span></span><span class="line"><span class="ln">9</span><span class="cl">goldilocks-velero   Off    11m   <span class="m">183046954</span>   True       67d
</span></span></code></pre></div><p><em><strong>Note:</strong></em> Fairwinds propose aussi un chart Helm qui installe les VPA et Goldilocks en même temps, mais ne le recommendent pas.</p>
<p>Maintenant que l'on a nos VPAs créé automatiquement, on peut voir les recommendations sur le dashboard dédié !</p>
<p><img alt="Goldilocks Dashboard" class="zoomable" decoding="async" loading="lazy" src="/2022/05/2022-05-16-vpa-goldilocks-dashboard.webp"></p>
<p>OK, c'est très bien, mais on a généralement déjà de beaux tableaux de bord avec des outils de monitoring comme le couple Prometheus &amp; Grafana. Pourquoi ne pas les utiliser au lieu du dashboard dédié ? On y va !</p>
<h2 id="prometheus--grafana">Prometheus &amp; Grafana</h2>
<p>En cherchant un peu dans les paramètres des VPA et les métriques disponibles, on peut voir que <code>kube-state-metrics</code> expose les informations suivantes :</p>
<ul>
<li><code>kube_verticalpodautoscaler_status_recommendation_containerrecommendations_lowerbound</code></li>
<li><code>kube_verticalpodautoscaler_status_recommendation_containerrecommendations_upperbound</code></li>
</ul>
<p>Toutes ces métriques sont définies <a href="https://github.com/kubernetes/kube-state-metrics/blob/master/docs/verticalpodautoscaler-metrics.md">ici</a>.</p>
<p>Si on utilise l'opérateur Prometheus, il faudra le mettre à jour pour explicitement activer le collecteur <strong>verticalpodautoscalers</strong>  dans le déploiement <code>kube-state-metrics</code>.</p>
<p><img alt="Prometheus VPA Metrics" class="zoomable" decoding="async" loading="lazy" src="/2022/05/2022-05-16-vpa-goldilocks-prom.webp"></p>
<p>On a maintenant les VPA, les métriques Prometheus, on peut donc arêter le dashboard Goldilocks et créer le notre dans Grafana, et récupérant les bonnes métriques !</p>
<ol>
<li>On va donc mettre à jour le déploiement de Goldilocks pour désactiver le dashboard :</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="ln">1</span><span class="cl">$ helm upgrade goldilocks --namespace vpa fairwinds-stable/goldilocks -f goldilocks-custom-values.yaml
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln">1</span><span class="cl"><span class="nt">dashboard</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w">  </span><span class="nt">enabled</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span></span></span></code></pre></div><ol start="2">
<li>On crée un dashboard Grafana !</li>
</ol>
<p><img alt="Grafana VPA Recommendations" class="zoomable" decoding="async" loading="lazy" src="/2022/05/2022-05-16-vpa-goldilocks.webp"></p>
<p>Cool ! On peut maintenant suivre et adapter les Requests &amp; Limits de nos applications depuis notre outil de dashboard préféré, et adapter les différents seuils pour s'adapter au cluster. On peut aussi étendre l'historique des recommendations, pour mieux adapter les valeurs.</p>
<p>Un exemple de tableau de bord est disponible ici : <a href="https://grafana.com/grafana/dashboards/16294">https://grafana.com/grafana/dashboards/16294</a></p>
<p>Fun fact, j'ai utilisé Goldilocks pour adapter les Requests &amp; Limits de Goldilocks lui-même ;)</p>
<h2 id="ressources">Ressources</h2>
<p>On peut récupérer un autre tableau de bord <a href="https://grafana.com/grafana/dashboards/14588">ici</a>, lequel j'ai utilisé comme source pour crée le mien.</p>]]></content:encoded></item></channel></rss>