Self-Hosted Bare Metal Runner
Self-Hosted Runner 是一个由你自己运行和管理的 Bare Metal Runner,
而不是使用 Bencher 托管的 On-Demand 或 Dedicated Runner。
你将 runner 二进制文件指向某个 Bencher API 服务器,
它便会在你自己的硬件上认领并执行 Job。
在以下场景中自托管 Runner 是一个不错的选择:
- 在 Bencher Cloud 未提供的特定硬件上运行基准测试
- 将基准测试工作负载保留在你自己的网络或气隙(air-gapped)环境内
- 端到端运行 Bencher Self-Hosted
本指南将逐步介绍如何注册 Runner、用 Spec 描述其硬件、
将两者关联起来,以及启动 runner 二进制文件。
关于 Runner、Spec、Sandbox 和 Job 如何协同工作的概念性概述,
请参阅 Bare Metal 概述。
🐰 Firecracker 沙箱需要 Linux 5.14 或更高版本,启用 KVM,并在
/sys/fs/cgroup提供可写的 cgroup v2 层级, 并且 Runner 必须以root身份运行。 无论 CPU 核心数量多少,每个沙箱 Job 都在/sys/fs/cgroup/bencher下自己的 cgroup 中运行,并为其启用cpuset、memory和pids控制器。 Runner 通过该 cgroup 的cgroup.kill终止 Job 的进程,而较旧的内核没有它:在这些内核上,Runner 会在启动时退出;若以--danger-allow-no-sandbox启动,则每个沙箱 Job 都会失败。 运行每个沙箱 Job 的 Firecracker 进程都会以非特权用户的身份被限制在 chroot 中, 而构建这种限制所需的权限,仅仅启用 KVM 是无法获得的。 仅服务于非沙箱化 Spec 的 Runner 可以在任何受支持的主机上运行。
创建 Runner
Runner 是用于向你的 Bencher API 服务器认证 runner 二进制文件的资源。
可以使用 bencher CLI、
Runners REST API
或 Bencher Console 创建一个。
bencher runner create \ --host https://api.bencher.example.com \ --name "My Runner"Bencher 会返回新的 Runner 及其密钥:
{ "uuid": "...", "key": "bencher_runner_..."}该密钥只显示一次,因此请妥善保管。
你将在启动 Runner 时把它传给 runner 二进制文件。
如果密钥丢失或泄露,请使用 POST /v0/runners/{runner}/key API 轮换它,
这会立即使之前的密钥失效。
创建 Spec
Spec 描述 Runner 提供的硬件: 操作系统、CPU 架构、Sandbox 类型、CPU 数量、内存、磁盘和网络访问。 Job 声明它所需的 Spec,而 Runner 只认领其所支持的 Spec 对应的 Job。
可以使用 bencher CLI、
Specs REST API
或 Bencher Console 创建 Spec:
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,
如果允许该 Spec 上的 Job 进行网络访问,则传入 --network。
内存和磁盘以字节为单位指定。
关于 Spec 以及 Testbed 如何记录每个 Report 所使用的 Spec 的更多信息,
请参阅 Testbed 与 Spec。
将 Spec 分配给 Runner
Runner Spec 将一个 Spec 关联到一个 Runner。 一个 Runner 可以支持多个 Spec,一个 Spec 也可以由多个 Runner 支持。 Runner 只认领其被分配的 Spec 对应的 Job。
可以使用 bencher CLI
或 Runner Specs REST API 将 Spec 添加到 Runner:
bencher runner spec add my-runner \ --host https://api.bencher.example.com \ --spec intel-v1之后可使用 DELETE /v0/runners/{runner}/specs/{spec} API 移除该关联。
启动 Runner
在创建好 Runner 并至少分配一个 Spec 后,
使用 runner up 在你的硬件上启动 runner 二进制文件。
提供 API 服务器主机、Runner 的 UUID 或 slug,以及 Runner 密钥,
既可以作为标志,也可以作为环境变量:
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 uprunner 二进制文件向 API 服务器打开单个 WebSocket 连接,
轮询与其 Spec 匹配的 Job,逐个执行,并将结果报告回去。
该连接在多个 Job 之间保持打开,
并且当有新版本可用时,二进制文件会在 Job 之间自我更新。
默认情况下,Runner 遵循 stable 更新通道,
仅更新到版本化的发布版本。
canary 通道则跟踪部署到 Bencher Cloud 的滚动 canary 构建。
执行沙箱 Job 的 Runner 必须以 root 身份运行,
因此上面的命令是按 root 用户执行的方式给出的,
或者在以 root 启动 Runner 的服务管理器下运行。
默认情况下,Runner 会拒绝任何其 Spec 没有 Sandbox 的 Job。
要让 Runner 直接在主机上执行非沙箱化的 Job,
请使用 --danger-allow-no-sandbox 标志 启动它。
runner uprunner up启动 runner,轮询并执行基准测试 Job。 这是用于操作 Self-Hosted Runner 的长时间运行命令。 runner 向 API 服务器打开单个 WebSocket 连接, 认领与其 Spec 匹配的 Job,执行它们,并将结果报告回去。 runner 以 JSON 格式将日志写入 stderr,每行一个对象,并且只以字节数记录 Job 的输出。
runner up [OPTIONS]选项
--host <HOST>要连接的 Bencher API 服务器。 默认使用
https://api.bencher.dev。 也可以通过BENCHER_HOST环境变量设置。--runner <RUNNER>要作为其运行的 Runner 的 UUID 或 slug。 也可以通过
BENCHER_RUNNER环境变量设置。--key <KEY>创建 Runner 时返回的 Runner 认证密钥(
bencher_runner_...)。 也可以通过BENCHER_RUNNER_KEY环境变量设置。--poll-timeout <POLL_TIMEOUT>等待 Job 时的长轮询超时(秒),介于
1和900之间。 默认使用55。--state-dir <STATE_DIR>Runner 的持久状态目录。 必须是绝对路径。 限制沙箱的 jail 在该目录下创建, 非正常退出遗留的 jail 会在每个沙箱 Job 运行前从中清除。 每个 jail 都保存着完整的客户机根文件系统和一份 microVM 二进制文件副本, 而 Runner 一次只运行一个 Job, 因此该目录只需为一个 jail 准备真实的容量。 设置
--image-cache-size后,它还会保存 Image 缓存,最多到该大小。 无论状态目录如何,一台主机同一时间只运行一个 Runner: 以root身份启动的 Runner 在运行期间一直持有锁/run/bencher/runner.lock, 以root身份启动的第二个 Runner 会立即退出,并报出指明该锁的错误;不以root身份运行的 Runner 无法打开该锁,会在没有锁的情况下运行。 以root身份启动的 Runner 在启动时以及每个沙箱 Job 之前,都会终止仍在/sys/fs/cgroup/bencher下的 cgroup 中运行的任何进程,例如在 Job 中途被终止的 Runner 留下的沙箱,无论该 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>沙箱进程切换到的非特权用户 ID。 默认使用
61016。 不能为0。 宿主机上拥有该用户 ID 的进程可以向沙箱发送信号, 因此请设置为宿主机未分配给其他用途的 ID。 也可以通过BENCHER_JAIL_UID环境变量设置。--jail-gid <JAIL_GID>沙箱进程切换到的非特权组 ID。 默认使用
61016。 不能为0。 也可以通过BENCHER_JAIL_GID环境变量设置。--danger-allow-no-sandbox允许在没有 Sandbox 的情况下执行 Job。 如果没有此标志,其 Spec 没有 Sandbox 的 Job 会在运行时被拒绝。 非沙箱化的 Job 直接在主机上运行,因此仅对受信任的工作负载启用此项。 也可以通过
BENCHER_DANGER_ALLOW_NO_SANDBOX环境变量设置。--sandbox-log-level <SANDBOX_LOG_LEVEL>沙箱进程的日志级别。 默认使用
warning。--no-raid-pause在主机上的 md RAID 阵列执行重新同步、恢复、检查、修复或重塑时,继续接受 Job。 默认情况下,Runner 在请求每个 Job 之前检查其 md 阵列, 只要其中任何一个正在执行这些操作,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>自我更新二进制文件的最大下载大小(字节)。 默认使用
500MiB。 与--no-auto-update冲突。--image-cache-size <IMAGE_CACHE_SIZE>Runner 在 Job 之间保留的 Image blob 的最大大小(字节)。 Runner 将其拉取的每个 Image 的 config 和 layer blob 保存在其
--state-dir下的blobs中, 因此 Job 只下载 Runner 尚未持有的 blob, 但每次仍从注册表获取 Image 清单。 在 Job 之间,Runner 会先移除最久未使用的 blob,直到其余部分不超过此大小。 Runner 在启动时创建该存储并使其仅归自己所有,如果无法做到,则立即退出,并报错指明该目录。 默认情况下,或设置为0时,Runner 不在 Job 之间保留 blob,并会移除之前保留的 blob。 也可以通过BENCHER_IMAGE_CACHE_SIZE环境变量设置。--max-output-size <MAX_OUTPUT_SIZE>收集的
stdout和stderr的最大大小(字节)。 默认使用25MiB。--max-file-count <MAX_FILE_COUNT>要解码的输出文件的最大数量。 默认使用
255。--max-symlinks <MAX_SYMLINKS>路径解析期间要跟随的符号链接的最大数量, 与 Linux 内核的
MAXSYMLINKS限制相匹配。 默认使用40。 仅在非沙箱化模式下使用。主机调优
在每次基准测试之前,
runner会应用主机调优以减少测量噪声。 默认情况下,它会禁用 ASLR、NMI 看门狗、SMT / 超线程和睿频加速(turbo boost); 将 CPU 调频调速器(governor)设为performance,swappiness 设为10,perf_event_paranoid设为-1。 在自上次启动以来runner尚未调优过的主机上,下面的标志会改为将各项优化保持在其主机默认值。主机调优会一直保留到主机重启:每项设置都是仅在运行时生效的写入,
runner退出时不会将其还原;C-state 保持除外,它会在runner退出时结束。 在一次调优运行之后,跳过其他任何设置的标志从下一次重启起生效:在此之前,该设置保持其调优值,runner会在启动时记录该值。 移除runner后,请重启共享主机。--no-tuning禁用所有主机调优优化。
--aslr不禁用 ASLR(默认:基准测试时禁用)。
--nmi-watchdog不禁用 NMI 看门狗(默认:基准测试时禁用)。
--smt不禁用 SMT / 超线程(默认:基准测试时禁用)。
--turbo不禁用睿频加速(默认:基准测试时禁用)。
--numa-balancing不禁用自动 NUMA 平衡(默认:基准测试时禁用)。
--timer-migration不禁用定时器迁移(默认:基准测试时禁用)。
--soft-watchdog不禁用软锁死(soft lockup)看门狗(默认:基准测试时禁用)。
--ksm不禁用内核同页合并 KSM(默认:基准测试时禁用)。
--cstates不禁用深度 C-state(默认:基准测试时禁用)。
--no-irq-steering不将设备 IRQ 和内核工作队列引导到内务(housekeeping)核心。
--no-cpuset-partition不通过隔离的 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打印帮助。
有关每个选项,请参阅 runner CLI 参考,
有关与服务器交换的消息,请参阅 Runner 协议 参考。
主机维护
Runner 与其主机的维护(例如软件包升级或 RAID 巡检)轮流进行,因此维护绝不会在 Job 运行期间进行。
轮流通过 /run/bencher 实现,这是一个以 root 启动的 Runner 只为 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 阵列正在同步时,它会发出警告但仍然运行。
不是 root 的 Runner 既无法打开 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/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 占用文件描述符 3,因此即使命令像 apt.systemd.daily 那样重新打开描述符 3,锁也仍然保持。
在主机准备关机期间,ExecStopPost= 会保留标记,因此在重启清空 /run 之前 Runner 不接受任何 Job。
排空 Runner
要排空一个 Runner,请以 root 身份创建普通标记:
touch /run/bencher/maintenanceRunner 会完成当前的轮询(最多 --poll-timeout 秒)以及正在运行或由该轮询带来的 Job,之后在标记消失之前不再接受其他 Job:
rm /run/bencher/maintenance/run 在启动时会被清空,因此重启也会删除该标记。
Self-Hosted Runner 工作流
一旦 Runner 连接好,你提交基准测试的方式与任何 Bare Metal Runner 相同:
构建并推送一个 Image,然后使用匹配的 Spec 运行 bencher run --image。
下图展示了从注册 Runner 到执行 Job 的完整路径。