Runner 与运行环境
上一章我们反复提到 runs-on,说它决定 job 跑在哪台机器上。这台"机器"在 GitHub Actions 里有个正式名字——Runner(运行器)。这一章我们就揭开它的面纱:runner 到底是什么、有哪些种类、又该怎么选。
一句话概括:runner 是真正动手执行 job 的那台计算机,而 runs-on 就是你在菜单里点选"我要哪一类机器"的字段。
1. 什么是 runner
当你触发一个 workflow,GitHub Actions 会为每一个 job 分配一台 runner。这台机器上装好了操作系统、常见的开发工具链(Git、Node.js、Python、Docker 等),然后按 steps 的顺序一步步执行你的命令。
这里有个很关键的认知:runner 是 job 级别的——一个 job 独占一台机器(一个 runner),job 里所有的 step 都跑在同一台机器上。step 本身没有"自己的机器",它只是这台机器上按顺序执行的一个动作。
jobs:
build:
runs-on: ubuntu-latest # build 这一整个 job 都在同一台 ubuntu 机器上跑
steps:
- run: pwd
- run: echo "这两个 step 在同一台机器上"正因为 runner 是 job 级而非 step 级,所以同一个 job 里前面 step 生成的文件、装好的依赖,后面的 step 都能直接用。但不同 job 之间默认不共享磁盘——想共享产物得用 actions/upload-artifact 和 download-artifact(后续章节会讲)。
2. GitHub 托管的 runner 与 OS 标签
GitHub 提供了一批"GitHub 托管 runner"——也就是 GitHub 替你维护的云上虚拟机,你不用装系统、不用打补丁,开箱即用。你只需要用 runs-on 指定一个 OS 标签即可。常见的标签有:
ubuntu-latest:最新的 Ubuntu(Linux)ubuntu-22.04/ubuntu-24.04:指定版本的 Ubuntuwindows-latest:最新的 Windows Servermacos-latest:最新的 macOSmacos-14:指定版本的 macOS(常用于 Apple Silicon 构建)
jobs:
linux-job:
runs-on: ubuntu-latest
steps:
- run: echo "我在 Linux 上"
windows-job:
runs-on: windows-latest
steps:
- run: echo "我在 Windows 上"
mac-job:
runs-on: macos-latest
steps:
- run: echo "我在 macOS 上"注意:runs-on 的值可以是单个标签字符串,也可以是一个标签数组(数组表示"满足其中任一标签即可",GitHub 会从匹配的池中挑一台)。对初学者来说,先用单个标签就够了。
3. latest 还是具体版本:可复现性之争
这里有个特别容易踩的坑,必须单独拿出来说。
ubuntu-latest 这种带 latest 的标签,会随着时间自动指向更新的系统版本。今天可能是 Ubuntu 22.04,某次 GitHub 升级后可能悄悄变成 24.04。这看起来很方便,但也埋下了隐患:
- 某天你没改任何代码,CI 却突然红了——因为底层系统被自动升级了。
- 别人 fork 你的项目,跑出来的结果和你当初不一样——因为"latest"对他来说已经是另一个版本。
相比之下,ubuntu-22.04 这种具体版本标签永远指向固定的系统,保证可复现:今天跑、半年后跑、别人跑,环境都一模一样。
如果你做的是正经项目、尤其是要持续维护的仓库,强烈建议把 runs-on 锁成具体版本(如 ubuntu-22.04),而不是 ubuntu-latest。这样 CI 环境稳定可复现,避免"无改动却突然失败"的玄学问题。个人练手玩具用 latest 图省事则无妨。
jobs:
build:
runs-on: ubuntu-22.04 # 锁定具体版本,环境可复现
steps:
- run: cat /etc/os-release | head -n 24. 容器 runner:直接在镜像里跑
除了直接用整台虚拟机,GitHub Actions 还支持 容器 runner——你可以用 container: 字段指定一个 Docker 镜像,让 job 直接跑在这个镜像的容器里。这对于"需要特定语言/依赖版本"的场景特别方便,比如"就要 node:18 的环境"。
jobs:
build:
runs-on: ubuntu-latest
container: node:18 # 整个 job 跑在 node:18 的容器里
steps:
- run: node --version # 会输出 v18.x
- run: npm ci
- run: npm testcontainer: 写在 job 层级,意味着这个 job 里的所有 step 都在该容器中执行。此外,GitHub Actions 还支持 services: 字段来启动配套服务容器(比如一个 PostgreSQL、Redis),方便你跑需要数据库的集成测试——这个思路你先有个印象即可,具体用法我们在后续数据库/集成测试相关章节再展开。
runs-on 决定"宿主机"是什么系统(比如 Ubuntu 虚拟机),container 决定"在这台宿主机里再套一层什么容器"。两者配合:先在 Ubuntu 上起容器,再在容器里跑 step。如果只写 runs-on 不写 container,step 就直接跑在宿主机系统里。
5. 自托管 runner:用自己的机器
前面说的都是 GitHub 替你准备的机器。那如果 GitHub 提供的机器满足不了你呢?比如:
- 你需要特定硬件(GPU、超大内存、某款稀有架构);
- 你要访问公司内网的资源(内网数据库、私有制品库);
- 你对数据合规有要求,不能把代码送到公有云上跑。
这时候就可以用 自托管 runner(self-hosted runner):你把自己的机器(物理机、虚拟机、甚至树莓派)注册到仓库里,GitHub 把 job 派发到这台机器上执行。runs-on 写成 self-hosted 一类标签即可。
jobs:
heavy-build:
runs-on: [self-hosted, linux, gpu] # 用带 gpu 标签的自托管机器
steps:
- run: echo "在自家 GPU 机器上训练模型"自托管 runner 涉及机器注册、标签管理、以及非常重要的安全考量(因为是你自己的机器在执行别人可能提交的任务)。它的详细配置步骤和安全注意事项,我们会留到专门的"安全"章节系统讲解,这里你只需要知道"有这回事、适合什么场景"就够了。
6. 小结
这一章我们认识了真正干活的 runner:
- runner 是执行 job 的机器,
runs-on用来挑选用哪一类。 - runner 是 job 级别:一个 job 一台机器,step 都跑在这台机器/容器上;不同 job 默认不共享磁盘。
- GitHub 托管 runner 用 OS 标签选择:
ubuntu-latest、ubuntu-22.04、windows-latest、macos-latest等。 latest会自动升级,可能引入破坏性变化;生产环境建议锁定具体版本以保证可复现。- 容器 runner 用
container: 镜像名让 job 直接跑在指定镜像里,并可用services:启动配套服务。 - 自托管 runner 适合特定硬件/内网/合规场景,配置与安全细节留到安全章。
现在我们既知道 job 由谁执行(runner),也知道 step 是具体动作。下一个问题很实际:step 里的命令到底怎么写?怎么切换目录、指定解释器、控制超时? 下一章就来讲——如何运行 Shell 命令。
-
我在
runs-on: ubuntu-latest的 job 里第一个 step 生成了一个dist/目录,第二个 step 想用它。第二个 step 能直接用吗?如果换成一个新的 job 呢? -
为什么说生产项目用
ubuntu-latest可能"埋雷"?正确的做法是什么? -
下面这段配置会让 job 跑在哪里?
jobs:
build:
runs-on: ubuntu-latest
container: node:18
steps:
- run: node --version- 什么场景下你会考虑用
self-hostedrunner?举一个具体例子。