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 tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn