Chronique nº 002
GitLab a augmenté le loyer en appelant ça de l'IA. Alors on a sorti 700 dépôts en un week-end.
Le devis de renouvellement a atterri comme une demande de rançon.
GitLab avait augmenté le prix, refusé de négocier, et — c’est ça qui piquait — imposé un nombre minimum de licences dont on n’avait pas besoin. La justification ? Ils avaient livré un tas de nouvelles fonctionnalités d’IA. On n’avait rien demandé, ces fonctionnalités d’IA. On payait pour elles quand même, maintenant. C’était ça, le déclencheur. Pas une présentation stratégique, pas une grande vision cloud-native. Une facture qu’on ne voulait pas signer.
Alors on a décidé de partir. Deux ingénieurs DevOps, environ 700 dépôts servant à la fois l’usine logicielle et l’usine data, un week-end, et une contrainte ferme : friction minimale pour les équipes, et tout devait fonctionner lundi matin.
Les deux extrémités étaient on-prem, sur notre Nutanix d’entreprise. La destination : un cluster microk8s mono-nœud faisant tourner le chart Helm Gitea. Avec le recul, « mono-nœud » en dit déjà long dans cette phrase.
Le plan, pour autant qu’il y en ait eu un
Vendredi soir, GitLab est passé en mode maintenance pour geler les écritures. Les communications sont parties vers toutes les équipes : ne poussez rien, on gère, on se voit lundi.
Un peu de recherche a fait remonter gitlab2gitea.py et compagnie — des scripts qui déplacent les dépôts via l’API. Ça a marché, en grande partie. La première fissure est apparue tôt : les groupes et sous-groupes ne se mappent pas proprement sur Gitea. Le modèle de groupes imbriqués de GitLab et le modèle d’orgs plus plat de Gitea n’ont pas la même forme, et aucun script ne masque ça pour vous. On a commencé à tenir une liste des « trucs qui ne se sont pas traduits ». Elle est devenue longue.
Le bordel, c’était la CI. Le bordel, c’est toujours la CI.
Déplacer l’historique git, c’est la partie facile. Tout le monde l’apprend à la dure.
Chaque .gitlab-ci.yml qui livrait du code en production devait être réécrit en Gitea Actions — syntaxe différente, comportement différent. On a cherché un convertisseur comme on cherche ses clés quand on est déjà en retard. Un script pour transformer la CI GitLab en Actions. Il n’y en avait pas qui fonctionnait. Il n’y en a jamais.
Alors on a fait ce qui était soit de la sur-ingénierie, soit du platform engineering selon le lundi auquel on pensait :
On a arrêté de porter les pipelines un par un et on a commencé à construire des primitives — des templates réutilisables assez abstraits pour couvrir chaque dépôt qui build et livre en prod.
On a sorti les Dockerfiles des dépôts individuels vers un dépôt de config central, puis on les a classés par stack : Dockerfiles java, Dockerfiles node, et ainsi de suite. On a standardisé les branches. On a écrit un seul chart Helm générique myapp-chart dont les templates pouvaient exprimer toutes les stratégies de déploiement qu’on avait à l’époque, comme ça une équipe n’avait pas droit à un pipeline sur mesure — elle avait droit à la route pavée.
Cette décision a sauvé le week-end. Elle a aussi transformé en douce deux ingénieurs DevOps en une équipe plateforme, ce qui est un autre article de blog.
Pendant ce temps, la longue traîne des « et aussi » n’arrêtait pas d’arriver :
- LFS a dû être reconfiguré côté Gitea à partir de zéro.
- Les sous-modules ont eu besoin d’être recâblés.
- Le stockage des artefacts a été migré à la main.
- Le git push en SSH a dû être reconfiguré — et Gitea était derrière HAProxy, alors on a eu le plaisir du forwarding SSH à travers un load balancer, ce qui est exactement aussi amusant que ça en a l’air.
- Les secrets CI/CD en tant que concept de premier ordre n’existaient en gros pas dans Gitea comme dans GitLab.
- Les permissions de groupe — voir plus haut, ça ne s’est pas traduit.
Le truc qu’on n’avait pas vu venir, c’était l’équipe Data
L’usine logicielle, on pouvait la raisonner. L’usine data avait une dépendance qu’on n’avait pas cartographiée : leurs pipelines tournaient sur Google Cloud Build, déclenchés par l’intégration native de GitLab au push. Gitea n’avait pas d’intégration de déclenchement équivalente. Tout leur chemin de build supposait que GitLab existait.
Le correctif était moche et je ne vais pas prétendre le contraire. On a monté une instance fantôme GitLab Community, et on a écrit un job CI qui clonait et poussait les dépôts data dessus à chaque changement — uniquement pour que le déclencheur GitLab de Cloud Build continue de se déclencher.
La solution fonctionnait. À quel prix.
On avait quitté GitLab pour économiser de l’argent, et la première chose que la migration nous a forcés à faire, c’est de faire tourner un second GitLab.
Puis Gitea a commencé à s’effondrer aux pics de charge
Il tombait juste quand la charge était la plus forte. On l’a traqué à travers trois couches :
- Les descripteurs de fichiers Linux — Gitea, en indexant les gros dépôts, les épuisait.
- Postgres — la configuration HA réélisait un nouveau master toutes les deux secondes. (Si ça vous dit quelque chose, c’est la même classe de panne qui a bouffé deux de nos trois réplicas.)
- Redis — le chart Helm HA réélisait son instance d’écriture toutes les deux secondes lui aussi, ou échouait aux liveness probes pour des raisons qui n’ont jamais eu vraiment de sens.
À la troisième, on se demandait ouvertement si un microk8s mono-nœud avait jamais été le bon foyer pour cette charge de travail. On n’a jamais eu de réponse claire. On l’a rendu assez stable pour lundi, et « assez stable pour lundi », c’est son propre genre d’ingénierie.
Deux pièges qui ont failli tout faire capoter
L’outil de migration mentait sur le fait d’avoir terminé. gitlab2gitea ne déplaçait pas réellement le code quand il rendait la main — il lançait une tâche de migration que Gitea exécutait ensuite de façon asynchrone en arrière-plan. On a trouvé des dépôts coincés sur ce petit spinner de chargement en forme de tasse de thé, « migration en cours », pour toujours. Le script disait succès. Le code était encore dans l’ancien GitLab. Si vous faisiez confiance au code de sortie, vous faisiez confiance à un mensonge.
Le mode maintenance a été coupé — brièvement. Quelqu’un a désactivé le gel des écritures pour faire un peu de test, quelques écritures sont passées vers l’ancien GitLab, et on a eu droit à resynchroniser ces dépôts de GitLab vers Gitea une fois de plus. Un gel qu’on peut désactiver discrètement n’est pas un gel.
Lundi
Big bang. Le sous-domaine gitlab. a été raccourci en simple git., ce qui est la partie la plus satisfaisante de n’importe quelle migration — l’ancien nom arrête tout simplement de résoudre.
Les devs ont râlé. L’UI de Gitea n’est pas le truc poli qu’est GitLab, et ils l’ont senti. La CI/CD a fait son boulot, même si les runners échouaient par intermittence pour des raisons qui nous ont envoyés dans encore un autre terrier de lapin — ce qui, agaçant, a rendu nos pipelines, notre caching et notre configuration d’artefacts Nexus réellement meilleurs. Et l’entreprise a économisé une grosse facture. Cette partie-là était réelle et immédiate.
Ce que ça a vraiment coûté
Le week-end en lui-même était un trip. Deux personnes, du scripting, de l’architecture, de l’infra, toute la boîte à outils allumée d’un coup — il n’y a rien comme une deadline serrée et une bascule propre pour vous rappeler pourquoi vous faites ce métier.
Mais la dette a continué de s’accumuler pendant des semaines après. Le GitLab fantôme. Les secrets qu’on a re-platformés à la main. Le modèle de permissions qu’on n’arrêtait pas de rapiécer. Les gremlins des runners.
Voici le bilan honnête, et le seul enseignement qui compte : une plateforme managée comme GitLab est chère parce qu’elle fait en silence une quantité énorme de travail que vous ne voyez jamais — déplacer le code, le stocker, l’indexer, déclencher des choses, le garder en sécurité. Le renouvellement avait l’air extorsionnaire jusqu’au moment où on a détaillé poste par poste notre propre week-end, plus les mois de dérive qui ont suivi.
On n’a pas fait disparaître ce coût. On l’a déplacé quelque part où la facture ne pouvait pas le voir — sur nous-mêmes. Parfois c’est le bon arbitrage. Ne vous racontez juste pas que c’était gratuit.