GitHub Actions 搭建 ROCm 自动化测试流水线,每次提交都验真机
为什么要在 CI 里跑真机
做 DevOps 的朋友都有个共识:单元测试在 x86 CPU 上跑通了,不代表在 GPU 上也能稳如老狗。尤其是涉及 ROCm 这种对驱动版本、内核参数极其敏感的生态,光靠模拟或者交叉编译,很难发现那些“只在特定硬件上复现”的诡异 Bug。
之前我们团队在推进大模型从 CUDA 迁移到 AMD 平台时,就吃过不少亏。代码在开发机上好好的,一合并到主分支,在生产环境的 MI250 上就段错误。后来我们痛定思痛,决定把GitHub Actions和真实 AMD GPU 实例深度绑定,打造一套“每次提交必验真机”的自动化流水线。这套方案不仅涵盖了基础的 HIP 代码转换验证,还集成了 SGLang 推理服务的性能回归测试,确保合入的每一行代码都经得起实战考验。
基础设施:搞定自托管 Runner
GitHub 官方的 Hosted Runners 目前对 AMD GPU 的原生支持还不够灵活,特别是我们需要特定的 ROCm 版本(比如 6.2+)来配合 SGLang 和 TileLang 时,官方镜像往往滞后。因此,最稳妥的方案是搭建自托管 Runner (Self-hosted Runner)。
我们在云端申请了配备 MI210/MI250 的虚拟机,安装好 Ubuntu 22.04 和对应的 ROCm 驱动。接下来的步骤并不复杂,但有几个坑需要注意:
- 用户权限:运行 runner 的用户必须加入
render和video用户组,否则容器无法访问/dev/kfd和/dev/dri设备。 - Docker 配置:如果使用 Docker 执行作业,务必在 daemon.json 中启用 NVIDIA Container Toolkit 的 AMD 对应版(即
--device /dev/kfd --device /dev/dri),或者直接使用支持 ROCm 的 runtime。
注册 Runner 后,我们在仓库的 Settings -> Actions -> Runners 中就能看到它在线。为了安全起见,建议给这个 Runner 打上自定义标签,比如 roc-gpu-mi200,方便在 Workflow 中精准调度。
编写 Workflow:定义 GPU 作业
核心逻辑都在 .github/workflows/rocm-ci.yml 里。我们要做的不仅是跑通 pytest,还要验证 HIPify 的转换结果以及 SGLang 服务的启动情况。
下面是一个经过实战打磨的配置片段,展示了如何调用真机并设置环境:
name: ROCm Hardware Validation
on:
push:
branches: [ "main", "develop" ]
pull_request:
branches: [ "main" ]
jobs:
hip-verify-and-inference:
runs-on: self-hosted
# 关键:通过标签锁定带有 AMD GPU 的 Runner
labels: roc-gpu-mi200
container:
image: rocm/pytorch:rocm6.2.3-ubuntu22.04
options: --device /dev/kfd --device /dev/dri --group-add video --ipc=host --shm-size 16G
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Install Dependencies
run: |
pip install -r requirements-rocm.txt
pip install sglang tile-lang llama-factory
- name: Verify HIPify Migration
run: |
echo "Checking for residual CUDA calls..."
# 这里可以运行脚本扫描代码,确保没有硬编码的 cudaMalloc 等
python scripts/check_hip_compatibility.py src/
- name: Run Unit Tests on GPU
run: |
pytest tests/unit/test_kernels.py -v --device=all
- name: SGLang Inference Regression Test
run: |
# 启动 SGLang 服务并在后台运行
python -m sglang.launch_server \
--model-path meta-llama/Llama-3-8B-Instruct \
--port 30000 \
--mem-fraction-static 0.8 &
# 等待服务就绪
sleep 15
# 发送测试请求验证推理是否正常
curl -X POST http://localhost:3000/generate \
-H "Content-Type: application/json" \
-d '{"text": "Hello ROCm", "max_tokens": 50}'
# 检查退出码,非 0 则流水线失败
if [ $? -ne 0 ]; then exit 1; fi
在这个配置中,我们特意拉取了包含 PyTorch ROCm 版本的官方 Docker 镜像,避免了在 Runner 宿主机上污染环境。--ipc=host 和 --shm-size 是大模型推理的标配,不然 SGLang 启动时很容易因为共享内存不足而崩溃。
策略优化:触发条件与资源保护
让 CI 跑真机虽然爽,但成本也高。如果每次改文档都触发一次耗时的 GPU 测试,队列会瞬间堵塞,电费也吃不消。我们需要更精细的触发策略。
对于单元测试,我们可以保持高频触发,只要涉及 src/ 下的核心算子或 requirements 变动就立即运行。但对于SGLang 性能回归测试这类耗时较长的任务,建议加上路径过滤:
on:
pull_request:
paths:
- 'src/**'
- 'configs/**'
- '.github/workflows/rocm-ci.yml'
此外,为了防止恶意 PR 耗尽集群资源,可以在 Job 级别增加并发限制。利用 GitHub Actions 的 concurrency 特性,确保同一分支的旧运行在新运行开始时自动取消:
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
这样既能保证最新的提交得到及时验证,又不会让过时的任务空转占用宝贵的 GPU 卡时。
避坑指南:环境与依赖的隔离
在实际落地过程中,最让人头大的往往是环境问题。比如某个算子在 PyTorch 2.4 + ROCm 6.1 下正常,升级到 6.2 后却出现了数值精度偏差。
我们的经验是:不要信任宿主机的全局环境。所有测试必须在容器内进行,并且要在 requirements-rocm.txt 中锁死关键库的版本号。特别是 flash-attention 和 deepspeed 这类需要编译的依赖,必须确保它们是针对当前 ROCm 版本重新编译的,而不是直接用了预编译的 CUDA 包。
另外,关于 TileLang 优化的算子验证,建议在 CI 中加入一个简单的基准对比脚本。记录关键算子在标准实现和 TileLang 优化后的执行时间,如果新提交的代码导致性能下降超过 5%,直接报错拦截。这能有效防止“功能对了,性能崩了”的情况发生。
结语
将真实 AMD GPU 引入 CI 流水线,起初看起来增加了运维复杂度,但从长远看,它极大地降低了集成阶段的沟通成本和修复成本。现在,团队成员在提交涉及 ROCm 的代码时心里更有底了,因为他们知道,只要绿灯亮起,代码就能在生产环境的 MI250 上稳定运行。这套流程不仅适用于 SGLang 或 LLaMA-Factory 的适配,对于任何需要在异构计算平台上交付高质量软件的团队来说,都是值得投入的基础设施建设。
200小时GPU算力已就位,快来领取:https://marketing.csdn.net/questions/Q2604140858304426315?utm_source=AIpaper

更多推荐




所有评论(0)