
Optimism 单仓库 CI 自托管 Runner 深度解析机器执行器资源类型、Dockerfile 配置与 Fork 自托管方案【免费下载链接】optimismOptimism is Ethereum, scaled.项目地址: https://gitcode.com/GitHub_Trending/op/optimismOptimism 单仓库monorepo的 CI 中部分高负载任务运行在自托管self-hostedRunner 上而非 CircleCI 官方提供的 Docker 执行环境。本文基于仓库内 ops/book/src/ci/self-hosted-runners.md 的原始内容展开完整覆盖其定义的三类机器执行器资源类型、Runner 的 Dockerfile 与wrapper.sh脚本并结合当前仓库.circleci/下的真实流水线配置说明这些自托管节点如何嵌入 Optimism 的整体 CI 体系。读完本文你可以理解为什么 monorepo 需要自托管节点、如何在一台机器上区分不同并发度的资源池以及 Fork 该仓库后如何让自己的 CI 跑起来。为什么需要自托管 Runner自托管 Runner 的动机在原文档中开宗明义主要有两点更低的单任务成本在更大规格的机器上跑任务单位算力的花费更低构建缓存共享同一台机器上的多次任务运行可以共享构建缓存Go build cache、Go module 缓存、Rust sccache 等显著减少重复编译开销。对 Optimism 这类包含 Go 合约工具链、Solidity 合约、Rust 节点实现的超大 monorepo 来说这两点尤其关键。仓库中.circleci/continue/helpers.yml里的缓存命令正是“缓存共享”理念的具体落地restore-go-mod-cache/save-go-mod-cache以go.sum的 checksum 为 key 的共享 Go module 缓存全仓库只有一个 Go module因此每个go.sum版本只需下载一次模块其余任务全部只读恢复避免每个 job 各自触发一次proxy.golang.org下载每一次下载都是潜在的网络抖动点go-restore-cache/go-save-cache按namespace Branch Revision三级 key 回退恢复的 Go 编译缓存rust-prepare为 Rust 任务接入共享的 GCS sccache 编译缓存。这些缓存在自托管节点上驻留于同一台机器任务之间复用效率远高于按次销毁的 Docker 执行环境。机器执行器的三类资源类型使用自托管 Runner 的任务采用machine执行器并按资源类型resource type划分出三个并发池。原文档定义的三类如下资源类型定位并发上限设计理由latitude-1通用 Runner最多 16 路并发用于较小任务与 Go 构建通用负载latitude-1-go-e2eGo E2E 测试专用 Runner最多 2 路并发E2E 测试会在机器全部核心上并行展开限制并发以避免压垮单机latitdue-fps-1故障证明fault proofs构建专用同时只允许 1 个任务故障证明测试极其吃资源必须独占整台机器注latitdue-fps-1的拼写latitdue而非latitude是原文档中的原始记法引用时请保持一致以免与实际配置名不匹配。可以看出资源类型的设计本质上是在“机器总资源”与“任务资源画像”之间做匹配通用构建可以高密度并行E2E 测试需要预留每任务的核心配额而故障证明构建则是独占型负载。这种按资源池拆分并发上限的做法比把所有任务丢进一个池子里更能兼顾吞吐与稳定性。原文档同时给出了一条明确的选型建议新建任务应优先使用 CircleCI 官方 Docker Runner除非你大量使用 Go 工具链。这意味着自托管节点是“重负载特化”的定位而不是默认选项。Fork 单仓库后的 CI 自托管方案这是原文档的核心章节“Forking Monorepo CI”Fork 这个 monorepo 后在自己的 CircleCI 账户上让 CI 完整跑起来是可行的但需要额外步骤。原因在于OP Labs 使用的 Ansible playbook 负责自托管 Runner 的初始 provisioning但这些 playbook是闭源的因为包含密钥Fork 用户拿不到关键在于由于构建环境完全由 mise 管理自托管 Runner 上并没有安装任何“CircleCI 基础 Docker 镜像之外”的特殊软件。mise 会在每个任务开始时按mise.toml声明安装所有构建依赖所以自托管机器只需要提供 mise 本身能依赖的“裸”系统环境。原文档给出的是用于配置 Runner 的 Dockerfile基于 CircleCI 官方 machine 执行器镜像FROM circleci/runner-agent:machine-3.0.25-6554-32567e6 ARG GO_VERSION1.22.8 USER root RUN apt-get update \ apt-get install -y \ curl \ vim \ git \ build-essential \ clang \ jq \ lld \ binutils \ ca-certificates \ parallel COPY wrapper.sh /var/opt/circleci/wrapper.sh RUN chmod x /var/opt/circleci/wrapper.sh USER circleci:circleci # Everything below this line is installed locally as the CircleCI user. The CCI user does not have sudo for security # reasons, so dont put anything here that needs root. WORKDIR /home/circleci ENV PATH$PATH:/usr/local/go/bin:/home/circleci/.foundry/bin:/home/circleci/go/bin:/home/circleci/.cargo/bin:/home/circleci/.local/bin ENV CIRCLECI_RUNNER_COMMAND_PREFIX[/var/opt/circleci/wrapper.sh]对这份 Dockerfile 的逐段解读基础镜像circleci/runner-agent:machine-3.0.25-6554-32567e6即 CircleCI 官方 machine 执行器 agent 镜像自托管节点上直接运行这个镜像即可获得与官方 machine executor 一致的执行环境。root 层安装的最小系统依赖curl、git、build-essential、clang、lld、binutils、jq、parallel、ca-certificates、vim。这套包基本对应 Go 构建链clang/lld 作为链接器与 C 工具链、脚本工具jq/parallel/curl和安全证书没有任何项目特有的闭源组件——这正是“闭源 playbook 不构成 Fork 障碍”的底气所在。权限边界COPY wrapper.sh之后切回circleci:circleci用户注释明确说明 CircleCI 用户没有 sudo 权限安全考虑因此该行以下的内容都不能依赖 root。这条约定意味着自建 Runner 时所有工具链必须能以普通用户身份安装。PATH 预置把 Go/usr/local/go/bin、Foundry~/.foundry/bin、Go bin~/go/bin、Rust cargo~/.cargo/bin和~/.local/bin预置进 PATH。这些路径与 mise 安装各工具时的落盘位置一致。CIRCLECI_RUNNER_COMMAND_PREFIX这是整份 Dockerfile 最关键的一行。machine 执行器支持通过该环境变量指定一个命令前缀使任务 agent 执行的每条命令都经由wrapper.sh包装。这为自托管节点保留了统一的“任务启动钩子”。配套的wrapper.sh极其简单#!/bin/bash set -e task_agent_cmd${:1} echo Running CircleCI task agent with command: ${task_agent_cmd} # Set up PATH export PATH$PATH:/usr/local/go/bin:/home/circleci/.foundry/bin:/home/circleci/go/bin:/home/circleci/.cargo/bin # Run the command $task_agent_cmd # Collect exit code exit$? echo CircleCI task agent finished. exit $exit它做了三件事打印实际执行的 agent 命令便于调试自托管节点的命令注入问题、在运行前再次补齐 PATH与 Dockerfile 中的 ENV 双保险、执行命令并以原始退出码收尾。值得注意的是set -e与exit$?的组合如果前一条命令失败会立即退出因此这里的退出码收集逻辑在 wrapper 自身出错时会由set -e先行接管。Fork 用户的两条落地路径原文档给出的最终建议是拿到上述 Dockerfile 与wrapper.sh之后你可以选择其一自建自托管 Runner在自己的 CircleCI 账户中按这份 Dockerfile 镜像部署机器执行器使 Fork 的 CI 与上游尽量一致替换为 Docker Runner直接在 CI 配置中把所有使用自托管资源类型的任务替换为 CircleCI 官方 Docker Runner。由于 mise 保证了工具链可移植这条路径通常更省事。原文档表述为 “replace usages of the self-hosted runners in config.yaml”在 CircleCI 配置文件中替换自托管 Runner 的用法。对应到当前仓库CI 配置实际位于.circleci/config.ymlsetup 配置与.circleci/continue/目录下的各 continuation 配置文件中。结合当前仓库 CI 架构的理解补充从仓库当前的 CI 结构看自托管节点与动态流水线配置是分离的这对理解整套体系有帮助.circleci/config.yml是setup: true的动态配置入口。prepare-continuation-config任务在 Docker 执行器上完成七步准备工作安装 mise 工具集并预载共享 mise 缓存、收集字符串/布尔流水线参数、基于git diff做变更检测、执行路由策略计算c-run_*工作流开关、用 yq 合并 continuation 配置、最后调用 CircleCI continuation API 触发真正的流水线。也就是说工作流“何时”运行由 setup 阶段的路由决定声明式路由数据在.circleci/routing.yml中含 schedule、API dispatch 与变更模式而“如何”运行则定义在.circleci/continue/main.yml、rust-ci.yml、rust-e2e.yml等 continuation 配置里。自托管 machine 执行器的任务在 continuation 配置中执行。例如.circleci/continue/main.yml中对 machine-executor 任务有专门处理检测到 sccache 不存在时跳过 GCS 编译缓存步骤echo sccache not installed on this executor; skipping (no cache)以及按nproc计算并行度并留出一个空闲核心以避免打满执行器内存。这些细节印证了原文档中“按资源类型限制并发”的设计——不同执行器上的任务对缓存与核心数的处理是自适应的。缓存复用是贯穿 Docker 与 machine 两种执行器的统一机制。.circleci/continue/helpers.yml中共享 Go module 缓存的注释指出该缓存由prep-go-modules任务写入一次、其余任务只读恢复把“每个 job 各自下载模块”降为“每个 go.sum 版本下载一次”。自托管节点的价值正在于让这类缓存天然驻留在机器上。小结自托管 Runner 在 Optimism monorepo 中承担“重负载、可缓存复用”的角色通过machine执行器与latitude-1/latitude-1-go-e2e/latitdue-fps-1三类资源池实现差异化并发控制选型原则是保守的新任务默认走 CircleCI Docker Runner只有重度 Go 工具链负载才考虑自托管对 Fork 用户由于构建环境完全由 mise 声明见 ops/book/src/ci/mise.md自托管节点本身没有不可复制的特殊软件用官方 machine agent 镜像 上述 Dockerfile 与wrapper.sh即可复现或者干脆把自托管任务替换为 Docker Runner理解当前仓库的 setup 动态配置.circleci/config.yml、声明式路由.circleci/routing.yml与共享缓存命令.circleci/continue/helpers.yml能够把自托管节点放回它在整个 CI 体系中的正确位置。【免费下载链接】optimismOptimism is Ethereum, scaled.项目地址: https://gitcode.com/GitHub_Trending/op/optimism创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考