<?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>Theforge on Valérian Pyckaert</title>
    <link>https://valerian-pyckaert.dev/categories/theforge/</link>
    <description>Recent content in Theforge on Valérian Pyckaert</description>
    <image>
      <title>Valérian Pyckaert</title>
      <url>https://valerian-pyckaert.dev/images/default-cover.svg</url>
      <link>https://valerian-pyckaert.dev/images/default-cover.svg</link>
    </image>
    <generator>Hugo</generator>
    <language>en</language>
    <lastBuildDate>Wed, 03 Jun 2026 19:00:00 +0200</lastBuildDate>
    <atom:link href="https://valerian-pyckaert.dev/categories/theforge/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>TheForge #4 — Installation de Nginx Gateway Fabric &amp; Cert manager pour exposer les services sur Kubernetes</title>
      <link>https://valerian-pyckaert.dev/posts/the-forge-gateway/</link>
      <pubDate>Wed, 03 Jun 2026 19:00:00 +0200</pubDate>
      <guid>https://valerian-pyckaert.dev/posts/the-forge-gateway/</guid>
      <description>Apprenez à installer Nginx Gateway Fabric pour exposer vos services sur Kubernetes.</description>
      <content:encoded><![CDATA[<h2 id="ce-quon-va-faire">Ce qu&rsquo;on va faire</h2>
<blockquote>
<p>Nous allons installer Nginx Gateway Fabric pour exposer nos services Kubernetes sur internet, ainsi que Cert Manager pour gérer les certificats TLS.</p>
</blockquote>
<h2 id="architecture-cible">Architecture cible</h2>
<p>Je possède un nom de domaine chez Cloudflare, je vais donc utiliser Cloudflare comme DNS provider pour gérer les enregistrements DNS de mon domaine. Nginx Gateway Fabric sera configuré pour utiliser un LoadBalancer de type External, ce qui me permettra d&rsquo;obtenir une adresse IP publique pour exposer mes services.</p>
<p>Voici un schéma pour une meilleur compréhension :</p>
<pre class="mermaid">
  graph TD
    A[Internet] --&gt; B[Cloudflare DNS + Proxy]
    B --&gt; C[Enregistrement DNS pointant vers l&#39;IP du LoadBalancer GCP]
    C --&gt; D[LoadBalancer GCP]
    D --&gt; E[Nginx Gateway Fabric sur Kubernetes]
    E --&gt; F[HTTPRoute -&gt; Services Kubernetes]
    F --&gt; G[Cert Manager pour TLS]
</pre>

<hr>
<h2 id="étapes">Étapes</h2>
<h3 id="1-installation-de-la-crd-gateway-api">1. Installation de la CRD gateway API</h3>
<p>Les ingress controller ne sont plus supportés, il faut désormais ce tourner vers les gateway API. Il existe plusieurs implémentations, j&rsquo;ai choisi d&rsquo;installer Nginx Gateway Fabric qui est une solution robuste et bien supportée.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.4.0/standard-install.yaml
</span></span></code></pre></div><p>ou via ArgoCD (la sync-wave à 0 permet de s&rsquo;assurer que les CRDs sont installées avant les autres ressources) :</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">argoproj.io/v1alpha1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Application</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">gateway-api-crds</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">argocd</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">finalizers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">resources-finalizer.argocd.argoproj.io</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">annotations</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">argocd.argoproj.io/sync-wave</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;0&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">project</span><span class="p">:</span><span class="w"> </span><span class="l">default</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">source</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">repoURL</span><span class="p">:</span><span class="w"> </span><span class="l">https://github.com/nginx/nginx-gateway-fabric.git</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">targetRevision</span><span class="p">:</span><span class="w"> </span><span class="l">v2.6.3</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">config/crd/gateway-api/standard</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">destination</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">server</span><span class="p">:</span><span class="w"> </span><span class="l">https://kubernetes.default.svc</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">syncPolicy</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">automated</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">prune</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="cl"><span class="w">      </span><span class="nt">selfHeal</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="cl"><span class="w">    </span><span class="nt">syncOptions</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">ServerSideApply=true</span><span class="w">
</span></span></span></code></pre></div><h3 id="2-installation-de-nginx-gateway-fabric">2. Installation de Nginx Gateway Fabric</h3>
<p>Je vais aller assez vite sur l&rsquo;installation de Nginx Gateway Fabric, si vous avez suivi les articles précédents cela ne devrait pas poser de problème.</p>
<p>On ajoute dans notre repo de CD la configuration suivante :</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">argoproj.io/v1alpha1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Application</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">nginx-gateway-fabric</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">argocd</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">finalizers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">resources-finalizer.argocd.argoproj.io</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">annotations</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">argocd.argoproj.io/sync-wave</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;1&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">project</span><span class="p">:</span><span class="w"> </span><span class="l">default</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">source</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">repoURL</span><span class="p">:</span><span class="w"> </span><span class="l">ghcr.io/nginx/charts</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">chart</span><span class="p">:</span><span class="w"> </span><span class="l">nginx-gateway-fabric</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">targetRevision</span><span class="p">:</span><span class="w"> </span><span class="m">2.6.3</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">helm</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">releaseName</span><span class="p">:</span><span class="w"> </span><span class="l">nginx-gateway-fabric</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">destination</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">server</span><span class="p">:</span><span class="w"> </span><span class="l">https://kubernetes.default.svc</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">nginx-gateway</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">syncPolicy</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">automated</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">prune</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="cl"><span class="w">      </span><span class="nt">selfHeal</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="cl"><span class="w">    </span><span class="nt">syncOptions</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">CreateNamespace=true</span><span class="w">
</span></span></span></code></pre></div><p>Après quelques secondes les ressources sont créées et nous avons notre gateway qui tourne.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl get pods -n nginx-gateway
</span></span><span class="line"><span class="cl">NAME                                    READY   STATUS    RESTARTS   AGE
</span></span><span class="line"><span class="cl">nginx-gateway-fabric-77c559bb4f-c7zlb   1/1     Running   <span class="m">0</span>          93s
</span></span></code></pre></div><h3 id="3-installation-de-cert-manager">3. Installation de Cert Manager</h3>
<p>Le principe est le même, voici la configuration à appliquer :</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">argoproj.io/v1alpha1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Application</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">cert-manager</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">argocd</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">finalizers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">resources-finalizer.argocd.argoproj.io</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">annotations</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">argocd.argoproj.io/sync-wave</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;0&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">project</span><span class="p">:</span><span class="w"> </span><span class="l">default</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">source</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">repoURL</span><span class="p">:</span><span class="w"> </span><span class="l">https://charts.jetstack.io</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">chart</span><span class="p">:</span><span class="w"> </span><span class="l">cert-manager</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">targetRevision</span><span class="p">:</span><span class="w"> </span><span class="l">v1.20.2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">helm</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">releaseName</span><span class="p">:</span><span class="w"> </span><span class="l">cert-manager</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">values</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">        crds:
</span></span></span><span class="line"><span class="cl"><span class="sd">          enabled: true
</span></span></span><span class="line"><span class="cl"><span class="sd">          keep: true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">destination</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">server</span><span class="p">:</span><span class="w"> </span><span class="l">https://kubernetes.default.svc</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">cert-manager</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">syncPolicy</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">automated</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">prune</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="cl"><span class="w">      </span><span class="nt">selfHeal</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="cl"><span class="w">    </span><span class="nt">syncOptions</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">CreateNamespace=true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">ServerSideApply=true</span><span class="w">
</span></span></span></code></pre></div><p>Après quelques secondes les ressources sont créées et nous avons notre cert manager qui tourne.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl get pods -n cert-manager
</span></span><span class="line"><span class="cl">NAME                                       READY   STATUS              RESTARTS   AGE
</span></span><span class="line"><span class="cl">cert-manager-65dd558d9c-qzjw9              1/1     Running             <span class="m">0</span>          5m6s
</span></span><span class="line"><span class="cl">cert-manager-cainjector-77bd875bf4-6bcpn   1/1     Running             <span class="m">0</span>          5m5s
</span></span><span class="line"><span class="cl">cert-manager-startupapicheck-fhsng         0/1     ContainerCreating   <span class="m">0</span>          2m29s
</span></span><span class="line"><span class="cl">cert-manager-webhook-69666f4d4c-7wccg      1/1     Running             <span class="m">0</span>          5m6s
</span></span></code></pre></div><hr>
<h3 id="4-configuration-cloudflare">4. Configuration Cloudflare</h3>
<h4 id="récupération-de-lip-du-loadbalancer-gcp">Récupération de l&rsquo;IP du LoadBalancer GCP</h4>
<p>Maintenant que ma gateway est en place, je vais configurer Cloudflare pour pointer mon domaine vers l&rsquo;IP publique de mon LoadBalancer GCP. Je vais créer un enregistrement A dans Cloudflare qui pointe vers l&rsquo;IP du LoadBalancer, et je vais activer le proxy de Cloudflare pour bénéficier de la protection DDoS et du CDN.</p>
<p>Pour cela il faut récupérer l&rsquo;IP du LoadBalancer GCP qui a été créé pour exposer Nginx Gateway Fabric.
Vous pouvez le faire avec la commande suivante :</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl get svc -n nginx-gateway
</span></span><span class="line"><span class="cl"><span class="c1"># Noter l&#39;EXTERNAL-IP → à mettre dans Cloudflare DNS (enregistrement A)</span>
</span></span></code></pre></div><h4 id="création-dun-token-api-cloudflare">Création d&rsquo;un token API Cloudflare</h4>
<p>Pour automatiser la configuration de Cloudflare, je vais créer un token API avec les permissions nécessaires pour gérer les enregistrements DNS.
Dans Cloudflare → My Profile → API Tokens → Create Token :</p>
<p>Template : Edit zone DNS
Permissions : Zone / DNS / Edit
Zone : mon domaine (ici <code>valerian-pyckaert.dev</code>)</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl create secret generic cloudflare-api-token <span class="se">\
</span></span></span><span class="line"><span class="cl">  --namespace cert-manager <span class="se">\
</span></span></span><span class="line"><span class="cl">  --from-literal<span class="o">=</span>api-token<span class="o">=</span>&lt;TOKEN_CF&gt;
</span></span></code></pre></div><h3 id="5-configuration-de-cert-manager-pour-cloudflare">5. Configuration de Cert Manager pour Cloudflare</h3>
<p>Il s&rsquo;agit maintenant d&rsquo;ajouter une ressource <code>ClusterIssuer</code> pour utiliser le token API Cloudflare et gérer les certificats TLS automatiquement.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">cert-manager.io/v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">ClusterIssuer</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">letsencrypt</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">acme</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">email</span><span class="p">:</span><span class="w"> </span><span class="l">ton@email.com</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">server</span><span class="p">:</span><span class="w"> </span><span class="l">https://acme-v02.api.letsencrypt.org/directory</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">privateKeySecretRef</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">letsencrypt-key</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">solvers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">dns01</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">cloudflare</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">apiTokenSecretRef</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">cloudflare-api-token</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="l">api-token</span><span class="w">
</span></span></span></code></pre></div><p>Une fois cette configuration appliquée, Cert Manager pourra automatiquement obtenir et renouveler les certificats TLS pour les domaines gérés via Cloudflare.
Pour vérifier le bon fonctionnement :</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl get clusterissuer -A
</span></span><span class="line"><span class="cl">NAME               READY   AGE
</span></span><span class="line"><span class="cl">letsencrypt-prod   True    17h
</span></span><span class="line"><span class="cl">kubectl get certificate -A
</span></span><span class="line"><span class="cl">default     wildcard-cert   True    wildcard-cert   54m
</span></span></code></pre></div><h3 id="6-configuration-dune-httproute-pour-exposer-un-service">6. Configuration d&rsquo;une HTTPRoute pour exposer un service</h3>
<p>Maintenant que tout est en place, nous allons créer une ressource <code>HTTPRoute</code> pour exposer un service Kubernetes via Nginx Gateway Fabric et positionner le certificat généré par Cert Manager.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">gateway.networking.k8s.io/v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Gateway</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">main-gateway</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">default</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">annotations</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">cert-manager.io/cluster-issuer</span><span class="p">:</span><span class="w"> </span><span class="l">letsencrypt-prod</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">gatewayClassName</span><span class="p">:</span><span class="w"> </span><span class="l">nginx</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">listeners</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">https</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">hostname</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;*.mondomaine.com&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">port</span><span class="p">:</span><span class="w"> </span><span class="m">443</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">protocol</span><span class="p">:</span><span class="w"> </span><span class="l">HTTPS</span><span class="w">
</span></span></span><span class="line"><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="cl"><span class="w">      </span><span class="nt">mode</span><span class="p">:</span><span class="w"> </span><span class="l">Terminate</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">certificateRefs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">wildcard-cert</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nn">---</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">gateway.networking.k8s.io/v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">HTTPRoute</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">my-app</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">parentRefs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">main-gateway</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">hostnames</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="s2">&#34;app.mondomaine.com&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">rules</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">backendRefs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">my-service</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">port</span><span class="p">:</span><span class="w"> </span><span class="m">80</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nn">---</span><span class="w">
</span></span></span></code></pre></div><h2 id="résultat">Résultat</h2>
<p>Si vous avez suivi chaque étapes vous devriez avoir un résultat similaire à celui-ci :</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">❯ kubectl get gateway -A
</span></span><span class="line"><span class="cl">NAMESPACE   NAME           CLASS   ADDRESS         PROGRAMMED   AGE
</span></span><span class="line"><span class="cl">default     main-gateway   nginx   34.156.85.195   True         29m
</span></span><span class="line"><span class="cl">❯ kubectl get httproute -A
</span></span><span class="line"><span class="cl">NAMESPACE   NAME        HOSTNAMES                         AGE
</span></span><span class="line"><span class="cl">default     nginx-app   <span class="o">[</span><span class="s2">&#34;nginx.valerian-pyckaert.dev&#34;</span><span class="o">]</span>   29m
</span></span><span class="line"><span class="cl">❯ kubectl get certificate -A
</span></span><span class="line"><span class="cl">NAMESPACE   NAME            READY   SECRET          AGE
</span></span><span class="line"><span class="cl">default     wildcard-cert   True    wildcard-cert   21m
</span></span></code></pre></div><p>Votre service devrait être accessible via l&rsquo;URL <code>https://nginx.mondomaine.com</code> et le certificat TLS devrait être valide, en l&rsquo;occurence pour moi c&rsquo;était <code>https://nginx.valerian-pyckaert.dev</code> avec un certificat pour <code>valerian-pyckaert.dev</code> généré par Let&rsquo;s Encrypt.</p>
<p><img alt="Welcome nginx" loading="lazy" src="/the-forge-welcome-nginx.png"></p>
<p>Pour ce faire j&rsquo;ai enregistré un enregistrement A dans Cloudflare qui pointe vers l&rsquo;IP du LoadBalancer GCP :
<img alt="Cloudflare record" loading="lazy" src="/the-forge-cloudflare-record.png"></p>
<hr>
<h2 id="obstacles-rencontrés">Obstacles rencontrés</h2>
<h3 id="la-gateway-ne-peut-pas-accéder-au-namespace-demo-et-au-service-nginx">La gateway ne peut pas accéder au namespace <code>demo</code> et au service <code>nginx</code></h3>
<p>J&rsquo;avais installé ma ressource <code>HTTPRoute</code> dans le namespace <code>default</code> alors que mon service <code>nginx</code> était dans le namespace <code>demo</code>. Par défaut, la gateway n&rsquo;a pas accès aux ressources d&rsquo;autres namespaces, il faut donc lui accorder les permissions nécessaires.</p>
<p>Soit via un <code>ReferenceGrant</code> :</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">gateway.networking.k8s.io/v1beta1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">ReferenceGrant</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">allow-gateway-to-demo</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">demo</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">from</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">group</span><span class="p">:</span><span class="w"> </span><span class="l">gateway.networking.k8s.io</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">HTTPRoute</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">default</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">to</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">group</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Service</span><span class="w">
</span></span></span></code></pre></div><p>Soit installé la ressource <code>HTTPRoute</code> dans le même namespace que le service <code>nginx</code> (ici <code>demo</code>).</p>
<h3 id="pods-en-pending">Pods en pending</h3>
<p>Par péché de rapidité j&rsquo;ai volontairement omis de spécifier les ressources <code>CPU</code> et <code>memory</code> de mes workloads, ce qui a pour conséquence que les pods sont restés en <code>pending</code> car le scheduler ne pouvait pas les placer sur un nœud. Il faut donc veiller à toujours spécifier les ressources de vos workloads pour éviter ce genre de problème.</p>
<hr>
<h2 id="pour-la-suite">Pour la suite</h2>
<p>Dans le prochain et dernier article de cette série <code>The Forge</code> je vous parlerai de SOPS et de comment gérer vos secrets de manière sécurisée dans Kubernetes. Nous verrons comment chiffrer vos secrets avec SOPS et les stocker dans Git pour une gestion centralisée et sécurisée de vos secrets.</p>
]]></content:encoded>
    </item>
    <item>
      <title>TheForge #3 — Monitoring Kubernetes avec Prometheus, Grafana et Loki</title>
      <link>https://valerian-pyckaert.dev/posts/the-forge-monitoring/</link>
      <pubDate>Sun, 31 May 2026 09:35:18 +0200</pubDate>
      <guid>https://valerian-pyckaert.dev/posts/the-forge-monitoring/</guid>
      <description>Installation d&amp;#39;une stack de monitoring complète sur Kubernetes avec Prometheus, Grafana et Loki.</description>
      <content:encoded><![CDATA[<hr>
<h2 id="pré-requis">Pré-requis</h2>
<ul>
<li>Un cluster GKE up and running</li>
<li>Connaissance de base de Kubernetes et GitOps</li>
<li>ArgoCD installé et configuré</li>
</ul>
<hr>
<h2 id="étapes">Étapes</h2>
<h3 id="1-quest-ce-quon-va-installer-">1. Qu&rsquo;est ce qu&rsquo;on va installer ?</h3>
<table>
  <thead>
      <tr>
          <th>Outil</th>
          <th>Description</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Prometheus</td>
          <td>Système de surveillance open-source qui collecte et stocke des métriques sous forme de séries temporelles en interrogeant périodiquement des endpoints HTTP.</td>
      </tr>
      <tr>
          <td>Kube State Metrics</td>
          <td>Service Kubernetes qui expose l&rsquo;état des objets du cluster (pods, deployments, nodes…) sous forme de métriques consommables par Prometheus.</td>
      </tr>
      <tr>
          <td>Grafana</td>
          <td>Plateforme de visualisation qui permet de créer des dashboards interactifs à partir de sources de données comme Prometheus.</td>
      </tr>
      <tr>
          <td>Alertmanager</td>
          <td>Composant de l&rsquo;écosystème Prometheus qui gère le routage, le regroupement et l&rsquo;envoi des alertes vers des canaux de notification (Slack, e-mail, PagerDuty…).</td>
      </tr>
      <tr>
          <td>Loki</td>
          <td>Système d&rsquo;agrégation de logs inspiré de Prometheus, conçu pour indexer uniquement les métadonnées des logs afin de réduire les coûts de stockage.</td>
      </tr>
      <tr>
          <td>Alloy</td>
          <td>Agent de collecte de données de Grafana Labs, successeur de Grafana Agent, capable de collecter métriques, logs et traces pour les acheminer vers différents backends.</td>
      </tr>
  </tbody>
</table>
<h3 id="1-installation-de-la-kube-prometheus-stack-prometheus--grafana--alertmanager">1. Installation de la kube-prometheus-stack (Prometheus + Grafana + Alertmanager)</h3>
<p>Si vous avez suivi l&rsquo;article précédent, rien de plus simple : il suffit d&rsquo;ajouter une nouvelle application dans <code>apps/</code> dans notre repo de CD.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">argoproj.io/v1alpha1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Application</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">kube-prometheus-stack</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">argocd</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">finalizers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">resources-finalizer.argocd.argoproj.io</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">project</span><span class="p">:</span><span class="w"> </span><span class="l">default</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">source</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">repoURL</span><span class="p">:</span><span class="w"> </span><span class="l">https://prometheus-community.github.io/helm-charts</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">chart</span><span class="p">:</span><span class="w"> </span><span class="l">kube-prometheus-stack</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">targetRevision</span><span class="p">:</span><span class="w"> </span><span class="m">86.0.1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">helm</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">releaseName</span><span class="p">:</span><span class="w"> </span><span class="l">kube-prometheus-stack</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">valueFiles</span><span class="p">:</span><span class="w"> </span><span class="p">[]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">values</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">        grafana:
</span></span></span><span class="line"><span class="cl"><span class="sd">          enabled: true
</span></span></span><span class="line"><span class="cl"><span class="sd">          adminPassword: admin
</span></span></span><span class="line"><span class="cl"><span class="sd">          service:
</span></span></span><span class="line"><span class="cl"><span class="sd">            type: ClusterIP
</span></span></span><span class="line"><span class="cl"><span class="sd">        prometheus:
</span></span></span><span class="line"><span class="cl"><span class="sd">          prometheusSpec:
</span></span></span><span class="line"><span class="cl"><span class="sd">            retention: 7d
</span></span></span><span class="line"><span class="cl"><span class="sd">            resources:
</span></span></span><span class="line"><span class="cl"><span class="sd">              requests:
</span></span></span><span class="line"><span class="cl"><span class="sd">                cpu: 200m
</span></span></span><span class="line"><span class="cl"><span class="sd">                memory: 512Mi
</span></span></span><span class="line"><span class="cl"><span class="sd">              limits:
</span></span></span><span class="line"><span class="cl"><span class="sd">                memory: 2Gi
</span></span></span><span class="line"><span class="cl"><span class="sd">            storageSpec: {}
</span></span></span><span class="line"><span class="cl"><span class="sd">        alertmanager:
</span></span></span><span class="line"><span class="cl"><span class="sd">          enabled: true
</span></span></span><span class="line"><span class="cl"><span class="sd">        kubeStateMetrics:
</span></span></span><span class="line"><span class="cl"><span class="sd">          enabled: true
</span></span></span><span class="line"><span class="cl"><span class="sd">        nodeExporter:
</span></span></span><span class="line"><span class="cl"><span class="sd">          enabled: true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">destination</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">server</span><span class="p">:</span><span class="w"> </span><span class="l">https://kubernetes.default.svc</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">monitoring</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">syncPolicy</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">automated</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">prune</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="cl"><span class="w">      </span><span class="nt">selfHeal</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="cl"><span class="w">    </span><span class="nt">syncOptions</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">CreateNamespace=true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">ServerSideApply=true</span><span class="w">
</span></span></span></code></pre></div><p>On push ce fichier <code>kube-prometheus-stack.yaml</code> et Argo se charge de déployer la stack de monitoring dans notre cluster.</p>
<p>Après quelques minutes d&rsquo;attente, vous devriez voir les pods de Prometheus, Grafana et Alertmanager en cours d&rsquo;exécution dans le namespace <code>monitoring</code> :</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl get pods -n monitoring
</span></span><span class="line"><span class="cl">NAME                                                        READY   STATUS    RESTARTS   AGE
</span></span><span class="line"><span class="cl">alertmanager-kube-prometheus-stack-alertmanager-0           2/2     Running   <span class="m">0</span>          2m30s
</span></span><span class="line"><span class="cl">kube-prometheus-stack-admission-create-ljf79                0/1     Pending   <span class="m">0</span>          41s
</span></span><span class="line"><span class="cl">kube-prometheus-stack-grafana-6ddb85bf55-j5shg              3/3     Running   <span class="m">0</span>          2m35s
</span></span><span class="line"><span class="cl">kube-prometheus-stack-kube-state-metrics-5fc57bf8f9-vhfhq   1/1     Running   <span class="m">0</span>          2m35s
</span></span><span class="line"><span class="cl">kube-prometheus-stack-operator-585c4957f7-2r772             1/1     Running   <span class="m">0</span>          2m35s
</span></span><span class="line"><span class="cl">prometheus-kube-prometheus-stack-prometheus-0               2/2     Running   <span class="m">0</span>          2m30s
</span></span><span class="line"><span class="cl">---
</span></span></code></pre></div><p>Grafana est désormais accessible via un port-forwarding :</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl port-forward -n monitoring svc/kube-prometheus-stack-grafana 3000:80
</span></span></code></pre></div><p>Rendez-vous sur <code>http://localhost:3000</code> et connectez-vous avec les identifiants <code>admin/admin</code>.</p>
<p>Cliquez ensuite sur &ldquo;Dashboards&rdquo; pour voir la liste des dashboards disponibles par défaut avec la chart :
<img alt="Dashboards Grafana" loading="lazy" src="/dashboard-grafana.png"></p>
<h3 id="3-collecte-des-logs-avec-loki">3. Collecte des Logs avec Loki</h3>
<p>Là encore il suffit de créer une nouvelle application ArgoCD qui pointe vers le chart Helm de Loki pour déployer un stack de collecte de logs dans notre cluster.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">argoproj.io/v1alpha1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Application</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">loki</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">argocd</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">finalizers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">resources-finalizer.argocd.argoproj.io</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">project</span><span class="p">:</span><span class="w"> </span><span class="l">default</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">source</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">repoURL</span><span class="p">:</span><span class="w"> </span><span class="l">https://grafana.github.io/helm-charts</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">chart</span><span class="p">:</span><span class="w"> </span><span class="l">loki</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">targetRevision</span><span class="p">:</span><span class="w"> </span><span class="m">7.0.0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">helm</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">releaseName</span><span class="p">:</span><span class="w"> </span><span class="l">loki</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">values</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">        deploymentMode: SingleBinary
</span></span></span><span class="line"><span class="cl"><span class="sd">        loki:
</span></span></span><span class="line"><span class="cl"><span class="sd">          auth_enabled: false
</span></span></span><span class="line"><span class="cl"><span class="sd">          commonConfig:
</span></span></span><span class="line"><span class="cl"><span class="sd">            replication_factor: 1
</span></span></span><span class="line"><span class="cl"><span class="sd">          storage:
</span></span></span><span class="line"><span class="cl"><span class="sd">            type: filesystem
</span></span></span><span class="line"><span class="cl"><span class="sd">          schemaConfig:
</span></span></span><span class="line"><span class="cl"><span class="sd">            configs:
</span></span></span><span class="line"><span class="cl"><span class="sd">              - from: &#34;2024-01-01&#34;
</span></span></span><span class="line"><span class="cl"><span class="sd">                store: tsdb
</span></span></span><span class="line"><span class="cl"><span class="sd">                object_store: filesystem
</span></span></span><span class="line"><span class="cl"><span class="sd">                schema: v13
</span></span></span><span class="line"><span class="cl"><span class="sd">                index:
</span></span></span><span class="line"><span class="cl"><span class="sd">                  prefix: index_
</span></span></span><span class="line"><span class="cl"><span class="sd">                  period: 24h
</span></span></span><span class="line"><span class="cl"><span class="sd">        singleBinary:
</span></span></span><span class="line"><span class="cl"><span class="sd">          replicas: 1
</span></span></span><span class="line"><span class="cl"><span class="sd">          persistence:
</span></span></span><span class="line"><span class="cl"><span class="sd">            size: 10Gi
</span></span></span><span class="line"><span class="cl"><span class="sd">        read:
</span></span></span><span class="line"><span class="cl"><span class="sd">          replicas: 0
</span></span></span><span class="line"><span class="cl"><span class="sd">        write:
</span></span></span><span class="line"><span class="cl"><span class="sd">          replicas: 0
</span></span></span><span class="line"><span class="cl"><span class="sd">        backend:
</span></span></span><span class="line"><span class="cl"><span class="sd">          replicas: 0
</span></span></span><span class="line"><span class="cl"><span class="sd">        gateway:
</span></span></span><span class="line"><span class="cl"><span class="sd">          enabled: true
</span></span></span><span class="line"><span class="cl"><span class="sd">        chunksCache:
</span></span></span><span class="line"><span class="cl"><span class="sd">          enabled: false
</span></span></span><span class="line"><span class="cl"><span class="sd">        resultsCache:
</span></span></span><span class="line"><span class="cl"><span class="sd">          enabled: false</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">destination</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">server</span><span class="p">:</span><span class="w"> </span><span class="l">https://kubernetes.default.svc</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">monitoring</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">syncPolicy</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">automated</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">prune</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="cl"><span class="w">      </span><span class="nt">selfHeal</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="cl"><span class="w">    </span><span class="nt">syncOptions</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">CreateNamespace=true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">ServerSideApply=true</span><span class="w">
</span></span></span></code></pre></div><p>Enfin il faut indiquer à Grafana où trouver Loki pour pouvoir visualiser les logs dans notre dashboard.</p>
<p>Pour cela, on ajoute une datasource Loki dans Grafana en utilisant un ConfigMap Kubernetes :</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">source</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">helm</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">values</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">        grafana:
</span></span></span><span class="line"><span class="cl"><span class="sd">          additionalDataSources:
</span></span></span><span class="line"><span class="cl"><span class="sd">            - name: Loki
</span></span></span><span class="line"><span class="cl"><span class="sd">              type: loki
</span></span></span><span class="line"><span class="cl"><span class="sd">              access: proxy
</span></span></span><span class="line"><span class="cl"><span class="sd">              url: http://loki-gateway.monitoring.svc.cluster.local
</span></span></span><span class="line"><span class="cl"><span class="sd">              jsonData:
</span></span></span><span class="line"><span class="cl"><span class="sd">                maxLines: 1000</span><span class="w">
</span></span></span></code></pre></div><p>Et voilà le résultat :</p>
<p><img alt="Logs loki" loading="lazy" src="/loki.png"></p>
<h3 id="4-collecte-des-logs-de-notre-application">4. Collecte des logs de notre application</h3>
<p>Pour l&rsquo;heure, les logs de notre application nginx ne sont pas collectés par Loki.
Nous allons déployer Alloy en tant que DaemonSet dans notre cluster pour collecter les logs de tous les pods et les envoyer à Loki, il s&rsquo;agit ici encore d&rsquo;ajouter une nouvelle application ArgoCD dans notre repo de CD :</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">argoproj.io/v1alpha1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Application</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">alloy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">argocd</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">finalizers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">resources-finalizer.argocd.argoproj.io</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">project</span><span class="p">:</span><span class="w"> </span><span class="l">default</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">source</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">repoURL</span><span class="p">:</span><span class="w"> </span><span class="l">https://grafana.github.io/helm-charts</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">chart</span><span class="p">:</span><span class="w"> </span><span class="l">alloy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">targetRevision</span><span class="p">:</span><span class="w"> </span><span class="m">1.16.1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">helm</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">releaseName</span><span class="p">:</span><span class="w"> </span><span class="l">alloy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">values</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">        alloy:
</span></span></span><span class="line"><span class="cl"><span class="sd">          configMap:
</span></span></span><span class="line"><span class="cl"><span class="sd">            content: |
</span></span></span><span class="line"><span class="cl"><span class="sd">              discovery.kubernetes &#34;pods&#34; {
</span></span></span><span class="line"><span class="cl"><span class="sd">                role = &#34;pod&#34;
</span></span></span><span class="line"><span class="cl"><span class="sd">              }
</span></span></span><span class="line"><span class="cl"><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">              discovery.relabel &#34;pods&#34; {
</span></span></span><span class="line"><span class="cl"><span class="sd">                targets = discovery.kubernetes.pods.targets
</span></span></span><span class="line"><span class="cl"><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">                rule {
</span></span></span><span class="line"><span class="cl"><span class="sd">                  source_labels = [&#34;__meta_kubernetes_namespace&#34;]
</span></span></span><span class="line"><span class="cl"><span class="sd">                  target_label  = &#34;namespace&#34;
</span></span></span><span class="line"><span class="cl"><span class="sd">                }
</span></span></span><span class="line"><span class="cl"><span class="sd">                rule {
</span></span></span><span class="line"><span class="cl"><span class="sd">                  source_labels = [&#34;__meta_kubernetes_pod_name&#34;]
</span></span></span><span class="line"><span class="cl"><span class="sd">                  target_label  = &#34;pod&#34;
</span></span></span><span class="line"><span class="cl"><span class="sd">                }
</span></span></span><span class="line"><span class="cl"><span class="sd">                rule {
</span></span></span><span class="line"><span class="cl"><span class="sd">                  source_labels = [&#34;__meta_kubernetes_pod_container_name&#34;]
</span></span></span><span class="line"><span class="cl"><span class="sd">                  target_label  = &#34;container&#34;
</span></span></span><span class="line"><span class="cl"><span class="sd">                }
</span></span></span><span class="line"><span class="cl"><span class="sd">                rule {
</span></span></span><span class="line"><span class="cl"><span class="sd">                  source_labels = [&#34;__meta_kubernetes_pod_label_app&#34;]
</span></span></span><span class="line"><span class="cl"><span class="sd">                  target_label  = &#34;app&#34;
</span></span></span><span class="line"><span class="cl"><span class="sd">                }
</span></span></span><span class="line"><span class="cl"><span class="sd">              }
</span></span></span><span class="line"><span class="cl"><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">              loki.source.kubernetes &#34;pods&#34; {
</span></span></span><span class="line"><span class="cl"><span class="sd">                targets    = discovery.relabel.pods.output
</span></span></span><span class="line"><span class="cl"><span class="sd">                forward_to = [loki.write.default.receiver]
</span></span></span><span class="line"><span class="cl"><span class="sd">              }
</span></span></span><span class="line"><span class="cl"><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">              loki.write &#34;default&#34; {
</span></span></span><span class="line"><span class="cl"><span class="sd">                endpoint {
</span></span></span><span class="line"><span class="cl"><span class="sd">                  url = &#34;http://loki-gateway.monitoring.svc.cluster.local/loki/api/v1/push&#34;
</span></span></span><span class="line"><span class="cl"><span class="sd">                }
</span></span></span><span class="line"><span class="cl"><span class="sd">              }</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">destination</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">server</span><span class="p">:</span><span class="w"> </span><span class="l">https://kubernetes.default.svc</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">monitoring</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">syncPolicy</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">automated</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">prune</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="cl"><span class="w">      </span><span class="nt">selfHeal</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="cl"><span class="w">    </span><span class="nt">syncOptions</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">CreateNamespace=true</span><span class="w">
</span></span></span></code></pre></div><p>La configuration utilise la syntaxe River d&rsquo;Alloy pour :</p>
<ul>
<li>Découvrir tous les pods Kubernetes</li>
<li>Extraire les labels namespace, pod, container et app</li>
<li>Envoyer les logs vers votre Loki gateway</li>
<li>Alloy sera déployé comme DaemonSet et collectera les logs de votre nginx. Dans Grafana, vous pourrez les consulter avec :</li>
</ul>
<blockquote>
<p>{namespace=&ldquo;demo&rdquo;, app=&ldquo;nginx&rdquo;}</p>
</blockquote>
<p>De retour sur Grafana, la liste des filtres disponibles est désormais bien plus fournie :
<img alt="Filtres Loki" loading="lazy" src="/filtres-loki.png"></p>
<p><img alt="Logs Loki" loading="lazy" src="/logs-loki.png"></p>
<hr>
<h2 id="résultat">Résultat</h2>
<p>Dans cet article nous avons vu comment installer une stack de monitoring complète sur Kubernetes avec Prometheus, Grafana et Loki, le tout déployé en GitOps via ArgoCD.
Nous avons également vu comment collecter les logs de nos pods sur notre cluster avec Alloy et les visualiser dans Grafana.</p>
<p>Si vous avez suivi toutes les étapes de cet article vous devriez avoir les workloads suivantes :</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl get pods -n monitoring
</span></span><span class="line"><span class="cl">NAME                                                        READY   STATUS    RESTARTS   AGE
</span></span><span class="line"><span class="cl">alertmanager-kube-prometheus-stack-alertmanager-0           2/2     Running   <span class="m">0</span>          21m
</span></span><span class="line"><span class="cl">alloy-jb2nf                                                 2/2     Running   <span class="m">0</span>          5m22s
</span></span><span class="line"><span class="cl">kube-prometheus-stack-grafana-bf74d765f-4hzlt               3/3     Running   <span class="m">0</span>          23m
</span></span><span class="line"><span class="cl">kube-prometheus-stack-kube-state-metrics-6b747b7c5f-hjjwd   1/1     Running   <span class="m">0</span>          23m
</span></span><span class="line"><span class="cl">kube-prometheus-stack-operator-59c5d9f8c-q6phj              1/1     Running   <span class="m">0</span>          23m
</span></span><span class="line"><span class="cl">kube-prometheus-stack-prometheus-node-exporter-zhmfp        1/1     Running   <span class="m">0</span>          23m
</span></span><span class="line"><span class="cl">loki-0                                                      2/2     Running   <span class="m">0</span>          32m
</span></span><span class="line"><span class="cl">loki-canary-55ccd5d799-wkc7t                                1/1     Running   <span class="m">0</span>          32m
</span></span><span class="line"><span class="cl">loki-gateway-6df7c96bc9-l92d2                               1/1     Running   <span class="m">0</span>          32m
</span></span><span class="line"><span class="cl">prometheus-kube-prometheus-stack-prometheus-0               2/2     Running   <span class="m">0</span>          21m
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl get pods -n demo
</span></span><span class="line"><span class="cl">NAME                     READY   STATUS    RESTARTS   AGE
</span></span><span class="line"><span class="cl">nginx-7489fb554f-hffjv   1/1     Running   <span class="m">0</span>          33m
</span></span></code></pre></div><hr>
<p>L&rsquo;essentiel des travaux est disponible sur <a href="https://github.com/Bernedotcom2312/theforge-cd">mon repo GitHub</a></p>
<h2 id="ce-qui-vient-ensuite">Ce qui vient ensuite</h2>
<p>Nous verrons comment exposer notre application à travers la Gateway API dans le prochain article de la série, stay tuned !</p>
<hr>
<p><strong>Sources :</strong></p>
<ul>
<li><a href="https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack">kube-prometheus-stack Helm Chart</a></li>
<li><a href="https://github.com/grafana/helm-charts/tree/main/charts/loki">Loki Helm Chart</a></li>
<li><a href="https://github.com/grafana/alloy/blob/main/operations/helm/charts/alloy/README.md">Alloy Helm Chart</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>TheForge #2 — Installation d&#39;ArgoCD pour le déploiement continu sur Kubernetes</title>
      <link>https://valerian-pyckaert.dev/posts/the-forge-cd/</link>
      <pubDate>Thu, 28 May 2026 19:00:00 +0200</pubDate>
      <guid>https://valerian-pyckaert.dev/posts/the-forge-cd/</guid>
      <description>Installation d&amp;#39;ArgoCD pour le déploiement continu sur Kubernetes.</description>
      <content:encoded><![CDATA[<h2 id="ce-quon-fait">Ce qu&rsquo;on fait</h2>
<blockquote>
<p>Installation d&rsquo;ArgoCD pour le déploiement continu sur Kubernetes via Terraform et Helm.</p>
</blockquote>
<hr>
<h2 id="pré-requis">Pré-requis</h2>
<ul>
<li>Cluster GKE up and running</li>
<li>Connaissance de base de Kubernetes et GitOps</li>
<li>Outils : <code>terraform</code>, <code>gcloud</code>, <code>kubectl</code>, <code>helm</code>&hellip;</li>
</ul>
<hr>
<h2 id="étapes">Étapes</h2>
<h3 id="1-installation-dargocd">1. Installation d&rsquo;ArgoCD</h3>
<p>Pour cette partie j&rsquo;ai choisi d&rsquo;installer ArgoCD via Terraform en utilisant le provider Helm. Cela nous permettra de gérer l&rsquo;installation d&rsquo;ArgoCD de manière déclarative et reproductible.
Argo sera ensuite en charge de déployer les workloads sur notre cluster Kubernetes.</p>
<p>Ajout des providers nécessaires dans notre configuration Terraform :</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-hcl" data-lang="hcl"><span class="line"><span class="cl"><span class="k">provider</span> <span class="s2">&#34;kubernetes&#34;</span> {
</span></span><span class="line"><span class="cl"><span class="n">  host</span>                   <span class="o">=</span> <span class="s2">&#34;https://${google_container_cluster.primary.endpoint}&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">  token</span>                  <span class="o">=</span> <span class="k">data</span><span class="p">.</span><span class="k">google_client_config</span><span class="p">.</span><span class="k">default</span><span class="p">.</span><span class="k">access_token</span>
</span></span><span class="line"><span class="cl"><span class="n">  cluster_ca_certificate</span> <span class="o">=</span> <span class="k">base64decode</span><span class="p">(</span><span class="k">google_container_cluster</span><span class="p">.</span><span class="k">primary</span><span class="p">.</span><span class="k">master_auth</span><span class="p">.</span><span class="m">0</span><span class="p">.</span><span class="k">cluster_ca_certificate</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">provider</span> <span class="s2">&#34;helm&#34;</span> {
</span></span><span class="line"><span class="cl"><span class="n">  kubernetes</span> <span class="o">=</span> {
</span></span><span class="line"><span class="cl"><span class="n">    host</span>                   <span class="o">=</span> <span class="s2">&#34;https://${google_container_cluster.primary.endpoint}&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">    token</span>                  <span class="o">=</span> <span class="k">data</span><span class="p">.</span><span class="k">google_client_config</span><span class="p">.</span><span class="k">default</span><span class="p">.</span><span class="k">access_token</span>
</span></span><span class="line"><span class="cl"><span class="n">    cluster_ca_certificate</span> <span class="o">=</span> <span class="k">base64decode</span><span class="p">(</span><span class="k">google_container_cluster</span><span class="p">.</span><span class="k">primary</span><span class="p">.</span><span class="k">master_auth</span><span class="p">.</span><span class="m">0</span><span class="p">.</span><span class="k">cluster_ca_certificate</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  }
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>Ne pas oublier <em>terraform init</em> pour installer les providers.
Ensuite, nous allons ajouter une ressource Helm pour installer ArgoCD :</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-hcl" data-lang="hcl"><span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">&#34;helm_release&#34; &#34;argocd&#34;</span> {
</span></span><span class="line"><span class="cl"><span class="n">  name</span>             <span class="o">=</span> <span class="s2">&#34;argocd&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">  repository</span>       <span class="o">=</span> <span class="s2">&#34;https://argoproj.github.io/argo-helm&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">  chart</span>            <span class="o">=</span> <span class="s2">&#34;argo-cd&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">  version</span>          <span class="o">=</span> <span class="s2">&#34;9.5.15&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">  namespace</span>        <span class="o">=</span> <span class="s2">&#34;argocd&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">  create_namespace</span> <span class="o">=</span> <span class="kt">true</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">  set</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    {
</span></span><span class="line"><span class="cl"><span class="n">      name</span>  <span class="o">=</span> <span class="s2">&#34;dex.enabled&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">      value</span> <span class="o">=</span> <span class="s2">&#34;false&#34;</span>
</span></span><span class="line"><span class="cl">    }<span class="p">,</span>
</span></span><span class="line"><span class="cl">    {
</span></span><span class="line"><span class="cl"><span class="n">      name</span>  <span class="o">=</span> <span class="s2">&#34;notifications.enabled&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">      value</span> <span class="o">=</span> <span class="s2">&#34;false&#34;</span>
</span></span><span class="line"><span class="cl">    }<span class="p">,</span>
</span></span><span class="line"><span class="cl">    {
</span></span><span class="line"><span class="cl"><span class="n">      name</span>  <span class="o">=</span> <span class="s2">&#34;applicationSet.enabled&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">      value</span> <span class="o">=</span> <span class="s2">&#34;false&#34;</span>
</span></span><span class="line"><span class="cl">    }<span class="p">,</span>
</span></span><span class="line"><span class="cl">    {
</span></span><span class="line"><span class="cl"><span class="n">      name</span>  <span class="o">=</span> <span class="s2">&#34;server.extraArgs[0]&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">      value</span> <span class="o">=</span> <span class="s2">&#34;--insecure&#34;</span>
</span></span><span class="line"><span class="cl">    }
</span></span><span class="line"><span class="cl">  <span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">  depends_on</span> <span class="o">=</span> <span class="p">[</span><span class="k">google_container_node_pool</span><span class="p">.</span><span class="k">primary_nodes</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p><em>Ici nous installons ArgoCD dans le namespace <code>argocd</code> en désactivant certaines fonctionnalités que nous n&rsquo;utiliserons pas pour le moment (Dex, notifications, ApplicationSet) et en configurant le serveur ArgoCD pour qu&rsquo;il fonctionne en mode non sécurisé (insecure) pour simplifier les choses dans un environnement de développement.</em></p>
<hr>
<h3 id="2-création-de-la-root-application-argocd-app-of-apps-pattern">2. Création de la Root Application ArgoCD (App of Apps Pattern)</h3>
<p>Le pattern &ldquo;App of Apps&rdquo; est une approche recommandée pour organiser les applications dans ArgoCD. Il consiste à créer une application principale (root application) qui référence d&rsquo;autres applications (child applications).</p>
<p>Cela permet de structurer les déploiements de manière hiérarchique et de faciliter la gestion des dépendances entre les applications.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-hcl" data-lang="hcl"><span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">&#34;kubernetes_manifest&#34; &#34;argocd_app&#34;</span> {
</span></span><span class="line"><span class="cl"><span class="n">  manifest</span> <span class="o">=</span> {
</span></span><span class="line"><span class="cl"><span class="n">    apiVersion</span> <span class="o">=</span> <span class="s2">&#34;argoproj.io/v1alpha1&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">    kind</span>       <span class="o">=</span> <span class="s2">&#34;Application&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">    metadata</span> <span class="o">=</span> {
</span></span><span class="line"><span class="cl"><span class="n">      name</span>      <span class="o">=</span> <span class="s2">&#34;root-app&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">      namespace</span> <span class="o">=</span> <span class="s2">&#34;argocd&#34;</span>
</span></span><span class="line"><span class="cl">    }
</span></span><span class="line"><span class="cl"><span class="n">    spec</span> <span class="o">=</span> {
</span></span><span class="line"><span class="cl"><span class="n">      project</span> <span class="o">=</span> <span class="s2">&#34;default&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">      source</span> <span class="o">=</span> {
</span></span><span class="line"><span class="cl"><span class="n">        repoURL</span>        <span class="o">=</span> <span class="s2">&#34;https://github.com/Bernedotcom2312/theforge-cd.git&#34;</span><span class="c1"> #Changez pour le votre
</span></span></span><span class="line"><span class="cl"><span class="n">        targetRevision</span> <span class="o">=</span> <span class="s2">&#34;main&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">        path</span>           <span class="o">=</span> <span class="s2">&#34;apps&#34;</span>
</span></span><span class="line"><span class="cl">      }
</span></span><span class="line"><span class="cl"><span class="n">      destination</span> <span class="o">=</span> {
</span></span><span class="line"><span class="cl"><span class="n">        server</span>    <span class="o">=</span> <span class="s2">&#34;https://kubernetes.default.svc&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">        namespace</span> <span class="o">=</span> <span class="s2">&#34;default&#34;</span>
</span></span><span class="line"><span class="cl">      }
</span></span><span class="line"><span class="cl"><span class="n">      syncPolicy</span> <span class="o">=</span> {
</span></span><span class="line"><span class="cl"><span class="n">        automated</span> <span class="o">=</span> {
</span></span><span class="line"><span class="cl"><span class="n">          prune</span>    <span class="o">=</span> <span class="kt">true</span>
</span></span><span class="line"><span class="cl"><span class="n">          selfHeal</span> <span class="o">=</span> <span class="kt">true</span>
</span></span><span class="line"><span class="cl">        }
</span></span><span class="line"><span class="cl">      }
</span></span><span class="line"><span class="cl">    }
</span></span><span class="line"><span class="cl">  }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">  depends_on</span> <span class="o">=</span> <span class="p">[</span><span class="k">helm_release</span><span class="p">.</span><span class="k">argocd</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p><em>Cette configuration crée une application ArgoCD nommée <code>root-app</code> qui pointe vers un dépôt Git contenant les définitions des applications à déployer.
Le champ <code>syncPolicy</code> est configuré pour permettre le déploiement automatique et de réparer les ressources qui ne sont pas conformes à la configuration (selfHeal).</em></p>
<h3 id="3-application-de-la-configuration-terraform">3. Application de la configuration Terraform</h3>
<p>Maintenant que nous avons ajouté la configuration pour installer ArgoCD et créer la root application, nous pouvons appliquer notre configuration Terraform :</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">terraform apply
</span></span></code></pre></div><p>Cette commande va provisionner ArgoCD sur notre cluster GKE et créer la root application qui référencera les applications à déployer depuis notre dépôt Git.</p>
<hr>
<h2 id="résultat">Résultat</h2>
<p>ArgoCD est maintenant installé sur notre cluster GKE et prêt à être utilisé pour le déploiement continu de nos applications.</p>
<p>Vous pouvez vérifier que les pods ArgoCD sont en cours d&rsquo;exécution avec la commande suivante :</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl get pods -n argocd
</span></span><span class="line"><span class="cl">NAME                                                READY   STATUS    RESTARTS      AGE
</span></span><span class="line"><span class="cl">argocd-application-controller-0                     1/1     Running   <span class="m">0</span>             94s
</span></span><span class="line"><span class="cl">argocd-applicationset-controller-6c6b88d4bc-krjc5   1/1     Running   <span class="m">0</span>             95s
</span></span><span class="line"><span class="cl">argocd-dex-server-79fc985b7b-rbz6w                  1/1     Running   <span class="m">2</span> <span class="o">(</span>77s ago<span class="o">)</span>   96s
</span></span><span class="line"><span class="cl">argocd-notifications-controller-6c44f9dbbf-v4777    1/1     Running   <span class="m">0</span>             96s
</span></span><span class="line"><span class="cl">argocd-redis-7979d55d5b-ft6rc                       1/1     Running   <span class="m">0</span>             96s
</span></span><span class="line"><span class="cl">argocd-repo-server-584c965f87-tzvsg                 1/1     Running   <span class="m">0</span>             95s
</span></span><span class="line"><span class="cl">argocd-server-5f7d4885d5-vkccj                      1/1     Running   <span class="m">0</span>             95s
</span></span></code></pre></div><p>Accès au dashboard ArgoCD :</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl port-forward svc/argocd-server -n argocd 8080:80
</span></span></code></pre></div><p>Rendez vous sur <code>http://localhost:8080</code>.
Vous devriez avoir un warning de sécurité concernant le mode &ldquo;insecure&rdquo; que nous avons configuré pour le serveur ArgoCD, mais c&rsquo;est acceptable pour un environnement de développement, nous verrons comment sécuriser cela dans une prochaine étape.</p>
<p>Pour se connecter, utilisez les identifiants suivants :</p>
<ul>
<li>Username : admin</li>
<li>Password : le mot de passe est généré automatiquement et stocké dans le secrets <code>argocd-initial-admin-secret</code> dans le namespace <code>argocd</code>. Vous pouvez le récupérer avec la commande suivante :</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl -n argocd get secret argocd-initial-admin-secret -o <span class="nv">jsonpath</span><span class="o">=</span><span class="s2">&#34;{.data.password}&#34;</span> <span class="p">|</span> base64 -d
</span></span></code></pre></div><p>Notre application d&rsquo;exemple (Nginx) devrait également être déployée et visible dans l&rsquo;interface d&rsquo;ArgoCD.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl get pods -n demo
</span></span><span class="line"><span class="cl">NAME                    READY   STATUS    RESTARTS   AGE
</span></span><span class="line"><span class="cl">nginx-78b9f9966-bh7vb   1/1     Running   <span class="m">0</span>          110s
</span></span></code></pre></div><p>Un dernier port-forward pour accéder à l&rsquo;application Nginx :</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl port-forward svc/nginx -n demo 8081:80
</span></span></code></pre></div><p>Et voilà le résultat en accèdant au port 8081 de votre localhost :</p>
<p><img alt="Welcome page nginx" loading="lazy" src="/welcome_nginx.png"></p>
<h2 id="ce-qui-vient-ensuite">Ce qui vient ensuite</h2>
<p>La prochaine brique de notre plateforme Kubernetes : le monitoring.</p>
<hr>
<p><strong>Sources</strong></p>
<ul>
<li><a href="https://argo-cd.readthedocs.io/en/stable/">ArgoCD Documentation</a></li>
<li><a href="https://registry.terraform.io/providers/hashicorp/helm/latest/docs">Terraform Helm Provider</a></li>
<li><a href="https://blog.stephane-robert.info/docs/pipeline-cicd/argocd/premiere-application/">Blog Stéphane Robert</a></li>
<li><a href="https://github.com/Bernedotcom2312/theforge-infra">Mon Repo Infra</a></li>
<li><a href="https://github.com/Bernedotcom2312/theforge-cd">Mon Repo CD</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>TheForge #1 — Création d&#39;un cluster Kubernetes sur GCP avec Terraform</title>
      <link>https://valerian-pyckaert.dev/posts/the-forge-cluster/</link>
      <pubDate>Wed, 27 May 2026 13:00:00 +0200</pubDate>
      <guid>https://valerian-pyckaert.dev/posts/the-forge-cluster/</guid>
      <description>Dans ce premier article de la série TheForge, nous allons créer un cluster Kubernetes sur Google Cloud Platform (GCP) en utilisant Terraform. Nous verrons les étapes nécessaires pour configurer notre environnement, provisionner le cluster et valider son bon fonctionnement.</description>
      <content:encoded><![CDATA[<h2 id="pré-requis">Pré-requis</h2>
<ul>
<li>Un compte GCP avec les permissions nécessaires pour créer des ressources.
<ul>
<li>API Kubernetes Engine activée</li>
<li>Billing account configuré</li>
</ul>
</li>
<li>Terraform installé sur votre machine</li>
<li>Quelques connaissances sur Git</li>
</ul>
<hr>
<h2 id="étapes">Étapes</h2>
<h3 id="1-creation-du-repository-git">1. Creation du repository Git</h3>
<p>Ici pas question de réinventer la roue, j&rsquo;utilise un template fournit par Hashicorp(Terraform) pour le bootstrap de mon cluster.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git clone https://github.com/hashicorp-education/learn-terraform-provision-gke-cluster
</span></span><span class="line"><span class="cl">mv learn-terraform-provision-gke-cluster theforge-infra
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> theforge-infra
</span></span><span class="line"><span class="cl">git remote set-url origin git@github.com:mon-repo.git      
</span></span></code></pre></div><p><em>Nous ferons le tour d&rsquo;horizon des ressources juste après.</em></p>
<hr>
<h3 id="2-configuration-gcloud">2. Configuration GCLOUD</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">brew install --cask google-cloud-sdk
</span></span><span class="line"><span class="cl">gcloud init
</span></span><span class="line"><span class="cl">gcloud auth application-default login
</span></span></code></pre></div><hr>
<h3 id="3-architecture-du-repo">3. Architecture du repo</h3>
<table>
  <thead>
      <tr>
          <th>Fichier</th>
          <th>Rôle</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>vpc.tf</code></td>
          <td>Déclare le <strong>VPC</strong> et un <strong>subnet</strong> pour isoler le cluster, ainsi que les variables <code>project_id</code> / <code>region</code> et le provider Google.</td>
      </tr>
      <tr>
          <td><code>gke.tf</code></td>
          <td>Crée le <strong>cluster GKE</strong> (version 1.27.x, default node pool supprimé) et un <strong>node pool séparé</strong> de 2 nœuds <code>n1-standard-1</code>.</td>
      </tr>
      <tr>
          <td><code>versions.tf</code></td>
          <td>Fixe le provider Google à la version <code>7.33.0</code> et Terraform <code>&gt;= 0.14</code>.</td>
      </tr>
      <tr>
          <td><code>outputs.tf</code></td>
          <td>Expose la région, le project ID, le nom et l&rsquo;endpoint du cluster.</td>
      </tr>
      <tr>
          <td><code>terraform.tfvars</code></td>
          <td>Variables d&rsquo;entrée (<code>project_id</code> à remplacer, région <code>us-central1</code>).</td>
      </tr>
      <tr>
          <td><code>kubernetes-dashboard-admin.rbac.yaml</code></td>
          <td>Manifest RBAC Kubernetes pour un accès admin au dashboard (à appliquer post-déploiement).</td>
      </tr>
  </tbody>
</table>
<hr>
<h3 id="4-fine-tuning-de-la-configuration-terraform">4. Fine tuning de la configuration Terraform</h3>
<h4 id="ajout-du-project-id-et-de-la-région">Ajout du Project ID et de la région</h4>
<p>Quelques modifications à apporter à la configuration de base, notamment le <code>project_id</code> et la <code>region</code> dans le fichier <code>terraform.tfvars</code> :</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-hcl" data-lang="hcl"><span class="line"><span class="cl"><span class="n">project_id</span> <span class="o">=</span> <span class="s2">&#34;MON_PROJECT_ID&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">region</span>     <span class="o">=</span> <span class="s2">&#34;europe-west1&#34;</span>
</span></span></code></pre></div><p>Puis on valide que Terraform est correctement configuré et peut communiquer avec GCP :</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">terraform init
</span></span></code></pre></div><blockquote>
<p>Terraform has been successfully initialized!</p>
</blockquote>
<h4 id="mise-à-jour-de-la-version-de-terraform-et-des-providers">Mise à jour de la version de Terraform et des providers</h4>
<p>Le template utilise une version de Terraform et du provider Google qui sont un peu anciennes, il est recommandé de les mettre à jour pour bénéficier des dernières fonctionnalités et corrections de bugs.
Dans <code>versions.tf</code>, on peut mettre à jour la version de Terraform et du provider Google :</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-hcl" data-lang="hcl"><span class="line"><span class="cl"><span class="k">terraform</span> {
</span></span><span class="line"><span class="cl"><span class="n">  required_version</span> <span class="o">=</span><span class="n"> &#34;&gt;</span><span class="o">=</span> <span class="m">1</span><span class="p">.</span><span class="m">15</span><span class="err">&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">required_providers</span> {
</span></span><span class="line"><span class="cl"><span class="n">    google</span> <span class="o">=</span> {
</span></span><span class="line"><span class="cl"><span class="n">      source</span>  <span class="o">=</span> <span class="s2">&#34;hashicorp/google&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">      version</span> <span class="o">=</span> <span class="s2">&#34;~&gt; 7.33.0&#34;</span>
</span></span><span class="line"><span class="cl">    }
</span></span><span class="line"><span class="cl">  }
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>Sources :</p>
<ul>
<li><a href="https://registry.terraform.io/providers/hashicorp/google/latest">Version provider Google</a></li>
<li><a href="https://endoflife.date/terraform">Version Terraform</a></li>
</ul>
<h4 id="mise-à-jour-de-la-version-de-gke">Mise à jour de la version de GKE</h4>
<p>Par défaut, le template utilise aussi une version de GKE qui n&rsquo;est plus supportée (1.27.x).
Il faut donc mettre à jour la version du cluster dans <code>gke.tf</code> pour une version plus récente, par exemple <code>1.36.x</code> :</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-hcl" data-lang="hcl"><span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">&#34;google_container_cluster&#34; &#34;primary&#34;</span> {
</span></span><span class="line"><span class="cl"><span class="n">  name</span>     <span class="o">=</span> <span class="s2">&#34;theforge-cluster&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">  location</span> <span class="o">=</span> <span class="k">var</span><span class="p">.</span><span class="k">region</span><span class="c1">
</span></span></span><span class="line"><span class="cl"><span class="c1">
</span></span></span><span class="line"><span class="cl"><span class="c1">  # Autres configurations...
</span></span></span><span class="line"><span class="cl"><span class="c1">
</span></span></span><span class="line"><span class="cl"><span class="c1">  # Mise à jour de la version du cluster
</span></span></span><span class="line"><span class="cl"><span class="n">  min_master_version</span> <span class="o">=</span> <span class="s2">&#34;1.36&#34;</span>
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>Ensuite, on peut relancer <code>terraform plan</code> pour vérifier que la configuration est valide avec la nouvelle version.</p>
<p>Sources :</p>
<ul>
<li><a href="https://docs.cloud.google.com/kubernetes-engine/docs/release-schedule?hl=fr">Versions GKE disponibles</a></li>
</ul>
<h4 id="séparation-des-ressources-dans-des-fichiers">Séparation des ressources dans des fichiers</h4>
<p>Pour une meilleure organisation, j&rsquo;ai choisi de remanier quelque peu la structure du repo en séparant les ressources dans des fichiers dédiés (<code>variables.tf</code>, <code>providers.tf</code>).
J&rsquo;envisage en effet de réutiliser ce repository à l&rsquo;avenir pour d&rsquo;autres projets, à ce titre je souhaite le rendre plus modulaire et facilement maintenable.</p>
<hr>
<h2 id="résultat">Résultat</h2>
<p>Une fois satisfait de votre configuration il ne reste qu&rsquo;à lancer la commande <code>terraform apply</code> pour provisionner le cluster sur GCP.
Après quelques minutes d&rsquo;attente, le cluster devrait être opérationnel et vous devriez voir les outputs suivants :</p>
<h3 id="connexion-au-cluster">Connexion au cluster</h3>
<p>Google Cloud SDK (gcloud) est nécessaire pour récupérer les credentials du cluster et pouvoir interagir avec lui via kubectl.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">gcloud container clusters get-credentials <span class="s2">&#34;moncluster&#34;</span> --region europe-west1 --project <span class="s2">&#34;monprojet&#34;</span>
</span></span><span class="line"><span class="cl"><span class="c1">#&gt; kubeconfig entry generated for moncluster.</span>
</span></span></code></pre></div><h3 id="verification-du-status-des-pods">Verification du status des pods</h3>
<p>Une fois le cluster déployé vous devriez voir les pods du cluster en cours d&rsquo;exécution :</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl get pods -A
</span></span><span class="line"><span class="cl">NAMESPACE         NAME                                                             READY   STATUS    RESTARTS   AGE
</span></span><span class="line"><span class="cl">gke-managed-cim   kube-state-metrics-0                                             2/2     Running   <span class="m">0</span>          35m
</span></span><span class="line"><span class="cl">gmp-system        collector-jc4t8                                                  2/2     Running   <span class="m">0</span>          30m
</span></span><span class="line"><span class="cl">gmp-system        collector-jdjzw                                                  2/2     Running   <span class="m">0</span>          30m
</span></span></code></pre></div><h3 id="repo-disponible-sur-github">Repo disponible sur GitHub</h3>
<p>L&rsquo;ensemble de la configuration Terraform utilisée pour ce projet est disponible sur GitHub : <a href="https://github.com/Bernedotcom2312/theforge-infra">theforge-infra</a>.</p>
<hr>
<h2 id="erreurs-rencontrées">Erreurs rencontrées</h2>
<h3 id="pas-de-billing-account-configuré">Pas de billing account configuré</h3>
<blockquote>
<p>ERROR: (gcloud.services.enable) FAILED_PRECONDITION: Billing account for project &lsquo;X&rsquo; is not found</p>
</blockquote>
<p>Renseignez un billing account valide pour votre projet et c&rsquo;est réglé.</p>
<h3 id="api-kubernetes-désactivée">API Kubernetes désactivée</h3>
<blockquote>
<p>Error: Error retrieving available container cluster versions: googleapi: Error 403: Kubernetes Engine API has not been used in project deft-accord-496812-k9 before or it is disabled.</p>
</blockquote>
<p>Elle était assez explicite : l&rsquo;API Kubernetes Engine n&rsquo;était pas activée sur le projet GCP.
Je l&rsquo;ai activée via la console GCP, puis relancé <code>terraform plan</code> pour valider que tout est bon.
Vous pouvez aussi l&rsquo;activer avec la CLI :</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">gcloud services <span class="nb">enable</span> container.googleapis.com
</span></span></code></pre></div><h3 id="problème-de-provisioning-du-cluster">Problème de provisioning du cluster</h3>
<blockquote>
<p>Google Compute Engine: Not all instances running in IGM after 35m11.660099086s. Expected 1, running 0, transitioning 1. Current errors: [GCE_STOCKOUT]: Instance &lsquo;gke-deft-accord-496812-k-default-pool-19ca1c15-p6ls&rsquo; creation failed: The zone &lsquo;projects/deft-accord-496812-k9/zones/europe-west1-c&rsquo; does not have enough resources available to fulfill the request. Try a different zone, or try again later.</p>
</blockquote>
<p>Résolution : changer la zone de déploiement du cluster dans <code>gke.tfs</code> pour une zone moins sollicitée, par exemple <code>europe-west1-b</code> ou <code>europe-west1-d</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-hcl" data-lang="hcl"><span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">&#34;google_container_cluster&#34; &#34;primary&#34;</span> {
</span></span><span class="line"><span class="cl"><span class="n">  name</span>     <span class="o">=</span> <span class="s2">&#34;${var.project_id}-gke&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">  location</span> <span class="o">=</span> <span class="k">var</span><span class="p">.</span><span class="k">region</span>
</span></span><span class="line"><span class="cl"><span class="n">  node_locations</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;europe-west1-b&#34;, &#34;europe-west1-d&#34;</span><span class="p">]</span><span class="c1">
</span></span></span><span class="line"><span class="cl"><span class="c1">  # Autres configurations...
</span></span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><h3 id="plugin-gke-introuvable">Plugin GKE introuvable</h3>
<blockquote>
<p>couldn&rsquo;t get current server API group list: Get &ldquo;https://35.233.30.210/api?timeout=32s&rdquo;: getting credentials: exec: executable gke-gcloud-auth-plugin.exe not found</p>
</blockquote>
<p>Solution : installer le plugin d&rsquo;authentification GKE pour kubectl, qui est nécessaire pour se connecter au cluster GKE.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">gcloud components install gke-gcloud-auth-plugin
</span></span></code></pre></div>]]></content:encoded>
    </item>
  </channel>
</rss>
