Self-Hosted Bare Metal Runners
Um Self-Hosted Runner é um Bare Metal Runner que você executa e gerencia por conta própria,
em vez de usar um Runner On-Demand ou Dedicated gerenciado pela Bencher.
Você aponta o binário runner para um servidor de API Bencher,
e ele reivindica e executa Jobs no seu próprio hardware.
Auto-hospedar um Runner é uma boa escolha quando você quer:
- Executar benchmarks em hardware específico que a Bencher Cloud não oferece
- Manter as cargas de trabalho de benchmark dentro da sua própria rede ou em um ambiente isolado (air-gapped)
- Executar o Bencher Self-Hosted de ponta a ponta
Este guia mostra como registrar um Runner, descrever seu hardware com uma Spec,
vincular os dois e iniciar o binário runner.
Para uma visão conceitual de como Runners, Specs, Sandboxes e Jobs se encaixam,
consulte a Visão Geral de Bare Metal.
🐰 O sandboxing com Firecracker requer Linux 5.14 ou posterior com KVM habilitado e uma hierarquia cgroup v2 gravável em
/sys/fs/cgroup, e o Runner precisa ser executado comoroot. Cada Job com sandbox roda no seu próprio cgroup sob/sys/fs/cgroup/bencher, seja qual for o número de núcleos de CPU, com os controladorescpuset,memoryepidshabilitados. O Runner encerra os processos de um Job pelocgroup.killdesse cgroup, que kernels mais antigos não têm: neles um Runner sai ao iniciar ou, iniciado com--danger-allow-no-sandbox, faz cada Job com sandbox falhar. O processo do Firecracker por trás de cada Job com sandbox fica confinado em um chroot como usuário sem privilégios, e construir esse confinamento exige permissões que habilitar o KVM sozinho não concede. Um Runner que atende apenas Specs sem sandbox pode rodar em qualquer host suportado.
Criar um Runner
Um Runner é o recurso que autentica o binário runner no seu servidor de API Bencher.
Crie um com a CLI bencher,
a Runners REST API,
ou o Bencher Console.
bencher runner create \ --host https://api.bencher.example.com \ --name "My Runner"A Bencher retorna o novo Runner junto com sua chave:
{ "uuid": "...", "key": "bencher_runner_..."}A chave é exibida apenas uma vez, então guarde-a com segurança.
Você a passará ao binário runner quando iniciar o Runner.
Se uma chave for perdida ou vazada, rotacione-a com a API POST /v0/runners/{runner}/key,
que invalida imediatamente a chave anterior.
Criar uma Spec
Uma Spec descreve o hardware que um Runner fornece: sistema operacional, arquitetura de CPU, tipo de Sandbox, número de CPUs, memória, disco e acesso à rede. Um Job declara a Spec de que precisa, e um Runner só reivindica Jobs para Specs que ele suporta.
Crie uma Spec com a CLI bencher,
a Specs REST API,
ou o 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 137438953472Omita --sandbox para definir uma Spec sem sandbox,
e passe --network se os Jobs nesta Spec tiverem permissão de acesso à rede.
Memória e disco são especificados em bytes.
Para mais sobre Specs e como um Testbed registra a Spec usada em cada Report,
consulte Testbeds & Specs.
Atribuir uma Spec a um Runner
Uma Runner Spec vincula uma Spec a um Runner. Um Runner pode suportar múltiplas Specs, e uma Spec pode ser suportada por múltiplos Runners. Um Runner só reivindica Jobs cuja Spec lhe foi atribuída.
Adicione uma Spec a um Runner com a CLI bencher
ou a Runner Specs REST API:
bencher runner spec add my-runner \ --host https://api.bencher.example.com \ --spec intel-v1Remova o vínculo mais tarde com a API DELETE /v0/runners/{runner}/specs/{spec}.
Iniciar o Runner
Com um Runner criado e ao menos uma Spec atribuída,
inicie o binário runner no seu hardware com runner up.
Forneça o host do servidor de API, o UUID ou slug do Runner e a chave do Runner,
seja como flags ou variáveis de ambiente:
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 upO binário runner abre uma única conexão WebSocket ao servidor de API,
faz polling por Jobs que correspondam às suas Specs, executa cada um e reporta os resultados de volta.
A conexão permanece aberta entre os Jobs,
e o binário se atualiza entre os Jobs quando uma nova versão está disponível.
Por padrão, o Runner segue o canal de atualização stable
e só atualiza para releases versionadas.
O canal canary, por sua vez, acompanha o build canary contínuo implantado no Bencher Cloud.
Um Runner que executa Jobs com sandbox precisa rodar como root,
então os comandos acima são mostrados como o usuário root os executaria,
ou sob um gerenciador de serviços que inicie o Runner como root.
Por padrão, um Runner rejeita qualquer Job cuja Spec não tenha Sandbox.
Para permitir que um Runner execute Jobs sem sandbox diretamente no host,
inicie-o com a flag --danger-allow-no-sandbox.
runner uprunner upInicia o runner, fazendo polling e executando Jobs de benchmark. Este é o comando de longa duração usado para operar um Self-Hosted Runner. O runner abre uma única conexão WebSocket ao servidor de API, reivindica Jobs que correspondam às suas Specs, os executa e reporta os resultados de volta. O runner escreve seu log no stderr como JSON, um objeto por linha, e registra a saída de um Job apenas como contagens de bytes.
runner up [OPTIONS]Opções
--host <HOST>O servidor de API Bencher ao qual se conectar. Por padrão,
https://api.bencher.devé usado. Também pode ser definido com a variável de ambienteBENCHER_HOST.--runner <RUNNER>O UUID ou slug do Runner sob o qual operar. Também pode ser definido com a variável de ambiente
BENCHER_RUNNER.--key <KEY>A chave de autenticação do Runner (
bencher_runner_...) retornada quando o Runner foi criado. Também pode ser definida com a variável de ambienteBENCHER_RUNNER_KEY.--poll-timeout <POLL_TIMEOUT>O timeout de long-poll em segundos enquanto aguarda um Job, entre
1e900. Por padrão,55é usado.--state-dir <STATE_DIR>O diretório de estado persistente do Runner. Deve ser um caminho absoluto. A jail que confina o sandbox é criada sob este diretório, e as jails deixadas por um encerramento não limpo são removidas dele antes de cada Job com sandbox. Cada jail contém um sistema de arquivos raiz completo do convidado e uma cópia do binário da microVM, e um Runner executa os seus Jobs um de cada vez, portanto este diretório precisa de capacidade real para uma jail. Com
--image-cache-size, ele também guarda o cache de Image, até esse tamanho. Um host executa um único Runner por vez, seja qual for o seu diretório de estado: um Runner iniciado comorootmantém o bloqueio/run/bencher/runner.lockenquanto está em execução, e um segundo iniciado comorootencerra imediatamente com um erro que nomeia esse bloqueio; um Runner que não érootnão consegue abrir o bloqueio e é executado sem ele. Ao iniciar, e de novo antes de cada Job com sandbox, um Runner iniciado comorootencerra qualquer processo ainda em execução em um cgroup sob/sys/fs/cgroup/bencher, como a sandbox de um Runner encerrado no meio de um Job, seja qual for o diretório de estado desse Runner. Um sistema de arquivos dedicado ou uma montagemtmpfsé suportado e recomendado: mantém essa carga de escrita fora do disco do sistema, e um sistema de arquivos recém-criado é aceito mesmo que venha com um diretóriolost+found. O sistema de arquivos não pode estar montado comnodevounoexec, como/tmp,/dev/shme/runcostumam estar, então monte para ele umtmpfssem essas opções. O próprio diretório de estado pode ser um link simbólico criado pelo root para um sistema de arquivos assim, como/var/lib/bencher-runnerapontando para um disco dedicado. O link só é seguido quando o diretório que o contém só pode ser escrito pelo root, ele aponta diretamente para o seu destino final, e esse destino já existe em um caminho gravável apenas pelo root; um link cujo destino ainda não existe é recusado, então crie o destino primeiro. Por padrão,/var/lib/bencher-runneré usado. Também pode ser definido com a variável de ambienteBENCHER_STATE_DIR.--jail-uid <JAIL_UID>O ID de usuário sem privilégios para o qual o processo do sandbox muda. Por padrão,
61016é usado. Não pode ser0. Um processo no host que seja dono desse ID de usuário pode enviar sinais ao sandbox, portanto defina um ID que o host não destine a mais nada. Também pode ser definido com a variável de ambienteBENCHER_JAIL_UID.--jail-gid <JAIL_GID>O ID de grupo sem privilégios para o qual o processo do sandbox muda. Por padrão,
61016é usado. Não pode ser0. Também pode ser definido com a variável de ambienteBENCHER_JAIL_GID.--danger-allow-no-sandboxPermite executar Jobs sem um Sandbox. Sem essa flag, um Job cuja Spec não tem Sandbox é rejeitado em tempo de execução. Jobs sem sandbox rodam diretamente no host, então habilite isso apenas para cargas de trabalho confiáveis. Também pode ser definida com a variável de ambiente
BENCHER_DANGER_ALLOW_NO_SANDBOX.--sandbox-log-level <SANDBOX_LOG_LEVEL>O nível de log para o processo do sandbox. Por padrão,
warningé usado.--no-raid-pauseContinuar aceitando Jobs enquanto um array RAID md do host faz ressincronização, recuperação, verificação, reparo ou remodelação. Por padrão, o Runner verifica seus arrays md antes de pedir cada Job, e enquanto algum deles executa uma dessas operações ele pausa: não aceita nenhum Job novo até a sincronização terminar, e nunca interrompe um Job que já está em execução. Um Runner pausado continua conectado e continua se atualizando.
--no-auto-updateDesabilita as atualizações automáticas do servidor. Por padrão, o runner se atualiza entre os Jobs quando o servidor oferece uma nova versão. Também pode ser definida com a variável de ambiente
BENCHER_NO_AUTO_UPDATE.--update-channel <UPDATE_CHANNEL>O canal de atualização para as atualizações automáticas,
stableoucanary. Por padrão,stableé usado. O canalstableatualiza para releases versionadas. O canalcanaryacompanha o build canary contínuo, que é reconstruído sempre que o runner muda em um deploy do Bencher Cloud. Também pode ser definido com a variável de ambienteBENCHER_UPDATE_CHANNEL. Conflita com--no-auto-update.--max-download-size <MAX_DOWNLOAD_SIZE>O tamanho máximo de download em bytes para binários de auto-atualização. Por padrão,
500MiB é usado. Conflita com--no-auto-update.--image-cache-size <IMAGE_CACHE_SIZE>O tamanho máximo em bytes dos blobs de Image que o Runner mantém entre Jobs. O Runner mantém os blobs de configuração e de camadas de cada Image que baixa em
blobs, sob o seu--state-dir, de modo que um Job baixa apenas os blobs que o Runner ainda não tem, embora ainda busque o manifesto da sua Image no registro todas as vezes. Entre Jobs, o Runner remove os blobs usados há mais tempo até que o restante caiba neste tamanho. Ao iniciar, o Runner cria o armazenamento e o torna seu, e se não conseguir, encerra imediatamente com um erro que nomeia o diretório. Por padrão, ou com0, o Runner não mantém blobs entre Jobs e remove os que tiver mantido antes. Também pode ser definido com a variável de ambienteBENCHER_IMAGE_CACHE_SIZE.--max-output-size <MAX_OUTPUT_SIZE>O tamanho máximo em bytes para
stdoutestderrcoletados. Por padrão,25MiB é usado.--max-file-count <MAX_FILE_COUNT>O número máximo de arquivos de saída a decodificar. Por padrão,
255é usado.--max-symlinks <MAX_SYMLINKS>O número máximo de symlinks a seguir durante a resolução de caminho, correspondendo ao limite
MAXSYMLINKSdo kernel Linux. Por padrão,40é usado. Usado apenas no modo sem sandbox.Ajuste do Host
Antes de cada benchmark, o
runneraplica ajustes ao host para reduzir o ruído de medição. Por padrão, ele desabilita ASLR, o NMI watchdog, SMT / hyper-threading e o turbo boost; define o governor de escalonamento de CPU comoperformance, a swappiness como10eperf_event_paranoidcomo-1. Em um host que orunnernão ajustou desde a última reinicialização, as flags abaixo mantêm otimizações individuais nos padrões do host.O ajuste do host persiste até que o host seja reiniciado: cada configuração é uma escrita apenas em tempo de execução que o
runnernão reverte ao sair, exceto a retenção dos C-states, que termina quando orunnersai. Após uma execução com ajuste, uma flag que pula qualquer outra configuração passa a valer a partir da próxima reinicialização: até lá, a configuração mantém o valor ajustado, e orunnerregistra esse valor ao iniciar. Reinicie um host compartilhado depois de remover orunner.--no-tuningDesabilita todas as otimizações de ajuste do host.
--aslrNão desabilita o ASLR (padrão: desabilitado para benchmarks).
--nmi-watchdogNão desabilita o NMI watchdog (padrão: desabilitado para benchmarks).
--smtNão desabilita o SMT / hyper-threading (padrão: desabilitado para benchmarks).
--turboNão desabilita o turbo boost (padrão: desabilitado para benchmarks).
--numa-balancingNão desabilita o balanceamento NUMA automático (padrão: desabilitado para benchmarks).
--timer-migrationNão desabilita a migração de timers (padrão: desabilitada para benchmarks).
--soft-watchdogNão desabilita o soft lockup watchdog (padrão: desabilitado para benchmarks).
--ksmNão desabilita o KSM, kernel samepage merging (padrão: desabilitado para benchmarks).
--cstatesNão desabilita os C-states profundos (padrão: desabilitados para benchmarks).
--no-irq-steeringNão direciona as IRQs de dispositivos e as workqueues do kernel para os núcleos de housekeeping.
--no-cpuset-partitionNão desacopla os núcleos de benchmark do escalonador por meio de uma partição cpuset isolada.
--swappiness <SWAPPINESS>Define o valor de swappiness. Por padrão,
10é usado.--governor <GOVERNOR>Define o governor de escalonamento de CPU. Por padrão,
performanceé usado.--perf-event-paranoid <PERF_EVENT_PARANOID>Define o valor de
perf_event_paranoid. Por padrão,-1é usado.--thp <THP>Define o modo de transparent hugepage do host:
never,madvise,alwaysouleave. Useleavepara manter o modo atual do host. Por padrão,neveré usado.--helpImprime a ajuda.
Consulte a referência da CLI runner para todas as opções,
e a referência do Runner Protocol para as mensagens trocadas com o servidor.
Manutenção do host
Um Runner e a manutenção do seu host, como atualizações de pacotes ou uma verificação de RAID, se revezam, de modo que a manutenção nunca roda durante um Job.
Eles se revezam por meio de /run/bencher, um diretório que um Runner iniciado como root cria apenas para root:
- O bloqueio de Job,
/run/bencher/job.lock. O Runner o obtém antes de pedir um Job e o mantém até essa consulta terminar sem Job ou terminar o Job que ela trouxer. A manutenção espera pelo bloqueio, então só começa entre Jobs, e um Runner que encontra o bloqueio em uso não aceita nenhum Job até que ele fique livre. - Marcadores de manutenção,
/run/bencher/maintenanceou/run/bencher/maintenance.<name>. A manutenção cria seu próprio marcador antes de esperar pelo bloqueio e o remove quando termina. Enquanto existir algum marcador, o Runner não pede nenhum Job e deixa o bloqueio livre, então a manutenção em espera começa assim que terminar a consulta atual do Runner, ou o Job que essa consulta trouxer.
Enquanto espera pela manutenção, um Runner fica pausado: continua conectado e continua se atualizando.
runner run também espera pelo bloqueio de Job, e avisa, mas roda, enquanto existir um marcador ou um array md estiver sincronizando.
Um Runner que não é root não consegue abrir o bloqueio de Job nem ver os marcadores, então aceita Jobs sem eles.
Com o systemd, um drop-in faz uma unidade de manutenção esperar sua vez.
Por exemplo, /etc/systemd/system/fstrim.service.d/zz-bencher-quiet.conf envolve o próprio comando da unidade em flock -F.
O ExecStart= vazio apaga esse comando, então a linha seguinte deve repeti-lo exatamente:
[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 ocupa o descritor de arquivo 3, então o bloqueio continua em uso mesmo que o comando reabra o descritor 3, como faz o apt.systemd.daily.
Enquanto o host se prepara para desligar, ExecStopPost= mantém o marcador, então o Runner não aceita nenhum Job antes que a reinicialização esvazie /run.
Esvaziar um Runner
Para esvaziar um Runner, crie o marcador simples como root:
touch /run/bencher/maintenanceO Runner termina sua consulta atual, no máximo --poll-timeout segundos, e qualquer Job em execução ou trazido por essa consulta, e depois não aceita nenhum outro Job até o marcador sumir:
rm /run/bencher/maintenance/run é esvaziado na inicialização, então uma reinicialização também remove o marcador.
Fluxo de Trabalho do Self-Hosted Runner
Uma vez que o Runner está conectado, você envia benchmarks da mesma forma que com qualquer Bare Metal Runner:
construa e publique uma Image, depois execute bencher run --image com uma Spec correspondente.
O diagrama abaixo mostra o caminho completo, desde o registro do Runner até a execução de um Job.