Chronique nº 004
Le schéma disait que l'appli parlait à trois choses. Le câble en disait onze.
Le schéma d’architecture, c’est l’histoire que l’équipe se raconte à propos du réseau. Le réseau, lui, ne lit pas le schéma.
On l’a appris à la dure, sur une autre migration que celle de GitLab — même année, même réflexe de vouloir déplacer un truc lourd le temps d’un week-end. Un lot d’applis quittait un cluster pour un autre, et le plan, sur le papier, avait l’air propre. Chaque service avait sa petite boîte avec deux ou trois flèches qui en sortaient : une base de données, un cache, peut-être une file de messages. On déplace les boîtes, on redessine les flèches, on bascule. Bouclé pour dimanche.
Sauf que les flèches, c’était une supposition. Quelqu’un les avait dessinées dix-huit mois plus tôt, et depuis, les applis s’étaient discrètement fait des amis.
Chaque dépendance silencieuse que tu ne trouves pas avant une bascule, tu la trouves pendant — en général à 2h du matin, en général avec un client au téléphone.
Alors avant de déplacer quoi que ce soit, on a fait le truc pas glamour. On a regardé.
On ne peut pas migrer ce qu’on ne voit pas sortir
La question à laquelle on avait besoin de répondre, ce n’était pas « à quoi cette appli est censée parler ». C’était « à quoi cette appli, là, maintenant, ouvre concrètement des connexions ». Ce sont deux questions différentes, et c’est dans l’écart entre les deux que vivent les pannes.
La doc ne pouvait pas répondre. Le schéma ne pouvait pas répondre. Les développeurs — qu’ils soient bénis — répondaient avec assurance et se trompaient sur un tiers, non par négligence mais parce que les gens qui avaient écrit ces intégrations étaient partis, et que le code qui passait les appels était enfoui trois dépendances plus loin, dans une bibliothèque que personne ne lit.
La seule source de vérité honnête, c’est le chemin d’egress lui-même. Alors on l’a instrumenté.
On n’est pas allé chercher un service mesh. Déployer Istio pour répondre à une question, c’est comme acheter un restaurant pour savoir si on aime la soupe. On ne voulait pas de sidecars sur tout, et on n’avait pas le week-end pour materner le déploiement d’un mesh par-dessus la migration qu’on était déjà en train de faire.
On est descendu plus bas. eBPF, à surveiller connect() et accept() au niveau du noyau, sur chaque nœud, sans aucune modification des applis :
# Cilium était déjà sur le cluster, donc Hubble était à un flag de distance —
# chaque flux L3/L4, par pod, par destination, sans toucher aux applis.
hubble observe --since 24h --type trace \
--namespace payments \
-o jsonpath='{.destination.identity} {.IP.destination} {.l4.TCP.destination_port}' \
| sort | uniq -c | sort -rn
Sur les nœuds qui n’étaient pas meshés, on a obtenu la même image directement depuis le noyau — un petit programme BPF sur le tracepoint connect, ou tout simplement conntrack et les flow logs quand on avait besoin de quelque chose de plus bête et de plus rapide :
# Le recoupement fainéant : à qui ce pod est-il vraiment connecté, là, maintenant.
conntrack -L -p tcp | awk '{print $5, $7}' | sort | uniq -c | sort -rn
Puis on a laissé tourner. Pas une heure — une semaine complète, pour attraper les trucs qui ne se produisent que sur un planning. Le batch nocturne. Le job de réconciliation du dimanche. Le run de facturation mensuel qui parle à une passerelle de paiement exactement une fois tous les trente jours, et qui serait resté invisible à tout ce qui durerait moins longtemps.
Ce que le câble disait et que le schéma ne disait pas
Le schéma, pour un service représentatif, annonçait trois dépendances : sa base de données, Redis, et un service d’authentification interne.
Le câble en disait onze. Voici l’écart, et l’écart, c’est tout l’article :
- Une IP en dur vers un hôte qui n’existait plus. L’appli la retentait à chaque démarrage, avalait le timeout, et passait à autre chose. Personne ne l’avait remarqué parce que personne ne regardait — mais cette IP était sur le point d’appartenir à autre chose dans le nouveau réseau, et une connexion silencieuse vers un hôte mort devient une connexion bruyante vers le mauvais hôte à la seconde où l’adresse est recyclée.
- Une deuxième base de données dont personne n’avait parlé. Un read-replica de reporting, branché des années plus tôt pour un seul dashboard, toujours sollicité quelques centaines de fois par heure. Pas sur le schéma. Pas dans le runbook. Aurait collé un 500 au dashboard matinal d’un directeur le lundi, et on aurait passé la bascule à courir après un symptôme à trois sauts de la cause.
- Un appel droit vers l’internet public. Un SDK d’un fournisseur qui appelait une API SaaS sur l’internet ouvert, depuis un service dont tout le monde jurait qu’il ne parlait qu’à des systèmes internes. La politique d’egress du nouveau cluster allait être plus stricte. Cet appel aurait été jeté par terre, en silence, et serait remonté sous forme d’un vague ticket « la fonctionnalité X est lente parfois » des semaines plus tard.
- L’équipe Data. Encore. Évidemment que c’était l’équipe Data. Un job qui allait piocher dans GCP — le même puits gravitationnel externe qui nous avait mordus sur la migration GitLab. Pipeline différent, même leçon : l’usine à données a toujours une corde de plus attachée à quelque chose hors du bâtiment que ce dont quiconque se souvient.
- Un cache qui était en fait deux caches. L’appli parlait au Redis du schéma et à un second Redis vers lequel elle avait basculé pendant un incident des mois plus tôt et vers lequel elle n’était jamais revenue. Elle tournait sur le secours. Le « primaire » du schéma était décoratif.
Aucun de ces trucs n’était exotique. Aucun n’exigeait un génie pour le trouver. Ils exigeaient de regarder le vrai trafic plutôt que le dessin du trafic — et d’avoir la patience de regarder pendant une semaine plutôt qu’une après-midi.
La carte des dépendances, mais méritée
À la fin de la semaine, on n’avait pas la version du schéma de la carte des dépendances. On avait la vraie — construite à partir des flux, pas de la mémoire. Chaque destination qu’une appli ouvrait, à quelle fréquence, sur quel port, et surtout, sur quel planning.
Cet artefact a fait trois choses :
- Il a rendu la bascule ennuyeuse. On a pré-créé chaque règle d’egress, chaque trou de firewall, chaque entrée DNS dont les applis avaient réellement besoin — y compris les huit par service que personne n’avait documentées — avant de déplacer le moindre pod. Rien n’a découvert un chemin manquant à l’exécution, parce qu’on les avait déjà tous trouvés au moment de la surveillance.
- Il a éliminé le poids mort. La connexion vers l’hôte qui n’existait pas ? Supprimée, pas migrée. Le failover de cache que personne n’avait annulé ? Annulé. On ne porte pas un cadavre dans la nouvelle maison.
- Il est devenu un signal d’alerte précoce. On a gardé la surveillance d’egress active après le déplacement. Une nouvelle destination apparaissant dans les flux et absente de la carte signifiait soit une nouvelle intégration légitime dont personne n’avait prévenu l’équipe plateforme, soit un problème. Dans les deux cas, on voulait le savoir le jour où ça commençait, pas le jour où ça cassait.
Une carte des dépendances que tu génères une fois, c’est de la documentation. Une carte des dépendances que tu continues de générer, c’est de la supervision.
Ce que j’en ai vraiment retiré
- Instrumente l’egress avant la migration, pas pendant l’incident. Le moment le moins cher pour découvrir qu’une appli parle à un hôte mort, c’est une semaine avant de la déplacer. Le moment le plus cher, c’est pendant qu’elle est tombée.
- Surveille un cycle complet, pas un instant. Une heure de capture trouve les dépendances bavardes. Une semaine trouve les dangereuses — les batchs, les runs mensuels, les trucs qui ne se réveillent que sur un planning. L’appel rare, c’est celui qui fait tomber la bascule, précisément parce qu’il est assez rare pour que personne ne s’en souvienne.
- Prends l’outil le plus léger qui voit le câble. Si tu fais déjà tourner Cilium, Hubble est un flag. Sinon, le noyau te dira quand même tout via
conntrack, les flow logs, ou quelques lignes d’eBPF. Tu n’as pas besoin d’un service mesh pour répondre à « à quoi ce truc est-il connecté ». N’achète pas le restaurant pour goûter la soupe. - Le schéma est une hypothèse. Le flow log est la preuve. Fais confiance au second. Quand ils sont en désaccord — et ils le seront — c’est le réseau qui a raison et le dessin qui est vieux.
- Garde la surveillance active. La vraie valeur de la carte, ce n’est pas la migration. C’est qu’une flèche inattendue dans les flux, la semaine d’après, c’est la première panne qui s’annonce d’elle-même, tôt.
On est sorti de cette migration avec moins de surprises que de celle de GitLab, et la différence tenait entièrement à la semaine qu’on avait passée à regarder avant de toucher à quoi que ce soit. Le schéma disait trois. Le câble disait onze. On a déplacé les onze, et le lundi a été calme.
Le calme, c’est tout le métier. Personne ne te donne de médaille pour une migration que personne n’a remarquée — ce qui est exactement comme ça que tu sais que tu l’as réussie.