实战记录:在 Ubuntu 18.04 旧系统上部署新版 GitHub Actions Runner 的终极方案
1. 背景与痛点
最近在为公司的嵌入式项目配置 CI/CD,遇到了一个极其棘手的"死锁"问题:
- 编译环境锁死 :项目依赖 Qt 5.10 和特定的交叉编译链 (
aarch64-linux-gnu-g++),必须运行在 Ubuntu 18.04 (GLIBC 2.27) 环境下。 - Runner 强制更新 :GitHub Actions Runner 强制要求更新到 v2.330+ 版本,而新版 Runner 底层依赖 Node.js 20,必须要求 GLIBC 2.28+ (即 Ubuntu 20.04+)。
矛盾点:
- 在宿主机直接装 Runner -> 报错
GLIBC_2.28 not found,无限重启。 - 升级宿主机系统 -> 编译环境崩坏,项目跑不起来。
- 在 Docker 里跑编译 -> 迁移复杂的交叉编译环境成本太高。
2. 最终架构设计:Docker + SSH "遥控"模式
为了同时满足"Runner 不崩"和"编译环境不变",我采用了一套分离式架构:
- 指挥官 (Runner) :运行在 Docker 容器 (Ubuntu 20.04) 中。负责与 GitHub 通信,满足 GLIBC 要求。
- 执行者 (Host) :原本的 Ubuntu 18.04 宿主机。负责执行核心编译任务。
- 通信:Runner 通过 SSH 免密登录连接宿主机,发送指令。
3. 踩坑与解决方案全记录
3.1 网络"第一难":解决连接被墙
现象 :
curl 下载报错 OpenSSL SSL_connect: SSL_ERROR_SYSCALL,git clone 报错 Connection closed。
原因 :
公司防火墙/运营商阻断了 SSH (22端口) 和部分 GitHub IP。
解决方案:
-
清理 DNS :检查
/etc/hosts,删除陈旧的 GitHub IP 绑定。 -
SSH 走 HTTPS 通道 :修改
~/.ssh/config,强制 SSH 走 443 端口:textHost github.com Hostname ssh.github.com Port 443 User git
3.2 启动 Runner:Docker 容器化
使用 myoung34/github-runner 镜像,彻底解决 GLIBC 版本问题。
关键配置:
- 使用
--net host模式,方便连接宿主机。 - 使用
--restart always确保服务器重启后自动恢复。 - 注意环境变量名:此镜像使用
REPO_URL和RUNNER_TOKEN。
bash
sudo docker run -d --restart always --name cicd-runner \
--net host \
-e RUNNER_TOKEN="<GitHub_Registration_Token>" \
-e REPO_URL="https://github.com/organization/repository" \
-e RUNNER_LABELS="cicd-runner" \
myoung34/github-runner:latest
3.3 打通任督二脉:配置 SSH 免密登录
为了让 Docker 容器能控制宿主机:
- 进入容器:
docker exec -it cicd-runner bash - 生成密钥:
ssh-keygen -t rsa - 授权 :将容器内的
id_rsa.pub内容,追加到宿主机 的~/.ssh/authorized_keys文件中。 - 测试:在容器内执行
ssh user@127.0.0.1无需密码即可登录。
3.4 脚本化管理:告别臃肿的 YAML
为了避免在 YAML 的 ssh << EOF 块中写几十行复杂的逻辑,我将编译逻辑封装为 build.sh 脚本放入代码仓库。
流程优化:
- YAML :负责 checkout 代码,将
build.sh通过scp传送到宿主机的/tmp目录。 - SSH :远程执行
/tmp/build.sh,并透传 GitHub Secrets。 - Host :在宿主机执行实际的
qmake、make、AWS 上传等操作。
YAML 示例:
yaml
- name: Execute build.sh on Host
run: |
# 传送脚本
scp -o StrictHostKeyChecking=no .github/scripts/build.sh user@127.0.0.1:/tmp/build.sh
# 远程执行
ssh -o StrictHostKeyChecking=no user@127.0.0.1 \
"export AWS_KEY='${{ secrets.AWS_KEY }}' && /bin/bash /tmp/build.sh"
3.5 性能优化:解决内存溢出
现象 :
编译 QML 资源文件时报错:virtual memory exhausted: Cannot allocate memory。
原因 :
宿主机内存仅 2GB,且 Swap 空间不足,多线程编译 (make -j) 瞬间撑爆内存。
解决方案:
-
增加 Swap :创建 4GB 的 Swap 文件。
bashsudo fallocate -l 4G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile # 写入 /etc/fstab 实现开机挂载 -
限制并发 :在脚本中将
make -j$(nproc)改为make -j2(视情况而定)。
4. 总结
通过这套方案,我们达成以下目标:
- 兼容性:完美保留了 Ubuntu 18.04 的旧编译环境,无需重构项目。
- 稳定性:Runner 运行在官方推荐的 Ubuntu 20.04+ 容器中,永不因升级而崩溃。
- 自动化:利用 Docker 的自启特性,服务器重启后 CI/CD 自动恢复上线。
- 低成本:无需升级硬件,通过 Swap 优化解决了内存瓶颈。
这可能是目前解决 "旧系统开发环境 vs 新版 GitHub Actions" 冲突最优雅的解法。
Tags : DevOps, GitHub Actions, Docker, Ubuntu 18.04, CI/CD, Embedded Systems
Author: Tuo