Prhub

#33044 [CI] Pin NCCL ports for GB300 PR tests

原始 PR 作者 Fridge003 合并时间 2026-07-31 17:28 文件变更 3 提交数 4 评论 6 代码增减 +26 / -0

执行摘要

固定 GB300 PR 测试 NCCL 端口,消除 EADDRINUSE 竞争失败

PR body 指出根因:GB300 PR 作业通过 bind(:0) 选择临时 nccl_port 并在释放后,rank 0 的 Torch TCPStore 再绑定同一端口时触发 EADDRINUSE,并附了两个失败链接(39539/3953739541/39545)。第二次失败发生在 test_flashinfer_a2a.py,说明该竞争影响所有遗留 nccl_port=None 的 GB300 PR 服务器测试。

值得快速浏览,尤其是对 CI 测试稳定性维护感兴趣的工程师。其设计决策——从共享默认端口常量推导基准值并分段偏移——比硬编码端口更易维护;同时刻意避开 #31731 的全局方案,保持改动面最小,是好的工程边界把握。

讨论亮点

无正式 review 评论。issue 评论里作者发起了两次 /rerun-test:第一次(Run #30616149775)仍失败,第二次(Run #30618506277)全部通过,验证了修复有效。作者还主动做了 AST 层面的端口分配断言和格式检查。

实现拆解

  1. 在每个测试文件顶部从 DEFAULT_PORT_FOR_SRT_TEST_RUNNER 推导 NCCL_PORT_BASE(分别 +100/+110/+120),并注释保证端口低于 GB300 runner 临时端口下限 10240。
  2. test_deepseek_v3_cutedsl_4gpu.pyTestDeepseekR1Nvfp4CuteDSLDeepEPTestDummyWithSBO 两个服务器启动添加 --nccl-port(BASE、BASE+1)。
  3. test_flashinfer_a2a.py 的三个测试类添加 --nccl-port(BASE、BASE+1、BASE+2)。
  4. test_disaggregation_aarch64.pystart_prefillstart_decode(并发启动)分别添加 BASE 和 BASE+1。
  5. 配套验证:AST 检查确认七个启动都有显式端口、端口唯一且低于 10240;Ruff 0.15.1 lint/format、py_compile、pre-commit 全绿。
文件 模块 状态 重要度
test/registered/4-gpu-models/test_deepseek_v3_cutedsl_4gpu.py 模型测试 modified 4.41
test/registered/ep/test_flashinfer_a2a.py EP 测试 modified 4.27
test/registered/disaggregation/test_disaggregation_aarch64.py 分离部署 modified 4.2

关键符号

TestFlashinferA2ATrtllmRoutedFP4.setUpClass TestFlashinferA2ACutedslStaticFP4.setUpClass TestFlashinferA2ATrtllmRoutedFP8.setUpClass TestDeepseekR1Nvfp4CuteDSLDeepEP.setUpClass TestDummyWithSBO.setUpClass TestDisaggregationMooncakeAARCH64Accuracy.start_prefill TestDisaggregationMooncakeAARCH64Accuracy.start_decode

关键源码片段

test/registered/ep/test_flashinfer_a2a.py test-coverage

三个 FlashInfer A2A 测试类均受影响,是第二次失败点(`test_flashinfer_a2a.py`)所在文件。

# 从测试工具共享的默认端口常量推导出本文件的端口基准。
# GB300 runner 的临时端口区间从 10240 开始,
# 因此把 NCCL 端口固定在其下方,避免 bind 竞争导致的 EADDRINUSE。
NCCL_PORT_BASE = DEFAULT_PORT_FOR_SRT_TEST_RUNNER + 110class TestFlashinferA2ATrtllmRoutedFP4(CustomTestCase):
    """flashinfer A2A + flashinfer_trtllm_routed 的 modelopt_fp4 路径(DeepSeek V3)。"""
​
    @classmethod
    def setUpClass(cls):
        cls.model = DEEPSEEK_V3_FP4_MODEL
        cls.base_url = DEFAULT_URL_FOR_TEST
        # 每个测试类使用不同的端口偏移,确保同一文件内多个启动不冲突。
        cls.process = popen_launch_server(
            cls.model,
            cls.base_url,
            timeout=SERVER_LAUNCH_TIMEOUT,
            other_args=[
                "--tp", "4",
                "--ep", "4",
                "--dp", "4",
                "--enable-dp-attention",
                "--nccl-port", str(NCCL_PORT_BASE),
                "--moe-a2a-backend", "flashinfer",
                "--moe-runner-backend", "flashinfer_trtllm_routed",
                "--quantization", "modelopt_fp4",
                "--disable-flashinfer-autotune",
                "--model-loader-extra-config",
                '{"enable_multithread_load": true}',
            ],
        )
test/registered/disaggregation/test_disaggregation_aarch64.py test-coverage

AArch64 分离部署测试并发启动 prefill 和 decode 两个服务器,最能体现 " 每个启动独立端口 " 的设计约束。

# 每个 GB300 PR 测试文件使用独立的基准偏移,
# 避免并行运行的多个测试文件共享相同端口区间。
NCCL_PORT_BASE = DEFAULT_PORT_FOR_SRT_TEST_RUNNER + 120class TestDisaggregationMooncakeAARCH64Accuracy(PDDisaggregationServerBase):
    @classmethod
    def setUpClass(cls):
        ...
        # 非阻塞启动 prefill 与 decode 两个服务器,
        # 它们必须使用不同的 NCCL 端口,否则 rendezvous 相互干扰。
        cls.start_prefill()
        cls.start_decode()
        ...
​
    @classmethod
    def start_prefill(cls):
        prefill_args = [
            "--trust-remote-code",
            "--disaggregation-mode", "prefill",
            "--disaggregation-bootstrap-port", cls.bootstrap_port,
            "--tp", "2",
            # prefill 服务器占用基准端口。
            "--nccl-port", str(NCCL_PORT_BASE),
        ]
        ...
​
    @classmethod
    def start_decode(cls):
        decode_args = [
            "--trust-remote-code",
            "--disaggregation-mode", "decode",
            "--disaggregation-bootstrap-port", cls.bootstrap_port,
            "--tp", "2",
            "--base-gpu-id", "2",
            # decode 服务器占用基准端口 + 1,避开 prefill。
            "--nccl-port", str(NCCL_PORT_BASE + 1),
        ]
        ...

评论区精华

GB300 测试重跑验证 测试

作者发起 `/rerun-test` 指定 `test_numa_utils.py`、`test_flashinfer_a2a.py`、`test_disaggregation_aarch64.py`、`test_deepseek_v3_cutedsl_4gpu.py`。第一次重跑(Run #30616149775)仍然失败,第二次重跑(Run #30618506277)全部通过。

结论:固定端口后测试稳定通过,修复有效。 · 已解决

风险与影响

改动仅涉及 test/ 下三个文件,无生产代码风险。潜在风险:

1) 端口固定后,若同一 runner 上并行运行其他未固定端口的测试或服务,仍可能撞端口,但端口选择低于 ephemeral 范围、且 runner 按作业隔离,冲突概率低;
2) 若 DEFAULT_PORT_FOR_SRT_TEST_RUNNER 常量未来调整超过 10240 边界,端口会进入临时端口区间,需要配套更新注释与验证;
3) 这些端口只覆盖当前七个启动,未来新增 GB300 测试需沿用同一约定。

对 GB300(4×GPU)PR 测试的稳定性有直接改善,消除了 DeepSeek CuteDSL、FlashInfer A2A、AArch64 分离部署测试的间歇性 EADDRINUSE 失败。对最终用户和运行时无影响;对团队的影响是 CI 基建约定:GB300 测试应显式分配 --nccl-port

仅测试变更 固定端口依赖常量边界 端口分配约定需随新增测试同步

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论