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 queroot. 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ôleurscpuset,memoryetpidsactivés. Le Runner tue les processus d’un Job via lecgroup.killde 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.
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 :
bencher spec create \ --host https://api.bencher.example.com \ --name "Intel v1" \ --os linux \ --architecture x86_64 \ --sandbox firecracker \ --cpu 4 \ --memory 51539607552 \ --disk 137438953472Omettez --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 :
bencher runner spec add my-runner \ --host https://api.bencher.example.com \ --spec intel-v1Supprimez 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 :
runner up \ --host https://api.bencher.example.com \ --runner my-runner \ --key bencher_runner_...export BENCHER_HOST=https://api.bencher.example.comexport BENCHER_RUNNER=my-runnerexport BENCHER_RUNNER_KEY=bencher_runner_...runner upLe 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 uprunner upDé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.devest utilisé. Peut aussi être défini avec la variable d’environnementBENCHER_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’environnementBENCHER_RUNNER_KEY.--poll-timeout <POLL_TIMEOUT>Le délai d’attente du long-poll en secondes pendant l’attente d’un Job, entre
1et900. Par défaut,55est 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 querootdétient le verrou/run/bencher/runner.locktant qu’il s’exécute, et un second démarré en tant querootse termine immédiatement avec une erreur qui nomme ce verrou ; un Runner qui n’est pasrootne 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 queroottue 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 montagetmpfsest 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épertoirelost+found. Le système de fichiers ne doit pas être monté avecnodevounoexec, comme le sont souvent/tmp,/dev/shmet/run; montez donc pour lui untmpfssans 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-runnerpointant 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-runnerest utilisé. Peut également être défini avec la variable d’environnementBENCHER_STATE_DIR.--jail-uid <JAIL_UID>L’identifiant d’utilisateur non privilégié que le processus du bac à sable adopte. Par défaut,
61016est utilisé. Il ne peut pas valoir0. 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’environnementBENCHER_JAIL_UID.--jail-gid <JAIL_GID>L’identifiant de groupe non privilégié que le processus du bac à sable adopte. Par défaut,
61016est utilisé. Il ne peut pas valoir0. Peut également être défini avec la variable d’environnementBENCHER_JAIL_GID.--danger-allow-no-sandboxAutorise 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,
warningest utilisé.--no-raid-pauseContinuer à 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-updateDé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, soitcanary. Par défaut,stableest utilisé. Le canalstablemet à jour vers les versions publiées. Le canalcanarysuit 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’environnementBENCHER_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,
500Mio 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 avec0, 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’environnementBENCHER_IMAGE_CACHE_SIZE.--max-output-size <MAX_OUTPUT_SIZE>La taille maximale en octets pour les sorties
stdoutetstderrcollectées. Par défaut,25Mio est utilisé.--max-file-count <MAX_FILE_COUNT>Le nombre maximal de fichiers de sortie à décoder. Par défaut,
255est utilisé.--max-symlinks <MAX_SYMLINKS>Le nombre maximal de liens symboliques à suivre lors de la résolution des chemins, correspondant à la limite
MAXSYMLINKSdu noyau Linux. Par défaut,40est utilisé. Utilisé uniquement en mode sans sandbox.Réglage de l’hôte
Avant chaque benchmark, le
runnerapplique 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 surperformance, la swappiness sur10, etperf_event_paranoidsur-1. Sur un hôte que lerunnern’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
runnern’annule pas lorsqu’il se termine, sauf le maintien des C-states, qui prend fin lorsque lerunnerse 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 lerunnerjournalise cette valeur au démarrage. Redémarrez un hôte partagé après avoir retiré lerunner.--no-tuningDésactive toutes les optimisations de réglage de l’hôte.
--aslrNe désactive pas l’ASLR (par défaut : désactivé pour les benchmarks).
--nmi-watchdogNe désactive pas le NMI watchdog (par défaut : désactivé pour les benchmarks).
--smtNe désactive pas le SMT / hyper-threading (par défaut : désactivé pour les benchmarks).
--turboNe désactive pas le turbo boost (par défaut : désactivé pour les benchmarks).
--numa-balancingNe désactive pas l’équilibrage NUMA automatique (par défaut : désactivé pour les benchmarks).
--timer-migrationNe désactive pas la migration des timers (par défaut : désactivée pour les benchmarks).
--soft-watchdogNe désactive pas le soft lockup watchdog (par défaut : désactivé pour les benchmarks).
--ksmNe désactive pas le KSM, kernel samepage merging (par défaut : désactivé pour les benchmarks).
--cstatesNe désactive pas les C-states profonds (par défaut : désactivés pour les benchmarks).
--no-irq-steeringNe dirige pas les IRQ des périphériques ni les workqueues du noyau vers les cœurs de housekeeping.
--no-cpuset-partitionNe 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,
10est utilisé.--governor <GOVERNOR>Définit le gouverneur de mise à l’échelle du CPU. Par défaut,
performanceest utilisé.--perf-event-paranoid <PERF_EVENT_PARANOID>Définit la valeur de
perf_event_paranoid. Par défaut,-1est utilisé.--thp <THP>Définit le mode transparent hugepage de l’hôte :
never,madvise,alwaysouleave. Utilisezleavepour conserver le mode actuel de l’hôte. Par défaut,neverest utilisé.--helpAffiche 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/maintenanceou/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/bencherExecStartPre=+/usr/bin/touch /run/bencher/maintenance.%nOpenFile=/dev/nullExecStart=ExecStart=/usr/bin/flock -F /run/bencher/job.lock /sbin/fstrim --listed-in /etc/fstab:/proc/self/mountinfo --verbose --quiet-unsupportedExecStopPost=+/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=infinityOpenFile=/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 :
touch /run/bencher/maintenanceLe 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 :
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.