Aller au contenu principal

GitHub ARC - Runners CI/CD auto-hébergés sur Kubernetes

GitHub Actions Runner Controller

Contexte​

Le CATIE disposait d'une infrastructure CI/CD sur GitLab. La décision de migrer l'ensemble des dépôts vers GitHub a entraîné la reconstruction complète de cette infrastructure sur la nouvelle plateforme. J'ai porté cette migration côté exécution : poser GitHub Actions comme socle CI/CD et déployer Actions Runner Controller (ARC) sur le cluster Kubernetes interne, pour disposer de runners auto-hébergés, éphémères et élastiques, capables d'absorber les pipelines de l'ensemble des équipes. Les workflows eux-mêmes sont mutualisés dans une bibliothèque de workflows réutilisables.

Pourquoi ARC plutôt que des runners auto-hébergés classiques​

Un runner auto-hébergé classique s'enregistre manuellement sur une machine et tourne en permanence : un runner correspond à une machine, sans élasticité, et son état se pollue d'un job à l'autre si rien ne le nettoie. ARC gère les runners comme des pods Kubernetes éphémères : un pod par job, détruit à la fin, et un nombre de runners ajusté en continu selon la file d'attente GitHub Actions. Les runners hébergés par GitHub étaient l'autre option ; ils ne donnent accès ni au réseau interne ni aux ressources matérielles du cluster, et leur coût croît avec le volume de minutes consommées.

Architecture : contrôleur et scale set​

Le déploiement repose sur les deux charts Helm officiels du mode "runner scale set" (version 0.13.0), dans deux namespaces séparés :

Le contrôleur (gha-runner-scale-set-controller) orchestre le cycle de vie des pods de runner : création à la réception d'un job, mise à l'échelle selon la charge, suppression après exécution. Il crée pour chaque scale set un pod listener, qui maintient la connexion avec l'API GitHub et reçoit les jobs en attente.

Le chart est lui-même déployé par GitHub Actions à chaque push, avec le même workflow réutilisable deploy-helm que les autres services du cluster.

Observabilité​

J'ai activé les métriques du listener et du contrôleur, collectées par le Prometheus du cluster : jobs démarrés et terminés par dépôt et par workflow, runners enregistrés, occupés et inactifs, nombre de runners souhaité, et histogrammes de durée de démarrage et d'exécution des jobs. Ces métriques permettent de détecter des jobs bloqués ou un pool sous-dimensionné avant que les équipes ne le constatent, et ont guidé les réglages de capacité : le plafond est passé de 20 à 40 puis 60 runners au fil de la montée en charge.

Optimisation des performances​

L'observation des durées de job a fait apparaître plusieurs goulets d'étranglement, tous situés dans le réseau et les téléchargements plutôt que dans le calcul :

  • MTU et overlay Calico. L'overlay réseau Calico impose un MTU de 1480 octets, alors que les réseaux bridge créés à la volée par Docker dans le conteneur DinD restent à 1500. Les paquets trop grands étaient silencieusement perdus (échec de la découverte du MTU de chemin), ce qui provoquait des blocages de téléchargement difficiles à diagnostiquer. J'ai aligné le MTU du démon Docker sur celui de l'overlay et ajouté un ajustement du MSS TCP (règle TCPMSS) pour les réseaux que Docker crée par job.
  • Miroir de paquets. Le miroir Ubuntu générique, non géolocalisé, était environ 200 fois plus lent depuis le réseau du CATIE que le miroir français. Une redirection DNS seule ne suffisait pas, le miroir français servant ses fichiers par hôte virtuel : chaque pod embarque un petit reverse proxy nginx qui réécrit l'en-tête Host. Mesuré sur un pod isolé, apt update suivi de l'installation de curl passe de plus de 170 s à 12,6 s.
  • Caches partagés. Les pods passent par un cache APT (apt-cacher-ng) et un miroir de registre Docker Hub mutualisés dans le cluster, avec repli automatique vers les sources publiques si le cache est indisponible.
  • Parallélisme des téléchargements. Le démon Docker télécharge par défaut trois couches en parallèle, ce qui bridait les images volumineuses (une image Zephyr de 4,4 Go et 11 couches demandait environ deux minutes d'initialisation). La limite est passée à dix.

Résultats​

Les runners ARC exécutent les pipelines GitHub Actions de l'organisation, appelés par les workflows mutualisés de plusieurs dizaines de dépôts, sans runner à enregistrer ni à nettoyer à la main. Le pool garde 10 runners disponibles et monte jusqu'à 60 selon la file d'attente ; son plafond a été relevé deux fois à partir des métriques collectées. Les corrections réseau ont supprimé les blocages de téléchargement liés au MTU, et le proxy de miroir ramène apt update suivi d'une installation de plus de 170 s à 12,6 s sur un pod isolé.

Liens​

Articles liés

9 articles du blog détaillent des notions mises en pratique dans ce projet.

  1. CI/CD

    GitHub Actions : tests de simulation sur Kubernetes

    Runners auto-hébergés dans le cluster, qui joignent directement l'API Kubernetes pour créer et suivre les Jobs de simulation.

  2. Infrastructure as Code

    Ansible : Vault

    Stockage chiffré des tokens GitHub et des identifiants lors du provisionnement d'ARC via Ansible.

  3. Observabilité

    Loki

    Collecte centralisée des logs des runners ARC pour le débogage et l'audit.

  4. Observabilité

    Prometheus

    Collecte par le Prometheus du cluster des métriques du listener et du contrôleur ARC (jobs, runners occupés et inactifs, durées de démarrage et d'exécution), qui ont guidé le relèvement du plafond de 20 à 60 runners.

  5. Infrastructure as Code

    Ansible

    Provisionnement automatique du cluster Kubernetes et installation d'ARC via des playbooks Ansible.

  6. Réseau

    Nginx

    Reverse proxy Nginx embarqué dans chaque pod de runner, qui réécrit l'en-tête Host pour rediriger les téléchargements de paquets vers un miroir servi par hôte virtuel.

  7. CI/CD

    GitHub Actions

    Runners auto-hébergés déployés avec ARC sur un cluster Kubernetes interne, ciblés par runs-on dans les workflows de l'organisation.

  8. CI/CD

    GitHub Actions : ARC

    Déploiement d'ARC en mode runner scale set (charts Helm officiels 0.13.0) sur le cluster kubeadm du CATIE, avec authentification par GitHub App et runners éphémères Docker-in-Docker maintenus entre 10 et 60 pods.

  9. CI/CD

    GitHub Actions : runner auto-hébergé

    Modèle du runner auto-hébergé classique (une machine, un runner permanent) comparé à ARC, retenu pour ses pods éphémères par job et son ajustement continu à la file d'attente GitHub Actions.