Megatron-LM CI/CD 全指南:流水线结构、PR 测试标签与内部 GitLab CI 触发排查
【免费下载链接】Megatron-LMOngoing research training transformer models at scale项目地址: https://gitcode.com/GitHub_Trending/me/Megatron-LM
Megatron-LM(NVIDIA 的 Megatron Core 大规模 Transformer 训练框架)在仓库内维护了一套完整的分层 CI/CD 体系:GitHub Actions 工作流根据 PR 标签动态决定测试范围、重复次数与容器镜像,同时提供tools/trigger_internal_ci.py把本地分支推送到内部 GitLab 并触发功能测试流水线。本文以 skills/mcore-cicd/SKILL.md 为主线,结合 .github/workflows/cicd-main.yml、tools/trigger_internal_ci.py 及 tools/trigger_internal_ci.md 等源码级证据,系统讲解:CI 流水线如何分层、各 PR 标签究竟控制什么参数、如何安全触发内部 GitLab CI、以及 CI 失败时如何从日志 artifact 中定位根因。读完本文,你将能正确地为自己的 PR 选择测试标签、安全地触发内部流水线,并独立完成一次 CI 失败的排查闭环。
一、Answer-First:先记住这套 CI 事实速查表
Megatron-LM 的 CI 对“该跑什么测试”的决策完全由PR 标签(label)驱动。SKILL 文档要求在任何涉及标签或触发的问题上,优先给出精确值,而非长篇解释。速查如下:
| 场景 | scope | n_repeat | lightweight |
|---|---|---|---|
| 无任何标签 | mr-github-slim | 2 | false |
Run tests | mr-github | 1 | true |
Run functional tests | mr-github | 5 | false |
| 合并队列(merge group) | mr-github | 1 | false(自动,无需标签) |
三个正交附加标签:
| 标签 | 作用 |
|---|---|
container::lts | 仅把容器镜像路径切换为 LTS(长期支持版)NGC PyTorch 基础镜像,而非 dev 最新版——它是一次向后兼容性检查,不改变测试集,可与任意 scope 标签组合。必须显式 opt-in:只有当用户明确要求做 LTS 验证时才附加,即使是容器或依赖变更,也不要主动添加 |
Run MBridge tests | 额外触发 MBridge(Megatron-Bridge)L1 测试套件 |
Run NeMoRL tests | 额外触发 NeMo RL 的 Megatron 功能测试套件 |
⚠️破坏性远程写入警告:tools/trigger_internal_ci.py会把当前分支**强推(force-push)**到内部 GitLab 远端上的pull-request/<branch>引用。任何情况下都必须先以--dry-run运行并确认目标引用,才能不带该标志调用;绝不针对共享分支或受保护分支运行,只允许指向你自己的 PR 分支。安全预检命令:
python tools/trigger_internal_ci.py --gitlab-origin gitlab --dry-run只有在 dry-run 输出与预期目标一致后,才可添加可选的--functional-test-*标志。
二、CI 流水线结构:从 PR push 到最终 Gate
主工作流位于 .github/workflows/cicd-main.yml,其on触发器(第 16-23 行)包括:
- push 到
pull-request/[0-9]+分支(即 CI 分支,见下文第四节) - push 到
deploy-release/*分支 merge_group事件(合并队列校验)- 每日
schedule - 手动
workflow_dispatch
同时工作流设置了concurrency组与cancel-in-progress: true,保证同一 head ref 的新 run 会取消旧 run。
整个流水线的 job 拓扑(源自 SKILL.md 的文本树,结合源码验证)如下:
is-not-external-contributor # SSO / 成员资格检查,决定 runner 与 maintainer 身份 └─ pre-flight # 复用 NVIDIA-NeMo FW-CI-templates 的预检,判定 docs_only / ci_workload 等 └─ configure # 核心决策节点:读取 PR 标签,产出 scope / container tag / n_repeat / cadence ├─ linting # autoformat.sh 检查 + golden values 校验 + 内核变更确定性覆盖检查 ├─ cicd-container-build │ ├─ cicd-parse-unit-tests → cicd-unit-tests-latest # 单元测试矩阵 │ ├─ cicd-parse-integration-tests-h100 → cicd-integration-tests-latest-h100 │ └─ cicd-parse-integration-tests-gb200 → cicd-integration-tests-latest-gb200 (仅 maintainer) └─ Nemo_CICD_Test # 最终 pass/fail 汇总门(汇总所有测试 job 结果并决定整体状态)2.1configure:流水线的“大脑”
configurejob(cicd-main.yml)是理解整套机制的关键。它用一次gh pr view调用拉取 PR 的全部标签,然后按first match wins顺序决定参数:
if [ "$IS_MERGE_GROUP" == "true" ]; then SCOPE=L1; N_REPEAT=1; LIGHTWEIGHT=false elif [ "$HAS_RUN_TESTS" == "true" ]; then SCOPE=L1; N_REPEAT=1; LIGHTWEIGHT=true elif [ "$HAS_RUN_FUNCTIONAL" == "true" ]; then SCOPE=L1; N_REPEAT=5; LIGHTWEIGHT=false elif [ "$IS_CI_WORKLOAD" == "true" ] || [ "$EVENT_NAME" == "workflow_dispatch" ]; then SCOPE=L1; N_REPEAT=5; LIGHTWEIGHT=false else SCOPE=L0; N_REPEAT=2; LIGHTWEIGHT=false fi这里有一个值得注意的映射:SKILL.md 中出现的mr-github-slim/mr-github是GitHub 侧的 legacy scope 名,在 tests/test_utils/python_scripts/recipe_parser.py 中被统一别名到新的L-tier 成本分级词汇表:
| L-tier | 含义 | legacy 别名 |
|---|---|---|
L0 | 精简 PR 测试(最便宜) | mr-github-slim |
L1 | 完整 PR / 合并队列测试 | mr-github |
L2 | 夜间测试 | nightly |
L3 | 周测试 | weekly |
L0-smoke则是 GitLab 侧专用的亚 L0 层,用于轻量 smoke 测试(只做2步训练)。scope 是成本/套件标签,而触发轴由cadence(pr/nightly/mergegroup/weekly)单独承担——测试 recipe 默认挂[pr, nightly, mergegroup]三种 cadence。
configure还会输出一套决策树摘要到 step summary,内容与 SKILL.md 的表格完全一致,并附上术语表:
lightweight:训练 4 步而非 100 步,跳过 golden values 对比——反馈更快,但不保证数值正确性lts:使用 LTS 容器基础镜像替代最新的 dev 镜像dev:默认,使用最新开发容器镜像cadence:按触发事件过滤测试的维度(recipe 中的cadence:字段)run_mbridge:是否触发 Megatron-Bridge 下游 CI,PR push 默认关闭,加Run MBridge tests标签开启run_nemo_rl:是否触发 NeMo RL 下游 CI,合并队列校验默认跳过,PR 作者可加Run NeMoRL tests标签 opt-in
2.2 镜像构建与测试矩阵
cicd-container-build通过 .github/workflows/_build_ci_container.yml 复用构建镜像,容器镜像推送到:
- AWS ECR:
766267172432.dkr.ecr.us-east-1.amazonaws.com/… - GCP Artifact Registry:
us-east4-docker.pkg.dev/nv-projdgxchipp-20260113193621/megatron-lm/…
单元测试与集成测试都是典型的parse → matrix模式:cicd-parse-*先用yq读取 tests/test_utils/recipes/h100/unit-tests.yaml 等 recipe 文件生成 JSON 矩阵,再由cicd-unit-tests-latest/cicd-integration-tests-latest-h100用matrix.include并行展开。集成测试的 recipe 覆盖了 gpt、moe、mamba、hybrid、bert、t5、mimo、multimodal-llava、各类 inference server 等数十个用例(见 tests/test_utils/recipes/h100 目录)。
GB200 测试(cicd-*-gb200)额外受双重门控:is_maintainer == 'true'且vars.ENABLE_GB200_TESTING == 'true'——非 maintainer 的 PR 默认不会触发 GB200 测试。
2.3Nemo_CICD_Test:最终状态汇总
Nemo_CICD_Test是整体 pass/fail 的收口 job:docs-only 与部署工作流直接放行;否则汇总单元测试、H100/GB200 集成测试结果,并用gh run view做全 job 扫描(排除merge-queue-notification、cicd-mbridge-testing、cicd-nemo-rl-testing),任何 failure/cancelled job 都会导致整体失败。
2.4 单元测试划分与 cadence 过滤
单元测试矩阵来自 recipe 中的unit-tests.yaml,每个测试被切分为多个 bucket 并行执行;功能测试则通过 tests/test_utils/python_scripts/generate_jet_trigger_job.py 生成 GitLab 子流水线配置,其中--cadence参数透传给 tests/test_utils/python_scripts/recipe_parser.py 的load_workloads(),按pr/nightly/mergegroup过滤 recipe 行。Run tests/Run functional tests标签会置cadence_bypass=true,让贡献者可以绕过 cadence 过滤获得手动覆盖能力。
三、PR 测试标签选择指南:一张表搞定
SKILL.md 提供了按“变更性质”选标签的权威对照表,开 PR 时直接对号入座:
| 变更路径 / 性质 | 应附加的标签 |
|---|---|
仅文档(docs/、*.md、docstring) | 无 |
仅 CI/工具链(.github/、tools/、Makefile) | 无 |
仅测试文件(tests/)——修改已有测试,未新增 golden values | Run tests |
| 新增测试用例(尚无 golden values) | Run functional tests |
重新启用被禁用的测试(scope-broken→ active) | Run functional tests |
| 非数值类库代码(日志、错误处理、CLI 标志、重构) | Run tests |
| 可能影响训练数值(模型结构、attention、优化器、分布式、MoE 路由) | Run functional tests |
容器或依赖变更(docker/、pyproject.toml、uv.lock) | Run tests(仅在用户明确要求 LTS 验证时再加container::lts) |
| 涉及 MBridge 集成 | 追加Run MBridge tests |
| 可能影响 NeMo RL 的 Megatron 集成 | 追加Run NeMoRL tests |
经验法则:默认使用Run tests;当 PR 新增测试用例(必须生成 golden values)或变更可能引起 loss 曲线偏移时,一律使用Run functional tests。
这套决策与源码完全一致:linting job 中 cicd-main.yml 会对tests/functional_tests/test_cases/**/golden_values*.json的变更运行 tools/check_golden_values.py 校验;同时用 tools/check_kernel_determinism_coverage.py 强制内核(kernel)变更必须附带确定性测试——这正是“新增测试用例必须跑Run functional tests”在流水线层的体现。
四、触发内部 GitLab CI:先 dry-run,再触发
4.1 前置条件
tools/trigger_internal_ci.py的目标用户是 NVIDIA 内部成员(SKILL 文档注明仅对 NVIDIAN 有用),它可以在不触碰 GitLab UI 的情况下完成“推分支 + 触发流水线”。完整配置步骤见 tools/trigger_internal_ci.md:
1. 添加内部 GitLab 为 git remote:
git remote add gitlab git@<gitlab-hostname>:ADLR/Megatron-LM.git git remote -v # 检查已有 remote;注意:这个 origin 名之后会作为参数传入!2. 获取 Personal Access Token:
- 打开内部 GitLab 个人资料:User menu → Edit profile → Access tokens
- 点击Add new token,填写描述、设置过期时间,并勾选
apiscope - 创建后复制生成的 token(以
glpat-开头) - 存入环境变量,避免每次手动传参:
export GITLAB_TOKEN=glpat-<your-token>建议将该变量写入.env或.bashrc。
4.2 安装依赖与用法
python -m pip install python-gitlab python tools/trigger_internal_ci.py \ --gitlab-origin gitlab \ [--access-token glpat-<your-token>] \ [--functional-test-scope mr] \ [--functional-test-repeat 5] \ [--functional-test-cases all] \ [--functional-test-name release-testing/mcore-vX.Y.Z] \ [--functional-test-time-limit 14400] \ [--dry-run]参数对照表(来自 tools/trigger_internal_ci.md):
| 参数 | 默认值 | 说明 |
|---|---|---|
--gitlab-origin | 必填 | 指向内部 GitLab 的 git remote 名 |
--access-token | $GITLAB_TOKEN | 带apiscope 的 Personal Access Token |
--functional-test-scope | mr | FUNCTIONAL_TEST_SCOPE流水线变量 |
--functional-test-repeat | 5 | FUNCTIONAL_TEST_REPEAT流水线变量 |
--functional-test-cases | all | FUNCTIONAL_TEST_CASES流水线变量 |
--functional-test-name | commit SHA | FUNCTIONAL_TEST_NAME流水线变量——为pre-release/releasescope 的 run 命名(用作 run 名与 W&B 实验名) |
--functional-test-time-limit | 随 scope 而定 | FUNCTIONAL_TEST_TIME_LIMIT流水线变量(秒)。release/weekly长时运行 scope 默认14400(4 小时),其余 scope 不设置 |
--dry-run | 关闭 | 只打印将要执行的操作,不推送、不触发 |
release 测试请使用
--functional-test-scope release,并用release-testing/mcore-v<X.Y.Z>约定命名 run(例如release-testing/mcore-v0.17.0)。
4.3 脚本真实执行流程
从 tools/trigger_internal_ci.py 源码可以看到完整调用链(main(),第 136-255 行):
- 校验 token:
--access-token或GITLAB_TOKEN缺失则直接报错退出(第 209-211 行) - 获取当前分支:
git rev-parse --abbrev-ref HEAD(第 89-97 行) - 解析 GitLab 主机名:从 remote URL 中提取(兼容
git@SSH 与 HTTPS 两种格式,第 80-86 行) - 构造目标引用:
pull-request/<branch>(常量GITLAB_BRANCH_PREFIX = "pull-request",第 36 行) - 强推分支:
git push <origin> HEAD:pull-request/<branch> --force(第 100-110 行;--dry-run时只打印日志) - 组装流水线变量并触发:固定写入
UNIT_TEST=no与INTEGRATION_TEST=no,再叠加FUNCTIONAL_TEST_SCOPE/REPEAT/CASES,以及可选的FUNCTIONAL_TEST_NAME、FUNCTIONAL_TEST_TIME_LIMIT、CLUSTER_A100/H100/GB200;最终通过python-gitlab在项目 19378 上创建 pipeline(第 113-133 行)
脚本内部还包含一个细节:resolve_time_limit()(第 51-66 行)只在release/weekly两个长时 scope 下自动注入 4 小时上限,其余 scope 保持默认,避免为短时测试施加不必要的超时限制。
4.4 预期输出
Current branch: my-feature-branch Everything up-to-date Triggering pipeline on https://<gitlab-hostname> project 19378 @ pull-request/my-feature-branch Pipeline triggered: https://<gitlab-hostname>/<namespace>/<project>/-/pipelines/123456对应 SKILL.md 的约束:只针对你自己的 PR 分支操作,绝不触碰共享/受保护分支。当触发 GitLab 功能测试时,子流水线由generate_jet_trigger_job.py生成:它读取 recipe、按 model 分 stage、为每个 test_case 生成一个 GitLab job,并支持--enable-lightweight-mode(2 步 smoke 测试)、--enable-warmup(首个 job 作为后续 job 的缓存预热依赖)等开关。
五、CI 失败排查:从分支名到根因的完整链路
CI 分支永远遵循pull-request/<number>命名模式,这是所有排查的起点。
5.1 从 CI 分支定位 PR
# 从当前分支提取 PR 号 PR_NUMBER=$(git rev-parse --abbrev-ref HEAD | grep -oP '(?<=pull-request/)\d+') # 查看 PR 元数据(标题、标签、作者、基础分支) gh pr view "$PR_NUMBER" --repo NVIDIA/Megatron-LM # 查看该 PR 的变更集 gh pr diff "$PR_NUMBER" --repo NVIDIA/Megatron-LM5.2 读取 CI Job 日志
# 列出该 PR 最近的 workflow run gh run list --repo NVIDIA/Megatron-LM --branch "pull-request/$PR_NUMBER" # 流式输出失败 job 的日志 gh run view <run-id> --repo NVIDIA/Megatron-LM --log-failed关键点:各 rank 的完整日志不在runner 的 stdout 里,而是作为 GitHub artifacts 上传,命名格式为logs-<test_case>-<run_id>-<uuid>:
# 1. 先查出 artifact 名称 gh run view <run-id> --repo NVIDIA/Megatron-LM --json artifacts \ --jq '.artifacts[].name' # 2. 下载 artifact zip gh run download <run-id> --repo NVIDIA/Megatron-LM \ --name "logs-<artifact-name>" -D ./ci-logs # 3. 定位哪些 rank 日志含有错误 grep -r -l "ERROR\|Traceback\|FAILED\|fatal" ./ci-logs/ # 4. 日志文件可能超过 10000 行——永远不要一次性读完整份日志 wc -l ./ci-logs/<test>/<attempt>/attempt_0/<rank>/stderr.log sed -n '1,200p' ./ci-logs/.../stderr.log # 分段读取5.3 按失败类型定位根因
SKILL.md 给出五类典型失败的处理路径:
- Linting 失败—— 本地重跑
tools/autoformat.sh,diff 会精确显示需要修改的内容(该脚本在 linting job 中以CHECK_ONLY=true运行,见 cicd-main.yml) - 容器构建失败—— 检查
cicd-container-buildjob 日志 - 单元测试失败—— 失败的 bucket 位于
cicd-unit-tests-latestjob 的 matrix 中 - 功能测试失败—— 查看
cicd-integration-tests-*job,从rank 0 的stdout.log开始 - Flaky 测试—— runner 会自动重试至多 3 次;若重试耗尽且错误模式匹配已知的瞬时问题(NCCL、ECC、segfault),则属于基础设施噪声
5.4 将失败与 PR 变更集关联
# 找出覆盖了被改源码文件的单元测试 grep -r "from megatron.core.transformer.attention" tests/unit_tests/ -l # 查看 CODEOWNERS 确认 reviewer 分配 cat .github/CODEOWNERS | grep "<changed-path>".github/CODEOWNERS(位于仓库 .github 目录下)是确认各路径责任人的权威来源;结合gh pr diff输出的变更文件清单,即可快速圈定最可能受影响的测试模块与需要拉入 review 的人。
六、实践要点与避坑清单
- 先答事实,再讲道理:问标签选什么、触发什么参数时,直接给出第一节速查表中的精确值,这是仓库 CI 文档的第一原则。
container::lts是 opt-in:无论改动是否涉及容器/依赖,都不应主动附加该标签,除非用户明确要求做 LTS 兼容性验证。- dry-run 是铁律:
tools/trigger_internal_ci.py每次真实执行都是一次对pull-request/<branch>的 force-push,务必先跑--dry-run核对目标引用,且只作用于自己的 PR 分支。 - 数值型变更默认
Run functional tests:凡是可能影响 loss 曲线的改动(模型结构、attention、优化器、分布式、MoE 路由),都应触发 5 次重复 + golden values 对比的完整功能测试;仅日志/CLI/重构等非数值变更用Run tests(1 次重复、轻量模式)即可。 - 日志分段读取:单份 rank 日志可能超过一万行,务必先用
wc -l探明规模,再sed分段查看,避免一次读取吞掉上下文。 - 理解参数在源码中的落点:
scope/n_repeat/lightweight三个值最终经由 recipe_parser.py 的 scope 别名与 cadence 过滤,映射到具体的 recipe 测试行;阅读 tests/test_utils/recipes/h100 下的 YAML 可以精确预判“这个标签组合实际会跑哪些用例”。
参考文件速览
- 技能文档:skills/mcore-cicd/SKILL.md
- 主工作流:.github/workflows/cicd-main.yml
- 容器构建工作流:.github/workflows/_build_ci_container.yml
- 触发脚本:tools/trigger_internal_ci.py 与配置说明 tools/trigger_internal_ci.md
- recipe 解析:tests/test_utils/python_scripts/recipe_parser.py
- GitLab 任务生成:tests/test_utils/python_scripts/generate_jet_trigger_job.py
- 测试 recipe:tests/test_utils/recipes/h100(含 unit-tests.yaml 与各模型功能测试用例)
- 校验工具:tools/check_golden_values.py、tools/check_kernel_determinism_coverage.py、tools/autoformat.sh
- 代码归属:.github/CODEOWNERS
【免费下载链接】Megatron-LMOngoing research training transformer models at scale项目地址: https://gitcode.com/GitHub_Trending/me/Megatron-LM
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考