Self-Hosted Bare Metal Runner


Self-Hosted Runner — это Bare Metal Runner, который вы запускаете и которым управляете сами, вместо использования управляемого Bencher On-Demand или Dedicated Runner. Вы указываете бинарному файлу runner API-сервер Bencher, и он берёт и выполняет Job на вашем собственном оборудовании.

Самостоятельное размещение Runner подходит, когда вы хотите:

  • Запускать бенчмарки на определённом оборудовании, которое не предлагает Bencher Cloud
  • Держать benchmark-нагрузки внутри собственной сети или в изолированной (air-gapped) среде
  • Запускать Bencher Self-Hosted от начала до конца

Это руководство проведёт вас через регистрацию Runner, описание его оборудования с помощью Spec, связывание двух сущностей вместе и запуск бинарного файла runner. Концептуальный обзор того, как Runner, Spec, Sandbox и Job сочетаются друг с другом, смотрите в обзоре Bare Metal.

🐰 Изоляция через Firecracker требует Linux 5.14 или новее с включённым KVM и доступной для записи иерархией cgroup v2 в /sys/fs/cgroup, а сам Runner должен работать от имени root. Каждый Job в песочнице работает в собственной cgroup в /sys/fs/cgroup/bencher, независимо от числа ядер CPU, с включёнными контроллерами cpuset, memory и pids. Runner завершает процессы Job через cgroup.kill этой cgroup, которого нет в более старых ядрах: на них Runner завершается при запуске или, если он запущен с --danger-allow-no-sandbox, завершает с ошибкой каждый Job в песочнице. Процесс Firecracker, обслуживающий каждый Job в песочнице, запирается в chroot от имени непривилегированного пользователя, и построение этой изоляции требует прав, которых одно только включение KVM не даёт. Runner, обслуживающий только не изолированные Spec, может работать на любом поддерживаемом хосте.

Создание Runner

Runner — это ресурс, который аутентифицирует бинарный файл runner на вашем API-сервере Bencher. Создайте его с помощью CLI bencher, REST API для Runners или Bencher Console.

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

Bencher возвращает новый Runner вместе с его ключом:

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

Ключ показывается только один раз, поэтому сохраните его в надёжном месте. Вы передадите его бинарному файлу runner при запуске Runner. Если ключ когда-либо потерян или скомпрометирован, замените его с помощью API POST /v0/runners/{runner}/key, что немедленно делает предыдущий ключ недействительным.

Создание Spec

Spec описывает оборудование, которое предоставляет Runner: операционную систему, архитектуру CPU, тип Sandbox, количество CPU, память, диск и сетевой доступ. Job объявляет нужный ему Spec, а Runner берёт только те Job, чьи Spec он поддерживает.

Создайте Spec с помощью CLI bencher, REST API для Specs или 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

Опустите --sandbox, чтобы определить не изолированный Spec, и передайте --network, если Job на этом Spec разрешён сетевой доступ. Память и диск указываются в байтах. Подробнее о Spec и о том, как Testbed записывает Spec, использованный для каждого Report, смотрите в Testbeds и Specs.

Назначение Spec для Runner

Runner Spec связывает Spec с Runner. Runner может поддерживать несколько Spec, а Spec может поддерживаться несколькими Runner. Runner берёт только те Job, чей Spec ему назначен.

Добавьте Spec к Runner с помощью CLI bencher или REST API для Runner Specs:

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

Удалить связь позже можно с помощью API DELETE /v0/runners/{runner}/specs/{spec}.

Запуск Runner

После того как Runner создан и назначен хотя бы один Spec, запустите бинарный файл runner на вашем оборудовании с помощью runner up. Укажите хост API-сервера, UUID или slug Runner и ключ Runner, либо в виде флагов, либо в виде переменных окружения:

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

Бинарный файл runner открывает одно WebSocket-соединение с API-сервером, опрашивает на наличие Job, соответствующих его Spec, выполняет каждую и сообщает результаты обратно. Соединение остаётся открытым между Job, и бинарный файл обновляет себя между Job, когда доступна новая версия. По умолчанию Runner следует каналу обновлений stable и обновляется только до версионированных релизов. Канал canary вместо этого отслеживает скользящую canary-сборку, развёрнутую в Bencher Cloud.

Runner, выполняющий Job в песочнице, должен работать от имени root, поэтому команды выше приведены так, как их запустил бы пользователь root, или под менеджером служб, который запускает Runner от имени root.

По умолчанию Runner отклоняет любую Job, чей Spec не имеет Sandbox. Чтобы разрешить Runner выполнять не изолированные Job напрямую на хосте, запустите его с флагом --danger-allow-no-sandbox.

  • runner up

    runner up

    Запускает runner, который опрашивает сервер и выполняет benchmark Job. Это долго работающая команда, используемая для работы с Self-Hosted Runner. Runner открывает одно WebSocket-соединение с API-сервером, берёт Job, соответствующие его Spec, выполняет их и сообщает результаты обратно. Runner пишет свой журнал в stderr в формате JSON, по одному объекту на строку, а вывод Job записывает только как количество байтов.

    runner up [OPTIONS]

    Опции

    --host <HOST>

    API-сервер Bencher, к которому подключаться. По умолчанию используется https://api.bencher.dev. Также может быть задано переменной окружения BENCHER_HOST.

    --runner <RUNNER>

    UUID или slug Runner, от имени которого работать. Также может быть задано переменной окружения BENCHER_RUNNER.

    --key <KEY>

    Ключ аутентификации Runner (bencher_runner_...), возвращённый при создании Runner. Также может быть задано переменной окружения BENCHER_RUNNER_KEY.

    --poll-timeout <POLL_TIMEOUT>

    Тайм-аут длинного опроса в секундах при ожидании Job, от 1 до 900. По умолчанию используется 55.

    --state-dir <STATE_DIR>

    Постоянный каталог состояния Runner. Путь должен быть абсолютным. Изолирующая среда (jail), ограничивающая песочницу, создаётся внутри этого каталога, а среды, оставшиеся после аварийного завершения, удаляются из него перед запуском каждого Job с песочницей. Каждая среда содержит полную корневую файловую систему гостя и копию двоичного файла microVM, а Runner выполняет свои Job по одному, поэтому этому каталогу нужен реальный объём для одной среды. С --image-cache-size в нём также хранится кеш Image, до этого размера. На хосте одновременно работает только один Runner, независимо от его каталога состояния: Runner, запущенный от имени root, удерживает блокировку /run/bencher/runner.lock, пока работает, а второй, запущенный от имени root, сразу завершается с ошибкой, в которой названа эта блокировка; Runner, запущенный не от имени root, не может открыть блокировку и работает без неё. При запуске и снова перед каждым Job с песочницей Runner, запущенный от имени root, завершает любой процесс, который ещё выполняется в cgroup внутри /sys/fs/cgroup/bencher, например песочницу Runner, убитого посреди Job, независимо от каталога состояния этого Runner. Выделенная файловая система или монтирование tmpfs поддерживается и рекомендуется: это уводит нагрузку на запись с системного диска, а только что созданная файловая система принимается, даже если в ней есть каталог lost+found. Файловая система не должна быть смонтирована с nodev или noexec, как часто бывает с /tmp, /dev/shm и /run, поэтому смонтируйте для неё отдельный tmpfs без этих опций. Сам каталог состояния может быть символической ссылкой, созданной root, на такую файловую систему, например /var/lib/bencher-runner, указывающей на выделенный диск. Ссылка используется только тогда, когда в каталог, содержащий её, может писать только root, она указывает прямо на свою конечную цель, и эта цель уже существует по пути, куда может писать только root; ссылка, цель которой ещё не существует, отклоняется, поэтому сначала создайте цель. По умолчанию используется /var/lib/bencher-runner. Также может быть задан переменной окружения BENCHER_STATE_DIR.

    --jail-uid <JAIL_UID>

    Непривилегированный идентификатор пользователя, на который переходит процесс песочницы. По умолчанию используется 61016. Он не может быть равен 0. Процесс на хосте, которому принадлежит этот идентификатор, может посылать сигналы песочнице, поэтому выберите идентификатор, который хост ни на что другое не выделяет. Также может быть задан переменной окружения BENCHER_JAIL_UID.

    --jail-gid <JAIL_GID>

    Непривилегированный идентификатор группы, на который переходит процесс песочницы. По умолчанию используется 61016. Он не может быть равен 0. Также может быть задан переменной окружения BENCHER_JAIL_GID.

    --danger-allow-no-sandbox

    Разрешить выполнение Job без Sandbox. Без этого флага Job, чей Spec не имеет Sandbox, отклоняется во время выполнения. Не изолированные Job выполняются напрямую на хосте, поэтому включайте это только для доверенных нагрузок. Также может быть задано переменной окружения BENCHER_DANGER_ALLOW_NO_SANDBOX.

    --sandbox-log-level <SANDBOX_LOG_LEVEL>

    Уровень логирования для процесса sandbox. По умолчанию используется warning.

    --no-raid-pause

    Продолжать принимать Job, пока md RAID массив на хосте выполняет ресинхронизацию, восстановление, проверку, исправление или перестроение. По умолчанию Runner проверяет свои md массивы перед запросом каждого Job, и пока любой из них выполняет одну из этих операций, Runner приостанавливается: он не принимает новый Job, пока синхронизация не завершится, и никогда не останавливает уже выполняющийся Job. Приостановленный Runner остается подключенным и продолжает обновляться.

    --no-auto-update

    Отключить автоматические обновления с сервера. По умолчанию runner обновляет себя между Job, когда сервер предлагает новую версию. Также может быть задано переменной окружения BENCHER_NO_AUTO_UPDATE.

    --update-channel <UPDATE_CHANNEL>

    Канал автоматических обновлений: либо stable, либо canary. По умолчанию используется stable. Канал stable обновляет до версионированных релизов. Канал canary отслеживает скользящую canary-сборку, которая пересобирается всякий раз, когда runner меняется при деплое Bencher Cloud. Также может быть задано переменной окружения BENCHER_UPDATE_CHANNEL. Конфликтует с --no-auto-update.

    --max-download-size <MAX_DOWNLOAD_SIZE>

    Максимальный размер загрузки в байтах для бинарных файлов самообновления. По умолчанию используется 500 MiB. Конфликтует с --no-auto-update.

    --image-cache-size <IMAGE_CACHE_SIZE>

    Максимальный размер в байтах блобов Image, которые Runner хранит между Job. Runner хранит блобы конфигурации и слоёв каждого Image, который он загружает, в blobs внутри своего --state-dir, поэтому Job загружает только те блобы, которых у Runner ещё нет, но манифест своего Image по-прежнему каждый раз получает из реестра. Между Job Runner удаляет блобы, которые дольше всего не использовались, пока остальные не уместятся в этот размер. При запуске Runner создаёт хранилище и делает его своим, а если не может, сразу завершается с ошибкой, называющей каталог. По умолчанию, или при значении 0, Runner не хранит блобы между Job и удаляет те, что хранил раньше. Также может быть задан переменной окружения BENCHER_IMAGE_CACHE_SIZE.

    --max-output-size <MAX_OUTPUT_SIZE>

    Максимальный размер в байтах для собираемых stdout и stderr. По умолчанию используется 25 MiB.

    --max-file-count <MAX_FILE_COUNT>

    Максимальное количество выходных файлов для декодирования. По умолчанию используется 255.

    Максимальное количество символических ссылок, по которым следует переходить при разрешении путей, соответствующее ограничению MAXSYMLINKS ядра Linux. По умолчанию используется 40. Используется только в не изолированном режиме.

    Тонкая настройка хоста

    Перед каждым бенчмарком runner применяет тонкую настройку хоста, чтобы снизить шум измерений. По умолчанию он отключает ASLR, NMI watchdog, SMT / hyper-threading и turbo boost; устанавливает регулятор масштабирования частоты CPU в performance, swappiness в 10, а perf_event_paranoid в -1. На хосте, который runner не настраивал с момента последней перезагрузки, флаги ниже вместо этого оставляют отдельные оптимизации на значениях хоста по умолчанию.

    Тонкая настройка хоста сохраняется до перезагрузки хоста: каждый параметр записывается только во время выполнения, и runner не откатывает его при завершении, кроме удержания C-состояний, которое прекращается при завершении runner. После запуска с тонкой настройкой флаг, который пропускает любой другой параметр, вступает в силу со следующей перезагрузки: до тех пор параметр сохраняет настроенное значение, а runner записывает это значение в журнал при запуске. Перезагрузите общий хост после удаления runner.

    --no-tuning

    Отключить все оптимизации тонкой настройки хоста.

    --aslr

    Не отключать ASLR (по умолчанию: отключён для бенчмарков).

    --nmi-watchdog

    Не отключать NMI watchdog (по умолчанию: отключён для бенчмарков).

    --smt

    Не отключать SMT / hyper-threading (по умолчанию: отключён для бенчмарков).

    --turbo

    Не отключать turbo boost (по умолчанию: отключён для бенчмарков).

    --numa-balancing

    Не отключать автоматическую балансировку NUMA (по умолчанию: отключена для бенчмарков).

    --timer-migration

    Не отключать миграцию таймеров (по умолчанию: отключена для бенчмарков).

    --soft-watchdog

    Не отключать soft lockup watchdog (по умолчанию: отключён для бенчмарков).

    --ksm

    Не отключать KSM, kernel samepage merging (по умолчанию: отключён для бенчмарков).

    --cstates

    Не отключать глубокие C-состояния (по умолчанию: отключены для бенчмарков).

    --no-irq-steering

    Не направлять IRQ устройств и рабочие очереди ядра на служебные ядра CPU.

    --no-cpuset-partition

    Не отделять ядра CPU для бенчмарков от планировщика с помощью изолированного раздела cpuset.

    --swappiness <SWAPPINESS>

    Установить значение swappiness. По умолчанию используется 10.

    --governor <GOVERNOR>

    Установить регулятор масштабирования частоты CPU. По умолчанию используется performance.

    --perf-event-paranoid <PERF_EVENT_PARANOID>

    Установить значение perf_event_paranoid. По умолчанию используется -1.

    --thp <THP>

    Установить режим transparent hugepage на хосте: never, madvise, always или leave. Со значением leave текущий режим хоста сохраняется. По умолчанию используется never.

    --help

    Вывести справку.

Все опции смотрите в справочнике по CLI runner, а сообщения, которыми обмениваются с сервером, — в справочнике по Runner Protocol.

Обслуживание хоста

Runner и обслуживание его хоста, например обновление пакетов или проверка RAID, выполняются по очереди, поэтому обслуживание никогда не идет во время Job. Очередность задается через /run/bencher, каталог, который Runner, запущенный от root, создает только для root:

  • Блокировка Job, /run/bencher/job.lock. Runner берет ее перед запросом Job и держит, пока этот опрос не закончится без Job или пока не закончится полученный Job. Обслуживание ждет эту блокировку, поэтому начинается только между Job, а Runner, обнаруживший занятую блокировку, не принимает Job, пока она не освободится.
  • Маркеры обслуживания, /run/bencher/maintenance или /run/bencher/maintenance.<name>. Обслуживание создает свой маркер перед тем, как ждать блокировку, и удаляет его по завершении. Пока существует хотя бы один маркер, Runner не запрашивает Job и оставляет блокировку свободной, поэтому ожидающее обслуживание начинается, как только закончится текущий опрос Runner или Job, полученный этим опросом.

Пока Runner ждет обслуживание, он приостановлен: он остается подключенным и продолжает обновляться. runner run тоже ждет блокировку Job, а пока существует маркер или md массив синхронизируется, выводит предупреждение, но выполняется. Runner, запущенный не от root, не может ни открыть блокировку Job, ни увидеть маркеры, поэтому принимает Job без них.

В systemd drop-in заставляет юнит обслуживания дожидаться своей очереди. Например, /etc/systemd/system/fstrim.service.d/zz-bencher-quiet.conf оборачивает собственную команду юнита в flock -F. Пустой ExecStart= сбрасывает эту команду, поэтому следующая строка должна точно ее повторить:

[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 занимает файловый дескриптор 3, поэтому блокировка остается занятой, даже если команда заново открывает дескриптор 3, как это делает apt.systemd.daily. Пока хост готовится к выключению, ExecStopPost= оставляет маркер, поэтому Runner не принимает Job, пока перезагрузка не очистит /run.

Освободить Runner

Чтобы освободить Runner от Job, создайте простой маркер от root:

Terminal window
touch /run/bencher/maintenance

Runner завершает текущий опрос, не дольше --poll-timeout секунд, и любой Job, который выполняется или получен этим опросом, после чего не принимает другой Job, пока маркер не исчезнет:

Terminal window
rm /run/bencher/maintenance

/run очищается при загрузке, поэтому перезагрузка тоже удаляет маркер.

Рабочий процесс Self-Hosted Runner

Как только Runner подключён, вы отправляете бенчмарки так же, как и с любым Bare Metal Runner: соберите и отправьте Image, затем запустите bencher run --image с соответствующим Spec. Диаграмма ниже показывает полный путь, от регистрации Runner до выполнения 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

Продолжайте: справочник по CLI runner ➡

🤖 Этот документ был автоматически переведён с помощью ИИ. Он может быть неточным и содержать ошибки. Если вы обнаружите какие-либо ошибки, откройте проблему на GitHub.


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