当前位置: 首页 > news >正文

GitHub Actions 自定义 Runner 镜像实战:把初始化环境提前做好

前言我以前优化 GitHub Actions 时最先看的通常是缓存。Node 项目就看setup-node的缓存Java 项目就看 Maven 或 Gradle 缓存Docker 项目就看 layer cache。大部分项目做到这一步CI 时间已经能降下来不少。但有些项目不只是安装依赖慢。它们每次运行都要安装一堆系统工具、配置内部证书、下载私有二进制文件、准备 SDK、安装浏览器依赖甚至还要拉内部网络里的基础包。这个时候继续堆actions/cache会有点别扭因为你想缓存的已经不只是包管理器目录而是整个 Runner 环境。自定义 Runner 镜像解决的是这个层面的问题。它允许组织或企业为 GitHub-hosted larger runners 准备一套预热环境把工具、依赖和配置提前放进镜像里。后续 job 启动时Runner 直接从这个环境开始不再每次从干净系统里重新装一遍。这个能力目前面向使用 GitHub Team 或 GitHub Enterprise Cloud 的组织和企业依附于 GitHub-hosted larger runners。它不适合所有项目。普通 Web 项目如果只是npm ci慢先用依赖缓存就够了。自定义镜像更适合那些初始化步骤复杂、构建频率高、团队规模比较大而且已经在使用 larger runners 的项目。一、先判断是不是镜像问题我不会一看到 CI 慢就直接上自定义镜像。GitHub Actions 里有几类慢法处理方式不一样。依赖下载慢先用包管理器缓存。-uses:actions/setup-nodev6with:node-version:24cache:npmcache-dependency-path:package-lock.json构建产物要传给后续 job用 artifact。-uses:actions/upload-artifactv7with:name:web-distpath:dist/retention-days:7Docker 构建慢用 layer cache。-uses:docker/build-push-actionv6with:context:.push:falsecache-from:typeghacache-to:typegha,modemax自定义 Runner 镜像处理的是另一类问题每次 Runner 启动后都要重复准备系统环境。比如下面这些步骤如果每次 CI 都跑一遍时间会很快堆上去。sudoapt-getupdatesudoapt-getinstall-ylibgtk-3-0 libnss3 libxss1curl-Lhttps://internal.example.com/tools/buildkit.tar.gz|tar-xz-C/opt ./scripts/install-internal-certificates.sh ./scripts/install-mobile-sdk.sh这些内容和项目依赖不同。它们更像“构建机器应该自带的环境”。如果每天跑几十次、几百次工作流每次都从头装确实会浪费很多时间。我会先看 Actions 日志把耗时拆开Set up job 花了多久 安装系统依赖花了多久 语言依赖安装花了多久 构建和测试本身花了多久 镜像构建花了多久如果慢在系统工具和基础环境准备自定义镜像才值得考虑。GitHub-hosted larger runners 的自定义镜像可以预装工具、依赖和配置后续 job 会从预热环境启动减少重复下载和安装。二、snapshot 要写在 job 上自定义镜像的生成流程并不复杂但原稿里有一个容易误导的地方snapshot不是放在某个 step 后面而是放在 job 级别。一个最小示例大概这样name:Build custom runner imageon:workflow_dispatch:schedule:-cron:0 3 * * 1timezone:Asia/Shanghaijobs:build-node-image:runs-on:my-image-generation-runnersnapshot:node-web-cisteps:-uses:actions/checkoutv6-name:Install system packagesrun:|sudo apt-get update sudo apt-get install -y libgtk-3-0 libnss3 libxss1-name:Install Node.js toolingrun:|corepack enable npm install -g pnpmlatest-name:Install internal toolsrun:|sudo mkdir -p /opt/internal-tools curl -L https://example.com/tools/release-tool.tar.gz \ | sudo tar -xz -C /opt/internal-tools-name:Record image contentsrun:|node --version pnpm --version ls -la /opt/internal-tools这里的runs-on必须指向一个 image-generation runner。这个 Runner 要先在组织或企业里创建并且平台要和目标镜像一致。目前支持 Linux x64、Linux ARM64、Windows x64。创建镜像生成 Runner 时可以从 GitHub-owned image 开始也可以选择干净的系统镜像作为基础。snapshot: node-web-ci表示这个 job 成功完成后GitHub 会基于当前环境生成一个名为node-web-ci的自定义镜像。如果一个 workflow 里有多个 job 都写了snapshot每个 job 都会生成独立镜像。想只生成一个镜像就把镜像准备步骤放在同一个 job 里。也可以用 mapping 语法指定主版本jobs:build-node-image:runs-on:my-image-generation-runnersnapshot:image-name:node-web-civersion:2.*steps:-uses:actions/checkoutv6-run:./scripts/prepare-runner-image.sh这里可以指定 major versionminor 版本会自动递增。patch 版本不支持。比如version: 2.*下面后续可能出现2.0、2.1这类版本。我更倾向把镜像准备脚本放进仓库里比如.github/runner-images/node-web-ci/ prepare.sh README.md tools.lock这样镜像里装了什么不会只存在于某个 Actions 日志里。团队以后排查环境差异也能从仓库里找到来源。三、镜像要按版本用不要一直追最新镜像生成后需要创建或修改 GitHub-hosted larger runner让它使用这个自定义镜像。创建 Runner 时要选择同样的平台在 Custom tab 里选择镜像再选择Latest或某个具体版本。Runner 的存储大小也要能容纳镜像大小比如镜像是在 8-core Runner 上生成的运行时就要选择 8-core 或更大的 Runner。使用镜像的 workflow 反而很简单name:Web CIon:pull_request:jobs:test:runs-on:node-web-ci-runnersteps:-uses:actions/checkoutv6-run:pnpm install--frozen-lockfile-run:pnpm test这里的runs-on写的是安装了自定义镜像的 larger runner 名称不是镜像名称。版本策略要提前想好。如果 Runner 选择Latest新镜像生成后会自动跟上最新版本。这个方式适合开发环境或测试仓库。镜像每周更新一次Runner 自动拿到新版环境维护成本低一些。生产关键 workflow 我更愿意锁定具体版本。镜像更新后先让测试仓库跑一轮再手动把生产 Runner 切过去。这样慢一点但不会因为某次镜像更新把核心 CI 全部带坏。一个可操作的节奏是每周一凌晨生成新镜像 测试仓库先使用 Latest 跑完整 CI 确认没问题后把生产 Runner 从 1.4 切到 1.5 保留最近几个可回滚版本 清理长期不用的旧版本自定义镜像每次成功运行带snapshot的 job 都会生成新版本。如果镜像构建很频繁又保留很多历史版本Actions storage 会增长得很快。使用镜像的 job 按对应 larger runner 的每分钟费率计费镜像存储另外计费。四、镜像里别放太多东西自定义镜像最容易变成“万能环境”。一开始只是为了前端项目加 Node、pnpm、浏览器依赖。后来又塞 Python、Java、Android SDK、内部部署工具、数据库客户端。再往后所有项目都想用同一个镜像。镜像越来越大生成越来越慢安全面也越来越宽。我不太建议做这种通用大镜像。更稳的方式是按项目类型拆。node-web-ciNode.js、pnpm、浏览器测试依赖 python-api-ciPython、Poetry、内部证书、数据库客户端 java-service-ciJDK、Maven、Gradle、内部 Maven 配置 mobile-android-ciJDK、Android SDK、构建工具每个镜像只放这类项目真正需要的工具。这样镜像更小生成日志也更容易看。镜像内容最好写一份清单。比如在仓库里放README.md# node-web-ci ## Included - Ubuntu base image - Node.js 24 - pnpm latest through Corepack - Chromium runtime dependencies - Internal release tool under /opt/internal-tools - Company root CA certificates ## Not included - Project node_modules - Production secrets - Deployment credentials - Application build output尤其不要把生产密钥、token、云服务凭据写进镜像。镜像是环境基础不应该成为密钥仓库。需要凭据的地方继续用 GitHub Actions secrets、OIDC、environment secrets 等机制。这里还有一个安全边界生成生产镜像的 runner group 不要和开发、测试仓库共用。能够访问 image-generation runner 的仓库有机会影响后续镜像内容。生产镜像生成 Runner 应该放在专门的 runner group 里并且不要开放给 public repositories。五、它和 Docker 镜像不是一回事很多团队已经在 CI 里使用 Docker 镜像于是会问既然 Docker 能做统一构建环境还要自定义 Runner 镜像做什么这两个东西解决的问题不一样。Docker 镜像适合把构建步骤放进容器。你可以在 GitLab CI、CircleCI、本地 Docker、Kubernetes 里复用同一个镜像。它的可移植性强也更适合跨平台 CI 策略。GitHub Actions 自定义 Runner 镜像则是 GitHub-hosted larger runners 的预热环境。它让整个 Runner 以预配置状态启动包括系统层工具、证书、SDK、二进制文件和 Runner 运行环境。它更贴近 VM 级别的构建机快照。我会这样判断希望跨 CI 平台复用环境优先 Docker 镜像 只在 GitHub Actions larger runners 里使用且初始化系统环境很重可以考虑自定义 Runner 镜像 需要访问宿主机级别工具、系统包、内部证书自定义 Runner 镜像更合适 只是应用依赖下载慢先用 cache如果你已经有成熟的 Docker 构建镜像而且 CI 主要跑在容器里自定义 Runner 镜像不一定能带来太多收益。它适合那些容器之外还要准备大量 Runner 系统环境的工作流。六、迁移时先挑最慢的一条流水线我不建议一上来全组织推广。更稳的做法是先挑一条最慢、最稳定、收益最容易衡量的流水线。比如一个前端 E2E 项目每次都安装浏览器依赖和内部测试工具可以先做node-e2e-ci镜像。迁移前先记录三组数据Set up job 耗时 安装系统依赖耗时 整体 CI 耗时 失败率和失败原因迁移后再看同样的数据。不要只看一次运行要看一周。因为镜像生成、缓存命中、队列状态、Runner 负载都会影响单次结果。迁移流程可以这样走选一个耗时明显的项目 写镜像准备 workflow 创建 image-generation runner 生成第一版镜像 创建测试 runner 使用该镜像 在测试仓库跑完整 CI 让一个非关键 PR 使用新 runner 观察一周 再决定是否推广到核心 workflow同时保留回退路径。不要把旧 workflow 立刻删干净可以先保留一段时间jobs:test-standard:if:${{vars.USE_CUSTOM_IMAGE!true}}runs-on:ubuntu-lateststeps:-uses:actions/checkoutv6-uses:actions/setup-nodev6with:node-version:24cache:npm-run:pnpm install--frozen-lockfile-run:pnpm testtest-custom-image:if:${{vars.USE_CUSTOM_IMAGE true}}runs-on:node-web-ci-runnersteps:-uses:actions/checkoutv6-run:pnpm install--frozen-lockfile-run:pnpm test这段配置不是长期方案但迁移期有用。出问题时改一个变量就能退回标准 runner。总结GitHub Actions 自定义 Runner 镜像适合处理“每次都要重新准备 Runner 环境”的问题。它可以把系统工具、语言运行时、内部证书、自定义二进制文件和 SDK 提前放进 GitHub-hosted larger runner 的镜像里让后续工作流从预热环境启动。它不应该替代所有缓存策略。依赖下载慢先看setup-node、setup-python、setup-java和actions/cache。构建产物传递用 artifact。Docker 构建慢用 layer cache。只有系统环境准备本身占了很多时间而且项目已经在使用 larger runners自定义镜像才更值得投入。真正落地时要把镜像当作构建产物管理。镜像内容写进仓库定期生成测试后推广生产 Runner 尽量锁定版本旧版本保留一段时间生成 Runner 放在专门的 runner group 里。这样它才能帮 CI 少做重复初始化而不是变成另一套难维护的基础设施。
http://www.rkmt.cn/news/1387570.html

相关文章:

  • Qt5.12.9属性表控件实战:手把手教你定制一个仿Qt Designer的配置面板
  • 嵌入式实时紧急车辆警笛检测系统设计与优化
  • 别再只盯着频率了!手把手教你读懂DDR内存条标签上的‘2Rx8’、‘PC3-10600S’到底啥意思
  • Docker部署MySQL实战:配置、持久化与Compose编排
  • Unity Aseprite Importer:像素动画工作流的语义级导入方案
  • 2026年比较好的紫铜线/黄铜线/铜线/铍铜线可靠供应商推荐 - 行业平台推荐
  • 告别PSNR!用Python复现NIQE无参考图像质量评估算法(附完整代码与避坑指南)
  • Git merge 实战指南:从三路合并原理到企业级安全合并规范
  • Dubbo安全升级避坑指南:除了改版本号,XML配置和Curator依赖你动了吗?
  • Unity动画师和TA看过来:用Parent Constraint和代码实现高级角色装备绑定
  • Unity2D塔防游戏核心框架:状态管理与Buff系统实战
  • 机器人数据采集方案设计:从场景到落地的完整指南
  • Unity UGUI性能优化实战:用UIEffect替代传统粒子,实现轻量级屏幕过渡与高级模糊
  • Paillier同态加密算法原理与硬件加速优化
  • PaddleOCR训练前必看:你的合成数据集标签格式真的做对了吗?避坑labels.json与rec_gt.txt
  • Burp Suite与Xray联动配置实战:提升Web安全测试效率
  • 构建可解释AI智能体声誉系统:从密码学身份到EigenTrust算法
  • 2026年知名的有色金属工业硅酸钙板/硅酸钙板/昆山船舶专用硅酸钙板/设备隔热硅酸钙板推荐厂家精选 - 品牌宣传支持者
  • 基于Claude的SaaS代码生成插件:从AI对话到生产就绪项目的自动化实践
  • 拼多多商品数据采集实战:绕过反爬获取详情页价格与SKU
  • 避坑指南:QGC地面站二次开发中,让Vehicle参数实时显示不踩坑的3个关键点
  • 2026天然沥青直销厂家推荐:天然岩沥青生产厂家实力深度解析 - 栗子测评
  • Unity中使用SQLite4Unity3d实现跨平台本地数据库方案
  • 别再死磕硬件了!用NI-MAX虚拟板卡5分钟搞定LabVIEW数字IO调试(附PCI6224配置)
  • 智能辅助系统设计:情境感知与渐进式披露的工程实践
  • 2026年评价高的塑料模具/模具定制厂家精选合集 - 品牌宣传支持者
  • 量化投资决策支持系统:构建单一分数模型评估公司投资价值
  • CloudFox:云红队的权限路径建模与攻击面拓扑分析工具
  • 动态目标跨镜无缝接力追踪技术在移民局出入境人员轨迹溯源场景中的应用白皮书
  • 2026年热门的高温电气绝缘铝酸钙板/高介电强度铝酸钙板/铝酸钙板生产厂家推荐 - 行业平台推荐