Aller au contenu
CI/CD & Automatisation medium

Runners éphémères GitHub Actions

9 min de lecture

Les runners éphémères sont créés à la demande et détruits après chaque job. Ce pattern élimine les risques de persistence de données entre jobs et garantit un environnement propre à chaque exécution.

  • Comprendre pourquoi un runner jetable est plus sûr qu'un runner persistant
  • Activer le mode --ephemeral du runner GitHub
  • Mettre en place l'auto-scaling avec ARC (Kubernetes) ou des VM cloud
  • Conteneuriser un runner éphémère avec Docker
  • Gérer le pool et compenser l'absence de cache local
  1. Données résiduelles : fichiers, caches, credentials d'un job précédent
  2. Drift de configuration : l'environnement change au fil du temps
  3. Surface d'attaque : un job malveillant peut compromettre les suivants
  1. Isolation parfaite : comme les runners GitHub-hosted
  2. Reproductibilité : même environnement à chaque exécution
  3. Sécurité : pas de persistence entre jobs

Le runner GitHub supporte nativement le mode éphémère :

Fenêtre de terminal
./config.sh --url https://github.com/OWNER/REPO \
--token TOKEN \
--ephemeral

Comportement :

  • Le runner accepte un seul job
  • Après exécution, il se désenregistre automatiquement
  • La VM/container peut être détruite

Créer un runner à la main pour chaque job n'est pas tenable. L'auto-scaling provisionne et détruit les runners automatiquement selon la charge.

ARC est la solution officielle pour Kubernetes :

runner-deployment.yaml
apiVersion: actions.summerwind.dev/v1alpha1
kind: RunnerDeployment
metadata:
name: ephemeral-runners
spec:
replicas: 1
template:
spec:
ephemeral: true
repository: owner/repo
labels:
- self-hosted
- linux
- ephemeral
---
apiVersion: actions.summerwind.dev/v1alpha1
kind: HorizontalRunnerAutoscaler
metadata:
name: ephemeral-runners-autoscaler
spec:
scaleTargetRef:
name: ephemeral-runners
minReplicas: 0
maxReplicas: 10
metrics:
- type: TotalNumberOfQueuedAndInProgressWorkflowRuns
repositoryNames:
- owner/repo

Script de scaling avec AWS :

launch-ephemeral-runner.sh
#!/bin/bash
# Créer une VM
INSTANCE_ID=$(aws ec2 run-instances \
--image-id ami-xxxxx \
--instance-type t3.medium \
--user-data file://runner-init.sh \
--query 'Instances[0].InstanceId' \
--output text)
echo "Launched: $INSTANCE_ID"

Script d'initialisation :

#!/bin/bash
# runner-init.sh (user-data)
# Télécharger le runner
cd /opt
curl -o runner.tar.gz -L https://github.com/actions/runner/releases/download/v2.xxx.x/actions-runner-linux-x64-2.xxx.x.tar.gz
tar xzf runner.tar.gz
# Obtenir un token d'enregistrement (via API GitHub)
TOKEN=$(curl -s -X POST \
-H "Authorization: token $GITHUB_PAT" \
https://api.github.com/repos/OWNER/REPO/actions/runners/registration-token \
| jq -r '.token')
# Configurer en mode éphémère
./config.sh --url https://github.com/OWNER/REPO \
--token $TOKEN \
--ephemeral \
--unattended \
--labels ephemeral,linux
# Exécuter (va s'arrêter après un job)
./run.sh
# Auto-destruction
aws ec2 terminate-instances --instance-ids $(curl -s http://169.254.169.254/latest/meta-data/instance-id)

Sans Kubernetes, Docker suffit à obtenir des runners jetables : un conteneur par job, détruit à la fin de l'exécution.

Dockerfile.runner
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y \
curl jq git \
&& rm -rf /var/lib/apt/lists/*
# Télécharger le runner
RUN curl -o /tmp/runner.tar.gz -L \
https://github.com/actions/runner/releases/download/v2.xxx.x/actions-runner-linux-x64-2.xxx.x.tar.gz \
&& mkdir /runner \
&& tar xzf /tmp/runner.tar.gz -C /runner \
&& rm /tmp/runner.tar.gz
WORKDIR /runner
COPY entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
entrypoint.sh
#!/bin/bash
./config.sh --url $REPO_URL \
--token $RUNNER_TOKEN \
--ephemeral \
--unattended \
--labels docker,ephemeral
./run.sh
# Le container s'arrête après un job
docker-compose.yml
version: '3.8'
services:
runner:
build:
context: .
dockerfile: Dockerfile.runner
environment:
- REPO_URL=https://github.com/owner/repo
- RUNNER_TOKEN=${RUNNER_TOKEN}
restart: "no" # Ne pas redémarrer (éphémère)
deploy:
replicas: 3

Un pool de runners éphémères se pilote : trouver l'équilibre entre réactivité (éviter les cold starts) et coût (ne pas garder trop de runners inactifs).

Pour éviter les cold starts, maintenez un pool minimum :

# Kubernetes HPA
spec:
minReplicas: 2 # Toujours 2 runners prêts
maxReplicas: 20 # Scale jusqu'à 20 si nécessaire
# Prometheus metrics avec ARC
- job_name: 'actions-runner-controller'
static_configs:
- targets: ['actions-runner-controller-metrics:8080']

Métriques utiles :

  • actions_runner_controller_running_runners : runners actifs
  • actions_runner_controller_pending_runners : en attente de job
  • actions_runner_controller_registered_runners : total enregistrés
name: Build with Ephemeral Runner
on:
push:
branches: [main]
# Aucun droit par défaut : chaque job demande le minimum
permissions: {}
jobs:
build:
runs-on: [self-hosted, ephemeral, linux]
permissions:
contents: read
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- name: Build
run: |
npm ci
npm run build
# Pas besoin de cleanup : le runner sera détruit
deploy:
needs: build
runs-on: [self-hosted, ephemeral, linux]
permissions:
contents: read
# Nouveau runner frais pour le déploiement
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- run: ./deploy.sh

Le modèle éphémère a un prix : pas de cache local, des cold starts, un coût d'infrastructure variable. Voici les points à anticiper.

MéthodeCold start
Container Docker5-15s
VM cloud30-90s
Kubernetes pod10-30s

Avec des runners éphémères, le cache local n'existe pas. Options :

  1. actions/cache : cache sur GitHub Storage
- uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: ~/.npm
key: npm-${{ hashFiles('package-lock.json') }}
  1. Registry de cache : images Docker avec dépendances pré-installées
container:
image: myregistry.com/node-with-deps:latest
  1. Volume partagé : NFS ou EFS monté sur les runners
  • Plus de VMs créées/détruites = plus de coût si facturation à l'heure
  • Optimiser avec des instances spot/preemptible
  • Équilibrer entre pool minimum et coût
  • Un runner éphémère accepte un seul job puis se désenregistre, l'isolation des runners hosted, sur votre infra.
  • Le mode natif s'active avec ./config.sh --ephemeral : pas d'outil tiers requis.
  • En Kubernetes, Actions Runner Controller gère création, scaling et destruction automatiquement.
  • Sans cache local, compensez avec actions/cache, des images pré-garnies ou un volume partagé.
  • Gardez un pool minimum chaud pour absorber les cold starts sans exploser le coût.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn