如果你手里还压着一台跑着 Ubuntu 18.04 的老服务器,想让它接进 GitHub Actions 当自托管 Runner,直接下载最新的 Runner 二进制包解压运行,大概率会撞上一个让人血压升高的报错:GLIBC_2.28 not found。这不是你操作错了,而是新版 Runner 的底层依赖已经超过这个老系统自带的 glibc 版本。
这事的麻烦点在于:Ubuntu 18.04 早已 EOL,官方源里能给的补丁和依赖就那么多了;而 GitHub 那边又在不停往前更新 Runner 的运行时,两边越拉越远。我一开始也试过各种“硬刚”的办法,比如给系统装新库、用环境变量指过去,结果都是这边能跑那边就崩。折腾一圈下来,真正靠谱且敢叫“终极”的做法其实是容器化:宿主机系统再旧无所谓,Runner 跑在一个干净的新版 Ubuntu 容器里,两边各过各的日子。这篇文章就把完整的部署过程、选型理由和踩坑记录都写出来,给同样被困在旧系统上的朋友一条能直接抄的路径。
1. Ubuntu 18.04 其实卡在了哪一环:先从 GLIBC_2.28 这个报错说起
1.1 一条经典报错背后的依赖链
先看现象。把官方 Runner 下载回来之后:
bash复制mkdir actions-runner && cd actions-runner
curl -o actions-runner-linux-x64-2.319.1.tar.gz -L https://github.com/actions/runner/releases/download/v2.319.1/actions-runner-linux-x64-2.319.1.tar.gz
tar xzf actions-runner-linux-x64-2.319.1.tar.gz
sudo ./bin/installdependencies.sh
./config.sh --url https://github.com/yourname/yourrepo --token YOUR_TOKEN
前面的依赖脚本大概率会报一堆 Unable to locate package 之类的错误,因为很多依赖包在 Ubuntu 18.04 的源里已经不存在了。就算你强行把 config 跑过去,真正开始执行 job 时,Runner.Listener 进程会直接起不来。
最常见的报错长这样:
code复制./Runner.Listener: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.28' not found (required by ./Runner.Listener)
这里有个背景知识。glibc 是 Linux 下几乎所有动态链接程序的地基,系统里绝大多数命令、服务、语言运行时都构建在它上面。Ubuntu 18.04 自带的 glibc 版本是 2.27,而新版 GitHub Actions Runner 在编译时链接了需要 glibc 2.28 才提供的符号。简单类比:glibc 2.27 是一个按旧图纸建的地基,新版 Runner 是按新图纸盖的房子,把新房子直接放到旧地基上,自然会塌。
1.2 为什么“跑不起来”不止是 glibc 的事
很多人以为只要解决了 glibc 就完事了,实际不止。新版 Runner 已经切换到 .NET 运行时,它除了要求 glibc 版本够新之外,还依赖 OpenSSL、libkrb5、liblttng-ust 等一堆系统库。Ubuntu 18.04 上的这些库版本同样停留在 2018 年,虽然某些库还能勉强满足要求,但整体依赖链是拆东墙补西墙。
如果是在容器里部署,这个问题会简单很多:容器内部跑的是 Ubuntu 22.04 或 24.04 的基础镜像,glibc、OpenSSL、各种运行时从一开始就是新版,Runner 的所有依赖都能被满足。宿主机只需要提供一个能跑 Docker 的 Linux 内核——而 Ubuntu 18.04 的内核版本完全够用。
1.3 为什么不能靠旧版 Runner 凑合
我最早的想法是:既然新版 Runner 跑不动,那我找一个旧版本不就行了?试过之后发现这条路走得非常难受。
一方面,GitHub 官方对自托管 Runner 的版本有硬性淘汰机制。版本过旧的 Runner 在执行 job 时会直接报 This runner version is outdated 并被强制停止注册,你想长期“冻结”在一个旧版本上根本不现实。另一方面,旧版 Runner 对 workflow 语法新特性、新的 action 版本支持也不完整,比如新版 actions/checkout 需要更高版本的 Node.js 运行时,这又回到了系统库版本不够的坑里。
所以当时我就意识到:在 Ubuntu 18.04 上装新版 Runner,不能用“把 Runner 塞进旧系统”的思路,必须换一个思路——把 Runner 放进一个它应该待的新系统里,旧系统只负责给这个容器提供运行环境。这就是方案的转折点。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 三条可走的路:升级系统、硬打补丁、容器隔离
2.1 升级系统:治本但代价不小
最直接的解法其实是把 Ubuntu 18.04 升级到 20.04 或 22.04,升级完 glibc 版本自然上去了,什么都能跑。
但这个方案的代价往往被低估。我这边的情况是服务器上还在跑一堆内部脚本、定时任务和老的构建工具链,直接做系统大版本升级,光是回归测试就要排很长的周期。do-release-upgrade 过程中还有可能因为内核驱动、软件源配置、第三方 apt 仓库不兼容而中断,一旦出问题整台机器可能起不来。
如果这台机器没有特殊历史包袱,比如是刚装好的系统或者上面跑的东西都很标准,那升级系统就是最干净的路。但如果像很多生产环境一样,机器上积累了各种不可描述的配置和旧软件,升级的隐性成本会远高于你的预期。
2.2 在宿主机硬补 glibc:看着能跑,实则埋雷
网上有不少教程教人从源码编译新版 glibc,或者从其他发行版搬一个高版本库过来,再通
