Bare Metal Runners auto-hébergés


Un Runner auto-hébergé est un Bare Metal Runner que vous exécutez et gérez vous-même, au lieu d’utiliser un Runner On-Demand ou Dedicated géré par Bencher. Vous pointez le binaire runner vers un serveur API Bencher, et il réclame et exécute des Jobs sur votre propre matériel.

L’auto-hébergement d’un Runner est un bon choix lorsque vous souhaitez :

  • Exécuter des benchmarks sur du matériel spécifique que Bencher Cloud ne propose pas
  • Conserver les charges de travail de benchmark à l’intérieur de votre propre réseau ou d’un environnement isolé (air-gapped)
  • Exécuter Bencher Self-Hosted de bout en bout

Ce guide vous accompagne dans l’enregistrement d’un Runner, la description de son matériel avec un Spec, la liaison des deux ensemble, et le démarrage du binaire runner. Pour un aperçu conceptuel de la façon dont les Runners, Specs, Sandboxes et Jobs s’articulent, consultez l’Aperçu Bare Metal.

🐰 Le sandboxing Firecracker nécessite Linux 5.14 ou plus récent avec KVM activé et une hiérarchie cgroup v2 accessible en écriture sous /sys/fs/cgroup, et le Runner doit s’exécuter en tant que root. Chaque Job en bac à sable s’exécute dans son propre cgroup sous /sys/fs/cgroup/bencher, quel que soit le nombre de cœurs de CPU, avec les contrôleurs cpuset, memory et pids activés. Le Runner tue les processus d’un Job via le cgroup.kill de ce cgroup, que les noyaux plus anciens n’ont pas : un Runner s’y arrête au démarrage ou, démarré avec --danger-allow-no-sandbox, fait échouer chaque Job en bac à sable. Le processus Firecracker derrière chaque Job en bac à sable est confiné dans un chroot en tant qu’utilisateur non privilégié, et construire ce confinement demande des droits que l’activation de KVM seule n’accorde pas. Un Runner qui ne sert que des Specs sans sandbox peut s’exécuter sur n’importe quel hôte pris en charge.

Créer un Runner

Un Runner est la ressource qui authentifie le binaire runner auprès de votre serveur API Bencher. Créez-en un avec la CLI bencher, l’API REST Runners, ou la Bencher Console.

Terminal window
bencher runner create \
--host https://api.bencher.example.com \
--name "My Runner"

Bencher renvoie le nouveau Runner accompagné de sa clé :

{
"uuid": "...",
"key": "bencher_runner_..."
}

La clé n’est affichée qu’une seule fois, conservez-la donc en lieu sûr. Vous la transmettrez au binaire runner lorsque vous démarrerez le Runner. Si une clé est perdue ou divulguée, faites-la tourner avec l’API POST /v0/runners/{runner}/key, ce qui invalide immédiatement la clé précédente.

Créer un Spec

Un Spec décrit le matériel qu’un Runner fournit : système d’exploitation, architecture du CPU, type de Sandbox, nombre de CPU, mémoire, disque et accès réseau. Un Job déclare le Spec dont il a besoin, et un Runner ne réclame que les Jobs des Specs qu’il prend en charge.

Créez un Spec avec la CLI bencher, l’API REST Specs, ou la Bencher Console :

Terminal window
bencher spec create \
--host https://api.bencher.example.com \
--name "Intel v1" \
--os linux \
--architecture x86_64 \
--sandbox firecracker \
--cpu 4 \
--memory 51539607552 \
--disk 137438953472

Omettez --sandbox pour définir un Spec sans sandbox, et passez --network si les Jobs sur ce Spec sont autorisés à accéder au réseau. La mémoire et le disque sont spécifiés en octets. Pour en savoir plus sur les Specs et sur la façon dont un Testbed enregistre le Spec utilisé pour chaque Report, consultez Testbeds & Specs.

Assigner un Spec à un Runner

Un Runner Spec lie un Spec à un Runner. Un Runner peut prendre en charge plusieurs Specs, et un Spec peut être pris en charge par plusieurs Runners. Un Runner ne réclame que les Jobs dont le Spec lui a été assigné.

Ajoutez un Spec à un Runner avec la CLI bencher ou l’API REST Runner Specs :

Terminal window
bencher runner spec add my-runner \
--host https://api.bencher.example.com \
--spec intel-v1

Supprimez la liaison plus tard avec l’API DELETE /v0/runners/{runner}/specs/{spec}.

Démarrer le Runner

Avec un Runner créé et au moins un Spec assigné, démarrez le binaire runner sur votre matériel avec runner up. Fournissez l’hôte du serveur API, l’UUID ou le slug du Runner, et la clé du Runner, soit en tant que drapeaux, soit en tant que variables d’environnement :

Terminal window
runner up \
--host https://api.bencher.example.com \
--runner my-runner \
--key bencher_runner_...
Terminal window
export BENCHER_HOST=https://api.bencher.example.com
export BENCHER_RUNNER=my-runner
export BENCHER_RUNNER_KEY=bencher_runner_...
runner up

Le binaire runner ouvre une unique connexion WebSocket vers le serveur API, interroge les Jobs correspondant à ses Specs, exécute chacun d’eux, et renvoie les résultats. La connexion reste ouverte d’un Job à l’autre, et le binaire se met à jour lui-même entre les Jobs lorsqu’une nouvelle version est disponible. Par défaut, le Runner suit le canal de mise à jour stable et ne se met à jour que vers les versions publiées. Le canal canary suit quant à lui le build canary continu déployé sur Bencher Cloud.

Un Runner qui exécute des Jobs en bac à sable doit s’exécuter en tant que root, les commandes ci-dessus sont donc présentées telles que l’utilisateur root les lancerait, ou sous un gestionnaire de services qui démarre le Runner en tant que root.

Par défaut, un Runner rejette tout Job dont le Spec n’a pas de Sandbox. Pour permettre à un Runner d’exécuter directement des Jobs sans sandbox sur l’hôte, démarrez-le avec le drapeau --danger-allow-no-sandbox.

  • runner up

    runner up

    Démarre le runner, en interrogeant et en exécutant les Jobs de benchmark. C’est la commande de longue durée utilisée pour exploiter un Runner auto-hébergé. Le runner ouvre une unique connexion WebSocket vers le serveur API, réclame les Jobs correspondant à ses Specs, les exécute, et renvoie les résultats. Le runner écrit son journal sur stderr en JSON, un objet par ligne, et ne journalise la sortie d’un Job que sous forme de nombres d’octets.

    runner up [OPTIONS]

    Options

    --host <HOST>

    Le serveur API Bencher auquel se connecter. Par défaut, https://api.bencher.dev est utilisé. Peut aussi être défini avec la variable d’environnement BENCHER_HOST.

    --runner <RUNNER>

    L’UUID ou le slug du Runner sous lequel opérer. Peut aussi être défini avec la variable d’environnement BENCHER_RUNNER.

    --key <KEY>

    La clé d’authentification du Runner (bencher_runner_...) renvoyée lors de la création du Runner. Peut aussi être défini avec la variable d’environnement BENCHER_RUNNER_KEY.

    --poll-timeout <POLL_TIMEOUT>

    Le délai d’attente du long-poll en secondes pendant l’attente d’un Job, entre 1 et 900. Par défaut, 55 est utilisé.

    --state-dir <STATE_DIR>

    Le répertoire d’état persistant du Runner. Il doit s’agir d’un chemin absolu. La prison qui confine le bac à sable est créée dans ce répertoire, et les prisons laissées par un arrêt brutal en sont supprimées avant chaque Job avec bac à sable. Chaque prison contient un système de fichiers racine invité complet et une copie du binaire de la microVM, et un Runner exécute ses Jobs un par un, ce répertoire a donc besoin d’une capacité réelle pour une prison. Avec --image-cache-size, il contient aussi le cache d’Image, jusqu’à cette taille. Un hôte n’exécute qu’un Runner à la fois, quel que soit son répertoire d’état : un Runner démarré en tant que root détient le verrou /run/bencher/runner.lock tant qu’il s’exécute, et un second démarré en tant que root se termine immédiatement avec une erreur qui nomme ce verrou ; un Runner qui n’est pas root ne peut pas ouvrir le verrou et s’exécute sans lui. Au démarrage, puis avant chaque Job avec bac à sable, un Runner démarré en tant que root tue tout processus encore en cours d’exécution dans un cgroup sous /sys/fs/cgroup/bencher, comme le bac à sable d’un Runner tué en plein Job, quel que soit le répertoire d’état de ce Runner. Un système de fichiers dédié ou un montage tmpfs est pris en charge et recommandé : il maintient cette charge d’écriture hors du disque système, et un système de fichiers fraîchement créé est accepté même s’il arrive avec un répertoire lost+found. Le système de fichiers ne doit pas être monté avec nodev ou noexec, comme le sont souvent /tmp, /dev/shm et /run ; montez donc pour lui un tmpfs sans ces options. Le répertoire d’état lui-même peut être un lien symbolique créé par root vers un tel système de fichiers, comme /var/lib/bencher-runner pointant vers un disque dédié. Le lien n’est suivi que lorsque le répertoire qui le contient n’est inscriptible que par root, qu’il pointe directement vers sa cible finale, et que cette cible existe déjà sur un chemin inscriptible uniquement par root ; un lien dont la cible n’existe pas encore est refusé, créez donc d’abord la cible. Par défaut, /var/lib/bencher-runner est utilisé. Peut également être défini avec la variable d’environnement BENCHER_STATE_DIR.

    --jail-uid <JAIL_UID>

    L’identifiant d’utilisateur non privilégié que le processus du bac à sable adopte. Par défaut, 61016 est utilisé. Il ne peut pas valoir 0. Un processus de l’hôte possédant cet identifiant peut envoyer des signaux au bac à sable, choisissez donc un identifiant que l’hôte n’attribue à rien d’autre. Peut également être défini avec la variable d’environnement BENCHER_JAIL_UID.

    --jail-gid <JAIL_GID>

    L’identifiant de groupe non privilégié que le processus du bac à sable adopte. Par défaut, 61016 est utilisé. Il ne peut pas valoir 0. Peut également être défini avec la variable d’environnement BENCHER_JAIL_GID.

    --danger-allow-no-sandbox

    Autorise l’exécution de Jobs sans Sandbox. Sans ce drapeau, un Job dont le Spec n’a pas de Sandbox est rejeté à l’exécution. Les Jobs sans sandbox s’exécutent directement sur l’hôte, n’activez donc ceci que pour des charges de travail de confiance. Peut aussi être défini avec la variable d’environnement BENCHER_DANGER_ALLOW_NO_SANDBOX.

    --sandbox-log-level <SANDBOX_LOG_LEVEL>

    Le niveau de journalisation du processus de sandbox. Par défaut, warning est utilisé.

    --no-raid-pause

    Continuer à accepter des Jobs pendant qu’une grappe RAID md de l’hôte se resynchronise, se reconstruit, se vérifie, se répare ou se remodèle. Par défaut, le Runner vérifie ses grappes md avant de demander chaque Job, et tant que l’une d’elles effectue l’une de ces opérations, il se met en pause : il n’accepte aucun nouveau Job avant la fin de la synchronisation, et il n’arrête jamais un Job déjà en cours. Un Runner en pause reste connecté et continue de se mettre à jour.

    --no-auto-update

    Désactive les mises à jour automatiques depuis le serveur. Par défaut, le runner se met à jour lui-même entre les Jobs lorsque le serveur propose une nouvelle version. Peut aussi être défini avec la variable d’environnement BENCHER_NO_AUTO_UPDATE.

    --update-channel <UPDATE_CHANNEL>

    Le canal de mise à jour pour les mises à jour automatiques, soit stable, soit canary. Par défaut, stable est utilisé. Le canal stable met à jour vers les versions publiées. Le canal canary suit le build canary continu, qui est reconstruit chaque fois que le runner change lors d’un déploiement Bencher Cloud. Peut aussi être défini avec la variable d’environnement BENCHER_UPDATE_CHANNEL. En conflit avec --no-auto-update.

    --max-download-size <MAX_DOWNLOAD_SIZE>

    La taille de téléchargement maximale en octets pour les binaires de mise à jour automatique. Par défaut, 500 Mio est utilisé. En conflit avec --no-auto-update.

    --image-cache-size <IMAGE_CACHE_SIZE>

    La taille maximale en octets des blobs d’Image que le Runner conserve d’un Job à l’autre. Le Runner conserve les blobs de configuration et de couches de chaque Image qu’il récupère dans blobs, sous son --state-dir, de sorte qu’un Job ne télécharge que les blobs que le Runner ne détient pas déjà, mais récupère toujours le manifeste de son Image depuis le registre. Entre les Jobs, le Runner supprime les blobs les moins récemment utilisés jusqu’à ce que le reste tienne dans cette taille. Au démarrage, le Runner crée le stockage et le fait sien, et s’il n’y parvient pas, il s’arrête aussitôt avec une erreur qui nomme le répertoire. Par défaut, ou avec 0, le Runner ne conserve aucun blob d’un Job à l’autre et supprime ceux qu’il conservait auparavant. Peut également être défini avec la variable d’environnement BENCHER_IMAGE_CACHE_SIZE.

    --max-output-size <MAX_OUTPUT_SIZE>

    La taille maximale en octets pour les sorties stdout et stderr collectées. Par défaut, 25 Mio est utilisé.

    --max-file-count <MAX_FILE_COUNT>

    Le nombre maximal de fichiers de sortie à décoder. Par défaut, 255 est utilisé.

    Le nombre maximal de liens symboliques à suivre lors de la résolution des chemins, correspondant à la limite MAXSYMLINKS du noyau Linux. Par défaut, 40 est utilisé. Utilisé uniquement en mode sans sandbox.

    Réglage de l’hôte

    Avant chaque benchmark, le runner applique un réglage de l’hôte pour réduire le bruit de mesure. Par défaut, il désactive l’ASLR, le NMI watchdog, le SMT / hyper-threading et le turbo boost ; règle le gouverneur de mise à l’échelle du CPU sur performance, la swappiness sur 10, et perf_event_paranoid sur -1. Sur un hôte que le runner n’a pas réglé depuis son dernier redémarrage, les drapeaux ci-dessous conservent au contraire les optimisations individuelles à leurs valeurs par défaut de l’hôte.

    Le réglage de l’hôte persiste jusqu’au redémarrage de l’hôte : chaque paramètre est une écriture valable uniquement à l’exécution, que le runner n’annule pas lorsqu’il se termine, sauf le maintien des C-states, qui prend fin lorsque le runner se termine. Après une exécution avec réglage, un drapeau qui ignore tout autre paramètre prend effet à partir du prochain redémarrage : jusque-là, le paramètre conserve sa valeur réglée, et le runner journalise cette valeur au démarrage. Redémarrez un hôte partagé après avoir retiré le runner.

    --no-tuning

    Désactive toutes les optimisations de réglage de l’hôte.

    --aslr

    Ne désactive pas l’ASLR (par défaut : désactivé pour les benchmarks).

    --nmi-watchdog

    Ne désactive pas le NMI watchdog (par défaut : désactivé pour les benchmarks).

    --smt

    Ne désactive pas le SMT / hyper-threading (par défaut : désactivé pour les benchmarks).

    --turbo

    Ne désactive pas le turbo boost (par défaut : désactivé pour les benchmarks).

    --numa-balancing

    Ne désactive pas l’équilibrage NUMA automatique (par défaut : désactivé pour les benchmarks).

    --timer-migration

    Ne désactive pas la migration des timers (par défaut : désactivée pour les benchmarks).

    --soft-watchdog

    Ne désactive pas le soft lockup watchdog (par défaut : désactivé pour les benchmarks).

    --ksm

    Ne désactive pas le KSM, kernel samepage merging (par défaut : désactivé pour les benchmarks).

    --cstates

    Ne désactive pas les C-states profonds (par défaut : désactivés pour les benchmarks).

    --no-irq-steering

    Ne dirige pas les IRQ des périphériques ni les workqueues du noyau vers les cœurs de housekeeping.

    --no-cpuset-partition

    Ne détache pas les cœurs de benchmark de l’ordonnanceur via une partition cpuset isolée.

    --swappiness <SWAPPINESS>

    Définit la valeur de swappiness. Par défaut, 10 est utilisé.

    --governor <GOVERNOR>

    Définit le gouverneur de mise à l’échelle du CPU. Par défaut, performance est utilisé.

    --perf-event-paranoid <PERF_EVENT_PARANOID>

    Définit la valeur de perf_event_paranoid. Par défaut, -1 est utilisé.

    --thp <THP>

    Définit le mode transparent hugepage de l’hôte : never, madvise, always ou leave. Utilisez leave pour conserver le mode actuel de l’hôte. Par défaut, never est utilisé.

    --help

    Affiche l’aide.

Consultez la référence de la CLI runner pour chaque option, et la référence du Protocole Runner pour les messages échangés avec le serveur.

Maintenance de l’hôte

Un Runner et la maintenance de son hôte, comme les mises à niveau de paquets ou une vérification RAID, se relaient, de sorte que la maintenance ne s’exécute jamais pendant un Job. Ils se relaient au moyen de /run/bencher, un répertoire qu’un Runner démarré en tant que root crée pour root seul :

  • Le verrou de Job, /run/bencher/job.lock. Le Runner le prend avant de demander un Job et le garde jusqu’à ce que cette requête se termine sans Job ou que le Job qu’elle apporte se termine. La maintenance attend le verrou, elle ne commence donc qu’entre deux Jobs, et un Runner qui trouve le verrou pris n’accepte aucun Job tant qu’il n’est pas libre.
  • Les marqueurs de maintenance, /run/bencher/maintenance ou /run/bencher/maintenance.<name>. La maintenance crée son propre marqueur avant d’attendre le verrou et le supprime une fois terminée. Tant qu’un marqueur existe, le Runner ne demande aucun Job et laisse le verrou libre, de sorte que la maintenance en attente commence dès la fin de la requête en cours du Runner, ou du Job que cette requête apporte.

Pendant qu’il attend la maintenance, un Runner est en pause : il reste connecté et continue de se mettre à jour. runner run attend aussi le verrou de Job, et avertit mais s’exécute tant qu’un marqueur existe ou qu’une grappe md se synchronise. Un Runner qui n’est pas root ne peut ni ouvrir le verrou de Job ni voir les marqueurs, il accepte donc des Jobs sans eux.

Avec systemd, un drop-in fait attendre son tour à une unité de maintenance. Par exemple, /etc/systemd/system/fstrim.service.d/zz-bencher-quiet.conf enveloppe la commande propre de l’unité dans flock -F. Le ExecStart= vide efface cette commande, la ligne suivante doit donc la répéter exactement :

[Service]
ExecStartPre=+/usr/bin/install -d -m 0700 /run/bencher
ExecStartPre=+/usr/bin/touch /run/bencher/maintenance.%n
OpenFile=/dev/null
ExecStart=
ExecStart=/usr/bin/flock -F /run/bencher/job.lock /sbin/fstrim --listed-in /etc/fstab:/proc/self/mountinfo --verbose --quiet-unsupported
ExecStopPost=+/bin/sh -c '/usr/bin/busctl get-property org.freedesktop.login1 /org/freedesktop/login1 org.freedesktop.login1.Manager PreparingForShutdown | /usr/bin/grep -q true || /usr/bin/rm -f /run/bencher/maintenance.%n'
TimeoutStartSec=infinity

OpenFile=/dev/null occupe le descripteur de fichier 3, de sorte que le verrou reste pris même si la commande rouvre le descripteur 3, comme le fait apt.systemd.daily. Pendant que l’hôte se prépare à s’arrêter, ExecStopPost= laisse le marqueur, de sorte que le Runner n’accepte aucun Job avant que le redémarrage ne vide /run.

Vider un Runner

Pour vider un Runner, créez le marqueur simple en tant que root :

Terminal window
touch /run/bencher/maintenance

Le Runner termine sa requête en cours, au plus --poll-timeout secondes, et tout Job en cours ou apporté par cette requête, puis n’accepte aucun autre Job tant que le marqueur existe :

Terminal window
rm /run/bencher/maintenance

/run est vidé au démarrage, donc un redémarrage supprime aussi le marqueur.

Workflow d’un Runner auto-hébergé

Une fois le Runner connecté, vous soumettez les benchmarks de la même manière qu’avec n’importe quel Bare Metal Runner : construisez et poussez une Image, puis exécutez bencher run --image avec un Spec correspondant. Le diagramme ci-dessous montre le chemin complet, de l’enregistrement du Runner jusqu’à l’exécution d’un Job.

OCI Registryrunner binaryAPI ServerOCI Registryrunner binaryAPI ServerAdminbencher runner createRunner + keybencher spec createbencher runner spec addrunner up --host --runner --keyConnect (WebSocket) and poll for JobsAssign matching JobPull ImageImage layersRun benchmarkSubmit resultsAdmin

Continuez : Référence de la CLI runner ➡

🤖 Ce document a été automatiquement traduit par IA. Il peut ne pas être précis et peut contenir des erreurs. Si vous trouvez des erreurs, veuillez ouvrir une issue sur GitHub.


Published: Fri, June 19, 2026 at 8:00:00 AM UTC | Last Updated: Fri, October 9, 2026 at 3:39:11 PM UTC