ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

OpenClaw-RL 之 slime 子项目 GPU 自托管 CI 基础设施部署指南

OpenClaw-RL 之 slime 子项目 GPU 自托管 CI 基础设施部署指南
  • 人工智能
  • 大模型
  • 强化学习
  • AI Agent
  • 微调

【免费下载链接】OpenClaw-RL

OpenClaw-RL: Train any agent simply by talking

项目地址:https://gitcode.com/gh_mirrors/op/OpenClaw-RL
点击查看免费下载

本文围绕 slime/tests/ci/README.md 展开,系统讲解 OpenClaw-RL 仓库中 slime 子项目如何构建一套面向大规模强化学习(RL)训练的 GitHub Actions 持续集成(CI)体系:从 GitHub Secrets 配置、自托管 GPU Runner 的容器化部署,到 GPU 锁竞争机制与端到端训练测试的编排。读完本文,你将掌握如何在自有 GPU 机器上完整复刻一套支持多 Runner 并发、资源互斥、可快速调试的 RL 训练 CI 环境。

一、为什么需要一套自托管 GPU CI

slime 是 OpenClaw-RL 仓库中的大规模 RL 训练框架,其测试以端到端(e2e)训练脚本为主。以 tests/test_qwen2.5_0.5B_gsm8k_short.py 为例,一个测试就需要拉起 SGLang rollout 引擎与 Megatron 训练后端,真正运行 Qwen2.5-0.5B 的 GRPO 训练并完成 GSM8K 评测——这显然不是 GitHub 托管的ubuntu-latest虚拟机所能承担的工作负载。因此,slime 的 CI 采用了"GitHub 托管跑格式检查 + 自托管跑 GPU 训练"的混合策略:

  • pre-commit.yml 使用ubuntu-latest托管 Runner,负责代码风格与静态检查;
  • pr-test.yml 与 conda-ci.yml 声明runs-on: self-hosted,在带有 GPU 的裸机/容器 Runner 上执行真正的训练测试。

整个自托管 Runner 的搭建方法、资源互斥机制与调试手段,正是本文的核心内容。

二、第一步:配置 GitHub Secrets

CI 训练过程中需要将训练指标上报到 Weights & Biases(W&B)平台,工作流中通过secrets.WANDB_API_KEY注入(见 pr-test.yml 的env段)。配置方式为:

  1. 进入 GitHub 仓库的Settings → Secrets and variables → Actions页面;
  2. 添加名为WANDB_API_KEY的 repository secret,其值在 W&B 账号的授权页面获取(https://wandb.ai/authorize对应的 Authorize 页面);
  3. 保存后,CI 工作流即可在运行时自动解密注入。

从源码层面看,该 Key 的作用链路清晰:测试脚本通过U.get_default_wandb_args(__file__)(见 command_utils.py)读取环境变量WANDB_API_KEY,若不存在则跳过 wandb 配置并打印提示;存在则生成--use-wandb --wandb-project slime-{测试名} --wandb-group {run_id}_{GITHUB_COMMIT_NAME}等参数,其中GITHUB_COMMIT_NAME由工作流设为${{ github.sha }}_${{ github.event.pull_request.number }},保证每个 commit 的训练曲线可追溯。

三、第二步:搭建新的 GitHub Runner(三步骤详解)

Step 1:编写.env环境文件

在 github_runner 目录下,仿照.env.example编写.env文件:

GITHUB_RUNNER_URL=https://github.com/THUDM/slime GITHUB_RUNNER_TOKEN=paste-your-token-here

其中GITHUB_RUNNER_TOKEN从 GitHub 仓库的Settings → Actions → Runners → New self-hosted runner页面(选择 Linux x64 架构)获取。特别注意:该 Token 具有时效性,会在一段时间后失效,需在部署前重新获取。

从 docker-compose.yml 可以看到,这两个变量在容器启动时通过 entrypoint 被消费:/home/runner/config.sh --url ${GITHUB_RUNNER_URL} --token ${GITHUB_RUNNER_TOKEN} ...完成 Runner 注册。

Step 2:准备/home/runner/externals

Runner 镜像运行在宿主机之外,需要将官方 Runner 镜像内的externals(Runner 运行时依赖的外部可执行文件集)复制到宿主机目录,供容器以卷挂载方式使用。文档给出了一组可直接执行的命令:

# 1) 在宿主机上创建 externals 目录(借助特权容器操作根文件系统) docker run --rm -it --privileged --pid=host -v /:/host_root ubuntu /bin/bash -c 'rm -rf /host_root/home/runner/externals && mkdir -p /host_root/home/runner/externals && chmod -R 777 /host_root/home/runner/externals' # 2) 启动一个临时容器,将镜像内的 externals 拷贝出来 docker run -d --name temp-runner ghcr.io/actions/actions-runner:2.328.0 tail -f /dev/null docker cp temp-runner:/home/runner/externals/. /home/runner/externals docker rm -f temp-runner # 3) 校验拷贝结果 ls -alh /home/runner/externals

注意:--privileged --pid=host -v /:/host_root的组合会以宿主机根目录为挂载点执行写操作,务必在专用 CI 机器上执行,避免误操作生产环境。

Step 3:启动 Runner 集群

cd /mnt/data/tom/primary_synced/slime/tests/ci/github_runner docker compose up -d

(在实际使用中,将cd路径替换为你本地仓库中 github_runner 目录的绝对路径。)

四、深入 docker-compose:8 副本 Runner 集群的配置解析

github_runner/docker-compose.yml 是整个部署的核心,关键配置如下:

配置项值说明
imageghcr.io/actions/actions-runner:2.329.0官方 Runner 镜像。注释强调必须保持最新,否则 Runner 会试图自我升级,导致版本漂移问题
volumes/var/run/docker.sock、/data/slime_ci、/home/runner/externals分别提供 Docker-in-Docker 能力、共享 CI 工作区(模型/数据集缓存)、Runner 运行时依赖
deploy.replicas8单台宿主机拉起 8 个 Runner 副本,构成一个小型并发池
restartalways容器异常退出后自动拉起,保证 CI 可用性
environmentRUNNER_ALLOW_RUNASROOT: "1"允许以 root 身份运行 Runner(官方默认禁止)
privileged/usertrue/root特权容器模式,赋予 GPU 与内核能力访问
entrypointconfig.sh+run.sh每次启动先注册再运行

entrypoint 的核心逻辑值得展开:

entrypoint: > sh -c " cd /data/slime_ci && /home/runner/config.sh --url ${GITHUB_RUNNER_URL} --token ${GITHUB_RUNNER_TOKEN} --unattended --work /data/slime_ci/runner_$(hostname) --disableupdate && /home/runner/run.sh "
  • --unattended:无交互式注册;
  • --work /data/slime_ci/runner_$(hostname):每个副本使用以主机名为后缀的工作目录,避免多个副本抢占同一_work目录;
  • --disableupdate:禁用自动更新,配合"镜像保持最新"策略;
  • 源码注释中还留有 TODO:config.sh不应重复执行,暗示长生命周期下可能存在的注册状态问题,部署时值得关注。

五、调试与快速迭代

文档给出了完整的运维三板斧:

# 查看所有容器日志(compose 聚合) docker compose logs -f # 只看某一个 Runner 容器 docker logs -f github_runner-runner-1 # 进入容器排查(如检查 config.sh 注册状态) docker exec -it github_runner-runner-1 /bin/bash

快速迭代模式(重置并重启整个集群):

docker compose down -v && docker compose up -d && docker logs -f github_runner-runner-1

-v会连带删除匿名卷,适合反复调试注册流程;down && up保证 8 个副本以全新状态重新注册。

六、GPU 资源互斥:gpu_lock_exec.py的锁机制

多个 Runner 副本并发抢占同一批 GPU 时,若不做资源互斥,训练测试会互相踩踏。slime 为此提供了 tests/ci/gpu_lock_exec.py,它基于fcntl.flock(文件锁)实现了一套"按需持锁、用完释放"的 GPU 分配器,其工作方式如下:

  • 锁文件:默认以/dev/shm/custom_gpu_lock_{gpu_id}.lock为每张 GPU 创建独立锁文件(可通过--lock-path-pattern自定义,且必须包含{gpu_id}占位符);
  • 持锁:通过FdLock.lock()对 fd 执行flock(LOCK_EX | LOCK_NB)非阻塞排他加锁;os.set_inheritable(fd, True)保证execvp替换进程后锁依然有效,训练进程存活期间锁不会丢;
  • 分配策略:--devices 0,1指定显式 GPU 集合;--count N则自动扫描并凑齐任意 N 张空闲 GPU(_try_acquire_count);
  • 等待与超时:获取失败时以SLEEP_BACKOFF * random()随机退避重试(默认退避上限 5 秒),--timeout控制最长等待时间(默认 24 小时);
  • 探测模式:--print-only不加锁只探测,输出当前空闲 GPU 列表,可用于人工排障;
  • 注入环境变量:成功持锁后把选中的 GPU 列表以逗号分隔写入--target-env-name(默认CUDA_VISIBLE_DEVICES),随后execvp替换自身启动真正的测试命令。

典型用法(来自 CI 工作流与文档注释):

# 申请任意 4 张空闲 GPU 运行 GSM8K 短测试 python tests/ci/gpu_lock_exec.py --count 4 -- python tests/test_qwen2.5_0.5B_gsm8k_short.py # 指定 GPU 0,1 并设置 10 分钟等待超时 python tests/ci/gpu_lock_exec.py --devices 0,1 --timeout 600 -- python tests/test_qwen3_0.6B_fsdp_distributed.py

七、CI 工作流如何调度这些测试

pr-test.yml 是 GPU 测试的调度中枢,其设计要点:

  • 按标签触发:e2e-test-short、e2e-test-fsdp、e2e-test-megatron、e2e-test-long等 job 各自绑定形如run-ci-short、run-ci-fsdp的 PR 标签,只有被打上对应标签才会执行,避免每次 push 都跑全量;
  • 并发控制:concurrency.group以工作流 + PR 号为 key,cancel-in-progress: true保证同一 PR 的新 commit 会取消旧任务;
  • 容器化运行:job 使用slimerl/slime:latest镜像,options中挂载--gpus all、--ipc=host、--shm-size=16g、--ulimit memlock=-1,并把/data/slime_ci下的模型与数据集缓存目录挂载进容器;
  • 矩阵并行:strategy.matrix列出多个(num_gpus, test_file)组合,例如e2e-test-megatron同时跑 GLM4-9B、Qwen3-30B-A3B(含 DeepEP + FP8 rollout 变体)、Qwen3-4B PPO、Moonlight-16B-A3B 等 8 卡测试;
  • 执行入口统一:每个 job 的 Execute 步骤都是python tests/ci/gpu_lock_exec.py --count ${{ matrix.info.num_gpus }} -- python tests/${{ matrix.info.test_file }}——即先通过第六节的锁机制申请 GPU,再运行具体测试;
  • 环境变量透传:WANDB_API_KEY、SLIME_TEST_ENABLE_INFINITE_RUN、SLIME_TEST_USE_DEEPEP、SLIME_TEST_USE_FP8_ROLLOUT、SLIME_TEST_ENABLE_EVAL统一注入,控制测试的开关与精度变体。其中SLIME_TEST_ENABLE_INFINITE_RUN由 command_utils.py 的get_env_enable_infinite_run()消费,支持workflow_dispatch手动触发"无限训练"模式。

八、测试脚本中的 CI 模式与容错开关

除了基础设施,测试脚本本身也内建了 CI 适配逻辑,保证"本地可跑、CI 可验"。以 test_qwen2.5_0.5B_gsm8k_short.py 为例:

  • 脚本拼接完整的训练参数(rollout、GRPO、优化器、SGLang 引擎、容错等),并统一追加--ci-test标志;
  • 启动前显式清除http_proxy/https_proxy等代理环境变量,避免代理干扰训练中的网络通信(工作流本身通过-e http_proxy=$http_proxy透传代理用于拉取依赖,训练阶段则需移除)。

--ci-test标志在源码中有多处联动(见 arguments.py 的add_ci_arguments):

  • 开启--ci-fault-tolerance时,ray/rollout.py 会在第 2 个 rollout 触发故障注入,验证健康检查与自动恢复链路;
  • megatron_utils/model.py 中启用 KL 检查器(可被--ci-disable-kl-checker关闭),并限制 MTP(Multi-Token Prediction)训练行为;
  • metric_utils.py 结合--ci-metric-checker-key/--ci-metric-checker-threshold对关键训练指标做阈值断言,让 CI 能自动判定训练质量是否达标。

九、从零到一:部署清单

综合全文,在一台新 GPU 机器上落地 slime 自托管 CI 的完整清单如下:

  1. 仓库侧:在 GitHub 仓库 Actions Secrets 中添加WANDB_API_KEY;
  2. Runner 侧:获取新的 runner token,在 github_runner 下编写.env(参照 .env.example);
  3. 宿主侧:按第三节 Step 2 的命令准备/home/runner/externals,并确认/data/slime_ci目录存在(模型、数据集缓存的共享工作区);
  4. 启动:docker compose up -d拉起 8 副本 Runner,用docker compose logs -f观察注册与心跳;
  5. 验证:在 PR 上打对应标签(如run-ci-short)触发 pr-test.yml,观察gpu_lock_exec的加锁日志与训练曲线。

至此,一套支持多 Runner 并发、GPU 互斥分配、训练质量自动校验的 RL 训练 CI 环境即可稳定运行。

  • 人工智能
  • 大模型
  • 强化学习
  • AI Agent
  • 微调

【免费下载链接】OpenClaw-RL

OpenClaw-RL: Train any agent simply by talking

项目地址:https://gitcode.com/gh_mirrors/op/OpenClaw-RL
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进