CI/CD · Auto-hébergement · Qualité logicielle

Une infrastructure CI/CD auto-hébergée, gouvernée par ses propres contrôles

Contexte

J’exploite ma propre chaîne d’intégration continue plutôt que de m’appuyer sur des services cloud : forge Git, exécuteurs CI, qualité de code, sécurité. Trois raisons à ce choix :

  • Souveraineté

    Aucun code, à moi ou à un client, ne transite par un service tiers dont je ne maîtrise ni les conditions d’usage ni la pérennité.

  • Maîtrise

    Un environnement de construction figé et reproductible, dont chaque dépendance est connue et versionnée.

  • Dimension démonstrative assumée

    Une chaîne CI/CD auto-hébergée et documentée est une pièce de dossier devant un client, au même titre que ce site.

Toute la pile est open source : c’est une contrainte de licence choisie, pas une préférence, et elle détermine directement certains choix d’outillage (voir plus bas). L’ensemble tourne sur une machine à ressources limitées, ce qui pèse sur chaque décision d’architecture CI/CD — un budget mémoire et CPU serré force des arbitrages qu’un cluster surdimensionné n’impose pas.

Compétences exercées sur ce projet

Démarche

Chaque décision structurante est écrite en ADR (contexte, décision, conséquences favorables et défavorables, alternatives écartées, coût de retour en arrière). Une trentaine à ce jour. Quelques-unes illustrent la méthode :

Conteneurs plutôt qu’hyperviseur. Sur une itération précédente, des services installés à même l’hôte se cassaient mutuellement par partage de bibliothèques système. J’ai isolé chaque service en conteneur, piles versionnées indépendamment. Un hyperviseur aurait imposé de réinstaller l’hôte et de repartitionner un stockage déjà quasi plein et sans sauvegarde : le risque dépassait le bénéfice à ce stade.

Une forge tout-en-un plutôt qu’un assemblage de composants indépendants. GitLab CE plutôt qu’un empilement (dépôt Git + moteur CI + registre séparés), avec un réglage explicite qui ramène sa consommation mémoire d’environ 8 à 4 Go — nécessaire sur une machine aux ressources comptées. Un assemblage aurait été plus économe en régime permanent, mais il impose d’intégrer et de maintenir soi-même ce que GitLab fournit déjà assemblé : pour un opérateur seul dont le temps est la ressource rare, l’arbitrage va à l’intégré.

Qualité C++ par greffon communautaire, faute d’analyseur propriétaire compatible avec la contrainte open source. J’assume et je documente la limite plutôt que de la taire : la couverture des défauts est exactement celle des analyseurs statiques importés (cppcheck, clang-tidy), sans analyse inter-procédurale propriétaire. Une couverture partielle mais connue vaut mieux qu’une fausse impression d’exhaustivité.

Qualifier l’isolement réseau avec exactitude. Le projet visait au départ un fonctionnement complètement coupé du réseau. Le relevé de l’état réel a montré que ce n’était pas le cas : l’hôte garde une sortie Internet nécessaire aux mises à jour et à certaines dépendances de build. J’ai requalifié l’objectif — « souverain, privé et non exposé, à chaîne de construction hermétique » — plutôt que de laisser une affirmation inexacte dans la documentation. Un lecteur capable de vérifier une affirmation fausse est exactement celui qu’elle visait à convaincre : la découverte de l’écart aurait coûté plus cher que l’aveu.

Décisions clés de ce projet

Solution

Architecture de la plateforme

La chaîne repose sur trois briques principales, chacune choisie et documentée séparément :

  • La forge

    GitLab CE et son exécuteur (Runner) : hébergement du code, registre d’images de conteneurs, orchestrateur de pipeline. Image Docker épinglée à une version précise, jamais un tag flottant.

  • La qualité de code

    SonarQube Community, avec un greffon communautaire d’analyse C++ qui importe les rapports de cppcheck et clang-tidy plutôt que de dépendre d’un analyseur propriétaire. Compatible avec la contrainte 100 % open source du projet.

  • Le réseau

    Traefik en reverse proxy, TLS automatisé sur les services exposés en interne.

Le pipeline d’intégration continue enchaîne cinq étapes — build, test, couverture, analyse statique, remontée SonarQube — avec une porte de qualité bloquante : un pipeline qui ne peut pas échouer n’est pas un contrôle.

L’outillage de vérification est lui-même versionné à la racine du dépôt : détection de secrets (gitleaks, avec des règles ajoutées pour des motifs propres à ce projet), lint de documentation, détection de liens morts — déclenchés en CI sur toute modification de la documentation ou des scripts. Plus de trente scripts de contrôle en lecture seule vérifient chacun une affirmation précise de la documentation : pas un test générique, un test par phrase écrite.

La règle qui referme la boucle : « toute affirmation non couverte par une commande de vérification porte la mention ⚠️ À VÉRIFIER ». Quatre statuts sont possibles pour chaque affirmation documentaire :

  • Vérifié

    Couvert par une commande de vérification qui passe.

  • Décidé, non réalisé

    La décision est prise mais pas encore mise en œuvre.

  • Hérité non vérifié

    Affirmation reprise d’avant la règle, traitée comme fausse jusqu’à preuve du contraire.

  • Obsolète

    Conservé pour trace, sans valeur actuelle.

La discipline s’applique à la plateforme elle-même, pas seulement au code qu’elle héberge.

Résultat

Le dernier audit complet a catalogué 21 risques, dont 14 étaient absents ou invisibles dans le registre précédent — la mesure de ce que l’audit apporte par rapport à la documentation d’avant. Deux affirmations de sécurité tenues pour vraies (accès distant par clé uniquement, restreint au réseau local) se sont révélées fausses à la vérification : c’est cet écart qui a justifié la règle de vérifiabilité imposée depuis à toute la documentation, plutôt que l’inverse.

Un incident réel est documenté sans en atténuer la portée : un redémarrage a rendu l’ensemble des services injoignables pendant environ vingt-deux heures avant rétablissement complet. La cible d’hermétisme total de la chaîne CI n’est, à ce jour, pas atteinte — le contrôle correspondant est volontairement conçu pour échouer tant que l’écart existe, plutôt que d’être assoupli pour passer au vert.

Debian 13 · Docker · GitLab CE / Runner · SonarQube + sonar-cxx · Traefik · gitleaks

Un besoin similaire ?

Parlons de votre projet