Aller au contenu
CI/CD & Automatisation medium

Maintenance des runners self-hosted

11 min de lecture

Les runners self-hosted nécessitent une maintenance régulière pour rester performants et sécurisés. Ce guide couvre les opérations essentielles : mises à jour, monitoring, nettoyage et scaling.

  • Maintenir à jour le runner, l'OS et les outils
  • Superviser les runners avec des health checks et Prometheus
  • Nettoyer Docker, le répertoire de travail et les caches locaux
  • Faire évoluer le parc, manuellement ou par auto-scaling
  • Assurer la haute disponibilité pour éviter les points de défaillance

Trois éléments demandent une attention différente : le runner (géré par GitHub), l'OS et les outils de build (à votre charge).

Le runner se met à jour automatiquement quand GitHub déploie une nouvelle version. Lors d'un job, si une mise à jour est disponible, elle s'applique avant l'exécution.

Vérifier la version :

Fenêtre de terminal
./run.sh --version
# ou
cat /opt/actions-runner/.runner | jq '.agentVersion'
Fenêtre de terminal
# Ubuntu/Debian
sudo apt update && sudo apt upgrade -y
# Avec redémarrage planifié
sudo apt install unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades

Maintenez à jour les outils utilisés par vos workflows :

Fenêtre de terminal
# Node.js via nvm
nvm install --lts
nvm alias default lts/*
# Docker
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io
# kubectl
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install kubectl /usr/local/bin/

Un runner qui tombe sans alerte bloque toute la CI silencieusement. Le monitoring rend visibles l'état de la machine et la disponibilité du runner.

Fenêtre de terminal
# CPU, mémoire, disque
top -bn1 | head -20
free -h
df -h
# Processus du runner
ps aux | grep Runner.Listener
healthcheck.sh
#!/bin/bash
RUNNER_DIR="/opt/actions-runner"
# Vérifier que le runner tourne
if ! pgrep -f "Runner.Listener" > /dev/null; then
echo "CRITICAL: Runner not running"
exit 2
fi
# Vérifier l'espace disque
DISK_USAGE=$(df -h / | awk 'NR==2 {print $5}' | sed 's/%//')
if [ "$DISK_USAGE" -gt 90 ]; then
echo "WARNING: Disk usage at ${DISK_USAGE}%"
exit 1
fi
# Vérifier la connexion à GitHub
if ! curl -s --connect-timeout 5 https://api.github.com > /dev/null; then
echo "CRITICAL: Cannot reach GitHub API"
exit 2
fi
echo "OK: Runner healthy"
exit 0

Exposez des métriques avec node_exporter :

docker-compose.yml
services:
node-exporter:
image: prom/node-exporter:latest
ports:
- "9100:9100"
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro

Métriques utiles à surveiller :

  • node_cpu_seconds_total : utilisation CPU
  • node_memory_MemAvailable_bytes : mémoire disponible
  • node_filesystem_avail_bytes : espace disque
  • Métriques custom sur les jobs exécutés
# alertmanager rules
groups:
- name: runner-alerts
rules:
- alert: RunnerDown
expr: up{job="runner"} == 0
for: 5m
labels:
severity: critical
annotations:
summary: "Runner {{ $labels.instance }} is down"
- alert: RunnerDiskFull
expr: node_filesystem_avail_bytes{mountpoint="/"} < 5e9
for: 10m
labels:
severity: warning
annotations:
summary: "Runner disk space low"

Sur un runner persistant, builds et images s'accumulent jusqu'à saturer le disque. Un nettoyage planifié maintient la machine saine.

docker-cleanup.sh
#!/bin/bash
# Images non utilisées
docker image prune -af --filter "until=24h"
# Containers arrêtés
docker container prune -f
# Volumes orphelins
docker volume prune -f
# Networks inutilisés
docker network prune -f
# Tout d'un coup (agressif)
# docker system prune -af --volumes

Planifier avec cron :

/etc/cron.d/docker-cleanup
0 3 * * * root /opt/scripts/docker-cleanup.sh >> /var/log/docker-cleanup.log 2>&1
cleanup-workdir.sh
#!/bin/bash
RUNNER_DIR="/opt/actions-runner"
WORK_DIR="$RUNNER_DIR/_work"
# Supprimer les répertoires de travail vieux de plus de 7 jours
find "$WORK_DIR" -type d -mtime +7 -exec rm -rf {} \;
# Supprimer les logs anciens
find "$RUNNER_DIR/_diag" -name "*.log" -mtime +30 -delete
Fenêtre de terminal
# Le cache actions/cache est géré par GitHub
# Mais le cache local (npm, pip, etc.) peut s'accumuler
rm -rf ~/.npm/_cacache
rm -rf ~/.cache/pip
rm -rf ~/.m2/repository

Quand la file d'attente s'allonge, il faut ajouter des runners, à la main pour un petit parc, automatiquement dès que le volume grandit.

Ajouter un runner :

Fenêtre de terminal
# Sur une nouvelle machine
./config.sh --url https://github.com/ORG/REPO \
--token TOKEN \
--labels linux,x64,docker \
--name runner-$(hostname)
./run.sh
horizontal-runner-autoscaler.yaml
apiVersion: actions.summerwind.dev/v1alpha1
kind: HorizontalRunnerAutoscaler
metadata:
name: runner-autoscaler
spec:
scaleTargetRef:
name: runner-deployment
minReplicas: 2
maxReplicas: 20
scaleUpTriggers:
- githubEvent:
workflowJob: {}
duration: "30m"
scaleDownDelaySecondsAfterScaleOut: 300
terraform/autoscaling.tf
resource "aws_autoscaling_group" "runners" {
name = "github-runners"
desired_capacity = 2
min_size = 1
max_size = 10
vpc_zone_identifier = var.subnet_ids
launch_template {
id = aws_launch_template.runner.id
version = "$Latest"
}
tag {
key = "Name"
value = "github-runner"
propagate_at_launch = true
}
}
resource "aws_autoscaling_policy" "scale_up" {
name = "scale-up"
scaling_adjustment = 2
adjustment_type = "ChangeInCapacity"
cooldown = 300
autoscaling_group_name = aws_autoscaling_group.runners.name
}

Un runner unique est un point de défaillance : s'il tombe, la CI s'arrête. Plusieurs réflexes éliminent ce risque.

Toujours avoir plusieurs runners pour éviter les SPOF :

jobs:
build:
runs-on: [self-hosted, linux]
# GitHub choisira un runner disponible parmi ceux qui matchent
jobs:
build-eu:
runs-on: [self-hosted, linux, eu-west-1]
build-us:
runs-on: [self-hosted, linux, us-east-1]
jobs:
build:
# Essayer self-hosted d'abord, fallback sur GitHub-hosted
runs-on: ${{ vars.USE_SELF_HOSTED == 'true' && 'self-hosted' || 'ubuntu-24.04' }}

Pour ne rien oublier, voici les opérations à mener, réparties selon leur fréquence, du quotidien automatisé à la revue trimestrielle.

  • Health checks toutes les 5 minutes
  • Nettoyage Docker la nuit
  • Rotation des logs
  • Vérifier les mises à jour de l'OS
  • Vérifier l'espace disque
  • Revoir les alertes de la semaine
  • Mises à jour des outils (Node, Python, etc.)
  • Audit des accès aux runners
  • Revue des performances (temps de build)
  • Vérifier la rotation des tokens
  • Mise à jour majeure de l'OS si nécessaire
  • Revue de l'architecture des runners
  • Test de disaster recovery
  • Optimisation des coûts
  • Le runner se met à jour seul ; à votre charge : l'OS et les outils utilisés par vos workflows.
  • Un health check régulier (processus, disque, connexion GitHub) détecte les pannes avant qu'elles ne bloquent la CI.
  • Le nettoyage Docker et du _work est indispensable : sans lui, le disque sature en quelques jours.
  • L'auto-scaling (ARC ou cloud) ajuste le parc à la charge, pas de runners inactifs facturés pour rien.
  • Plusieurs runners par label évitent le point de défaillance unique ; un fallback GitHub-hosted sécurise les pics.

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