<?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>Microservices on Guillaume Delré</title><link>https://guillaumedelre.github.io/fr/tags/microservices/</link><description>Recent content in Microservices on Guillaume Delré</description><generator>Hugo</generator><language>fr-FR</language><lastBuildDate>Fri, 15 May 2026 15:00:00 +0000</lastBuildDate><atom:link href="https://guillaumedelre.github.io/fr/tags/microservices/index.xml" rel="self" type="application/rss+xml"/><item><title>L'hôte qui cachait le graphe</title><link>https://guillaumedelre.github.io/fr/2026/05/15/lh%C3%B4te-qui-cachait-le-graphe/</link><pubDate>Fri, 15 May 2026 15:00:00 +0000</pubDate><guid>https://guillaumedelre.github.io/fr/2026/05/15/lh%C3%B4te-qui-cachait-le-graphe/</guid><description>Part 4 of 8 in &amp;quot;Symfony vers le Cloud : Douze Facteurs, Treize Services&amp;quot;: Treize services partageant six variables de gateway identiques. La config semblait simple. Le graphe de dépendances était invisible — jusqu&amp;#39;à ce que Kubernetes demande où chaque service habitait vraiment.</description><category>symfony-to-the-cloud</category><content:encoded><![CDATA[<p>Chaque service de la plateforme avait ces six variables :</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>APP__GATEWAY__PRIVATE__HOST<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;platform.internal&#34;</span>
</span></span><span style="display:flex;"><span>APP__GATEWAY__PRIVATE__PORT<span style="color:#f92672">=</span><span style="color:#ae81ff">80</span>
</span></span><span style="display:flex;"><span>APP__GATEWAY__PRIVATE__SCHEME<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;http&#34;</span>
</span></span><span style="display:flex;"><span>APP__GATEWAY__PUBLIC__HOST<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;platform.internal&#34;</span>
</span></span><span style="display:flex;"><span>APP__GATEWAY__PUBLIC__PORT<span style="color:#f92672">=</span><span style="color:#ae81ff">80</span>
</span></span><span style="display:flex;"><span>APP__GATEWAY__PUBLIC__SCHEME<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;http&#34;</span>
</span></span></code></pre></div><p>Treize services, six variables chacun, une seule valeur. En lisant la config d&rsquo;un service quelconque, l&rsquo;architecture semblait plate. Tout parlait au même hôte. C&rsquo;était tout le tableau.</p>
<p>Ce ne l&rsquo;était pas.</p>
<h2 id="comment-fonctionnait-la-gateway">Comment fonctionnait la gateway</h2>
<p>La gateway se trouvait devant chaque service et gérait tout le trafic inter-services. Un service appelant l&rsquo;API content construisait une requête vers <code>http://platform.internal/content/api/</code> — la gateway la recevait, identifiait la cible depuis le chemin de l&rsquo;URL, et la transmettait au bon backend. Chaque client HTTP inter-service dans <code>framework.yaml</code> suivait le même schéma :</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">content.client</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">base_uri</span>: <span style="color:#e6db74">&#34;%http_client.gateway.base_uri%/content/api/&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">headers</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">Host</span>: <span style="color:#e6db74">&#34;%env(APP__GATEWAY__PRIVATE__HOST)%&#34;</span>
</span></span></code></pre></div><p>Le paramètre <code>http_client.gateway.base_uri</code> était assemblé depuis les variables GATEWAY. La gateway savait où tournait chaque service. Les services n&rsquo;avaient pas besoin de le savoir. De leur point de vue, tout était <code>platform.internal</code>.</p>
<p>Ça fonctionnait. Pendant des années, ça fonctionnait bien. Ajouter un service signifiait ajouter un alias DNS dans la config de la gateway, pas toucher treize fichiers <code>.env</code>. La gateway abstraisait la topologie. Les services restaient découplés du détail d&rsquo;infrastructure de qui tournait où.</p>
<h2 id="ce-que-la-gateway-absorbait">Ce que la gateway absorbait</h2>
<p>L&rsquo;abstraction avait un coût qui n&rsquo;apparaissait pas tant qu&rsquo;on n&rsquo;essayait pas de lire le système.</p>
<p>En regardant le fichier env de <code>content</code>, on voyait six variables de gateway et rien d&rsquo;autre sur la communication inter-services. Pour découvrir que <code>content</code> appelait <code>conversion</code>, <code>shorty</code> et <code>media</code>, il fallait lire <code>framework.yaml</code>. Pour découvrir que <code>pilot</code> appelait dix services externes, il fallait tracer les clients HTTP un par un et compter.</p>
<p>Le chiffre était dix. Authentication, bam, config, content, conversion, media, product, shorty, sitemap, social. Dix des treize services de la plateforme dont <code>pilot</code> dépendait à l&rsquo;exécution, aucun d&rsquo;eux visible depuis sa configuration. Six variables disaient : parle à la gateway. Elles ne disaient rien de la forme de ce qui se trouvait derrière.</p>
<p>Cette information existait — dans le code, dans la config framework, dans les têtes des gens qui avaient construit ces intégrations. Elle ne vivait juste nulle part où on pouvait la lire d&rsquo;un coup d&rsquo;œil.</p>
<h2 id="ce-que-kubernetes-a-rendu-explicite">Ce que Kubernetes a rendu explicite</h2>
<p>On-premise, la gateway était un seul hostname résolvable. Un enregistrement DNS, un jeu de variables, un seul endroit à mettre à jour. Kubernetes ne fonctionne pas comme ça. Chaque service obtient son propre nom DNS à l&rsquo;intérieur du cluster — <code>content.namespace.svc.cluster.local</code>, <code>conversion.namespace.svc.cluster.local</code>. Le trafic inter-services passe directement, service à service, sans gateway partagée.</p>
<p>Passer à Kubernetes signifiait que l&rsquo;abstraction de la gateway devait céder la place. Chaque service devait savoir, concrètement, où vivait chacune de ses dépendances. Les six variables génériques ne pouvaient pas exprimer ça.</p>
<p>Le refacto les a remplacées par des variables HOST par cible — une par dépendance de service, nommée d&rsquo;après la cible :</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># content/.env — content appelle ces quatre services</span>
</span></span><span style="display:flex;"><span>APP__CONFIG__HOST<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;platform.internal&#34;</span>
</span></span><span style="display:flex;"><span>APP__CONVERSION__HOST<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;platform.internal&#34;</span>
</span></span><span style="display:flex;"><span>APP__MEDIA__HOST<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;platform.internal&#34;</span>
</span></span><span style="display:flex;"><span>APP__SHORTY__HOST<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;platform.internal&#34;</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># pilot/.env — dix dépendances de service</span>
</span></span><span style="display:flex;"><span>APP__AUTHENTICATION__HOST<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;platform.internal&#34;</span>
</span></span><span style="display:flex;"><span>APP__BAM__HOST<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;platform.internal&#34;</span>
</span></span><span style="display:flex;"><span>APP__CONFIG__HOST<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;platform.internal&#34;</span>
</span></span><span style="display:flex;"><span>APP__CONTENT__HOST<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;platform.internal&#34;</span>
</span></span><span style="display:flex;"><span>APP__CONVERSION__HOST<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;platform.internal&#34;</span>
</span></span><span style="display:flex;"><span>APP__MEDIA__HOST<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;platform.internal&#34;</span>
</span></span><span style="display:flex;"><span>APP__PRODUCT__HOST<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;platform.internal&#34;</span>
</span></span><span style="display:flex;"><span>APP__SHORTY__HOST<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;platform.internal&#34;</span>
</span></span><span style="display:flex;"><span>APP__SITEMAP__HOST<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;platform.internal&#34;</span>
</span></span><span style="display:flex;"><span>APP__SOCIAL__HOST<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;platform.internal&#34;</span>
</span></span></code></pre></div><p>Chaque client HTTP dans <code>framework.yaml</code> a reçu sa propre <code>base_uri</code> construite depuis la variable HOST de sa cible, et le header <code>Host</code> a cédé la place à un <code>User-Agent</code> qui identifie l&rsquo;appelant :</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">content.client</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">base_uri</span>: <span style="color:#e6db74">&#34;%env(APP__HTTP__SCHEME)%://%env(APP__CONTENT__HOST)%:%env(APP__HTTP__PORT)%/content/api/&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">headers</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">User-Agent</span>: <span style="color:#e6db74">&#34;Platform Content - %semver%&#34;</span>
</span></span></code></pre></div><p>Le changement n&rsquo;est pas cosmétique. Dans l&rsquo;ancienne configuration, le header <code>Host</code> explicite garantissait que les requêtes atteignaient le bon virtual host de la gateway quelle que soit la résolution DNS. Dans la nouvelle, chaque client pointe directement vers le nom DNS de sa cible — le <code>Host</code> correct est dérivé automatiquement de la <code>base_uri</code>. L&rsquo;emplacement du header ne reste pas vide : le <code>User-Agent</code> identifie désormais le service appelant, ce qui remonte dans les logs et le traçage distribué sans instrumentation supplémentaire.</p>
<h2 id="linconfort-de-la-lisibilité">L&rsquo;inconfort de la lisibilité</h2>
<p>Le fichier env de <code>pilot</code> est passé de neuf variables de gateway à dix variables HOST spécifiques par service. Le fichier est devenu plus long. L&rsquo;architecture n&rsquo;est pas devenue plus simple — les dix dépendances étaient là avant et elles sont toujours là. Ce qui a changé, c&rsquo;est qu&rsquo;elles sont lisibles.</p>
<p>Le <a href="https://12factor.net/config" target="_blank" rel="noopener noreferrer">Facteur III</a>
 dit de stocker la config dans l&rsquo;environnement. L&rsquo;ancienne approche satisfaisait ça à la lettre : six variables, toutes dans des fichiers env, aucune en dur dans le code. Mais des variables qui effondrent le graphe de dépendances entier dans un seul hostname opaque ne sont pas vraiment de la configuration — elles sont un raccourci qui échange la lisibilité contre la commodité. Le Facteur III ne demande pas seulement que la config soit externalisée — il suppose implicitement qu&rsquo;elle reste informative une fois externalisée.</p>
<p>Le refacto n&rsquo;a rien simplifié. Il a rendu la complexité visible. Les dix variables HOST de <code>pilot</code> documentent, dans le fichier <code>.env</code> lui-même, les dix services dont il dépend. Un nouveau membre d&rsquo;équipe qui lit ce fichier apprend quelque chose de réel sur l&rsquo;architecture. L&rsquo;ancien fichier lui apprenait qu&rsquo;il y avait une gateway.</p>
<p>Il y a une version de cette histoire où on lit l&rsquo;état final et on conclut que l&rsquo;équipe a fait un travail inutile — elle a remplacé six variables par dix, toutes pointant vers le même hôte de toute façon. En développement local, <code>platform.internal</code> résout toujours au même endroit. Le comportement fonctionnel n&rsquo;a pas changé.</p>
<p>Le changement est dans ce que la config communique. Dans Kubernetes, les valeurs HOST divergent : chaque cible obtient son propre nom DNS interne au cluster, différent par environnement. Les variables portent maintenant une vraie information. Le refacto a préparé la config à être honnête sur une topologie qu&rsquo;elle simplifiait silencieusement depuis des années.</p>
]]></content:encoded></item></channel></rss>