devops
Quand le runner CI est aussi la machine de production
Quand le runner CI est aussi la machine de production
Chaque workflow ici tourne sur un runner auto-hébergé. Il y en a trois : un pour le lint et les tests, un sur la machine de dev, un qui est littéralement la VM de production. GitHub Actions n'est que la couche de déclenchement et d'orchestration — aucune minute de runner hébergé n'est facturée nulle part.
Le bénéfice sur le runner de production est direct : « exécute cette commande sur le runner » et « exécute cette commande sur le serveur qui doit finir par héberger les nouveaux conteneurs » sont la même phrase. Pas d'étape de déploiement SSH séparée, pas de clé secrète à gérer pour ça, pas de dérive entre « ce que la CI a construit » et « ce que le serveur récupère ».
Le piège qui a mis du temps à se voir
Un job CI qui monte une partie de la stack — docker compose up -d --build backend pour la tester en smoke — échouait par intermittence, et j'ai d'abord supposé que c'était un conteneur périmé laissé par un run précédent annulé. J'ai ajouté une étape de nettoyage docker compose down. Ça a quand même échoué.
La vraie cause : les noms de conteneurs Docker sont uniques par démon, pas par projet Compose. Un override container_name: n'est pas namespacé par le label de projet comme le sont les volumes et les réseaux. Comme le runner s'exécute directement sur la même machine et le même démon Docker que la stack de production en direct, et que le fichier compose de dev utilisait les mêmes valeurs de container_name que la production, le up du job CI entrait en collision avec des conteneurs de production actuellement en fonctionnement au nom identique. L'étape de nettoyage n'aurait jamais pu aider — elle est cadrée sur le label de projet du job CI lui-même, et la production tourne sous un autre.
Le correctif a été de préfixer chaque nom de conteneur dev/CI distinctement de celui de la production. Évident une fois vu. Invisible avant, parce que la défaillance ressemblait exactement à un problème de nettoyage instable ordinaire.
Ce qu'il faut garder de ça
- Les runners auto-hébergés valent la peine pour le coût et pour effacer le saut de déploiement — mais tu as échangé l'isolation contre ça. Le runner partage un noyau, un démon Docker, du disque et de la RAM avec tout le reste sur cette machine.
- Donne à chaque environnement son propre nom de projet Compose et ses propres noms de conteneurs explicites. Les deux sont namespacés différemment et tu as besoin que les deux soient distincts.
- Ne laisse jamais un job CI lancer un large
docker system pruneou quoi que ce soit avec--volumessur une machine partagée. Un conteneur de production arrêté et son volume peuvent être balayés dans la même passe — c'est exactement comme ça que tout l'historique d'un outil de monitoring a été définitivement supprimé une fois. - Surveille la marge de RAM, pas seulement le CPU. Un runner en plein build plus la stack de production plus une stack de monitoring peuvent dépasser le plafond d'une petite VM, et ce qui se fait tuer par l'OOM n'est pas toujours ce que tu voudrais.