<?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>ArgoCD on Valérian Pyckaert</title>
    <link>https://valerian-pyckaert.dev/tags/argocd/</link>
    <description>Recent content in ArgoCD 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/tags/argocd/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 #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>The Forge Project</title>
      <link>https://valerian-pyckaert.dev/posts/the-forge-project/</link>
      <pubDate>Fri, 22 May 2026 08:00:00 +0200</pubDate>
      <guid>https://valerian-pyckaert.dev/posts/the-forge-project/</guid>
      <description>Projet fil rouge : construire une plateforme Kubernetes complète sur GCP, from scratch, avec les outils indispensables en 2026. Chaque article documente une étape, les obstacles rencontrés et comment je les ai résolus.</description>
      <content:encoded><![CDATA[<blockquote>
<p><strong>Stack</strong> : GCP · GKE · Terraform · Helm · ArgoCD · SOPS · Kube-Prom-Stack · Nginx Gateway Fabric · cert-manager</p>
</blockquote>
<hr>
<h2 id="pourquoi-ce-projet-">Pourquoi ce projet ?</h2>
<p>Je me lance en freelance DevOps / Platform Engineer / Cloud Engineer. Pour montrer ce que je sais faire (et documenter ce que j&rsquo;apprends en chemin), je démarre un projet fil rouge public : construire une plateforme Kubernetes complète sur GCP, from scratch, avec les outils que j&rsquo;estime indispensables en 2026.</p>
<p>Chaque article de la série documente <strong>une étape</strong>, les <strong>obstacles rencontrés</strong> et comment je les ai résolus. Format court, orienté pratique.</p>
<p>Je vais tenter de garder une approche pragmatique orientée &ldquo;best practices&rdquo; (mais pas dogmatique) pour que ce soit utile à d&rsquo;autres personnes qui veulent se lancer dans la même aventure.</p>
<hr>
<h2 id="ce-quon-va-construire">Ce qu&rsquo;on va construire</h2>
<p>Un cluster GKE avec la stack suivante, déployée en GitOps :</p>
<table>
  <thead>
      <tr>
          <th>Composant</th>
          <th>Rôle</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>Terraform</strong></td>
          <td>Provisionning du cluster et bootstrap</td>
      </tr>
      <tr>
          <td><strong>Helm + ArgoCD</strong></td>
          <td>GitOps, tout passe par là après le bootstrap</td>
      </tr>
      <tr>
          <td><strong>SOPS</strong></td>
          <td>Gestion des secrets chiffrés dans Git</td>
      </tr>
      <tr>
          <td><strong>kube-prometheus-stack</strong></td>
          <td>Monitoring &amp; alerting (Prometheus, Grafana, Alertmanager)</td>
      </tr>
      <tr>
          <td><strong>Nginx Gateway Fabric</strong></td>
          <td>Ingress via la Gateway API</td>
      </tr>
      <tr>
          <td><strong>cert-manager + Let&rsquo;s Encrypt</strong></td>
          <td>TLS automatique</td>
      </tr>
  </tbody>
</table>
<p>Je me focalise sur ce qui me semble essentiel pour adresser la majorité des besoins.
A termes j&rsquo;envisage d&rsquo;ajouter d&rsquo;autres composants destinés à des usages plus complexes : KEDA, Karpenter, Dapr, &hellip;</p>
<hr>
<h2 id="les-étapes-prévues">Les étapes prévues</h2>
<ol>
<li><strong>Création du cluster GKE via Terraform</strong></li>
<li><strong>Bootstrap Helm &amp; ArgoCD via Terraform</strong></li>
<li><strong>Déploiement de kube-prometheus-stack</strong> via ArgoCD</li>
<li><strong>Déploiement de Nginx Gateway Fabric / Gateway API</strong> via ArgoCD</li>
<li><strong>Déploiement de cert-manager + Let&rsquo;s Encrypt</strong> via ArgoCD</li>
<li><strong>Mise en place de SOPS</strong> pour la gestion des secrets</li>
</ol>
<p><em>J&rsquo;ajouterai probablement d&rsquo;autres étapes au fur et à mesure</em>* (ex: gestion des node pools, autoscaling, etc.) selon les besoins et les obstacles rencontrés*</p>
<hr>
<h2 id="principes-de-base">Principes de base</h2>
<ul>
<li><strong>Tout dans Git</strong> — aucune modification manuelle sur le cluster après le bootstrap</li>
<li><strong>Least privilege</strong> — les SA GCP auront le minimum requis</li>
<li><strong>Les secrets ne sont jamais en clair</strong> dans le repo</li>
<li><strong>KISS</strong> — Keep It Simple Stupid. Pas de sur-optimisation, pas de microservices inutiles, pas de complexité superflue</li>
</ul>
<hr>
<h2 id="prochaine-étape">Prochaine étape</h2>
<p>→ <a href="#">Création du cluster GKE avec Terraform</a> <em>(à venir)</em></p>
]]></content:encoded>
    </item>
  </channel>
</rss>
