Skip to content

fix(ci): fix 2-gpu runner for RDMA - #1705

Merged
XinyueZhang369 merged 3 commits into
mainfrom
xz/pod-host-network
Jun 13, 2026
Merged

XinyueZhang369 merged 3 commits into
mainfrom
xz/pod-host-network

Conversation

@XinyueZhang369

@XinyueZhang369 XinyueZhang369 commented Jun 12, 2026 •

Copy link
Copy Markdown
Collaborator

Description

Problem

PD KV transfer (NIXL for vLLM, Mooncake for sglang) moves the KV cache over RDMA on the host mlx5 NICs. On the legacy runners the pod ran in an overlay-CNI network namespace, where the shared host NICs show PORT_ACTIVE but expose
an all-zero GID table inside the pod — so RDMA init died at worker startup (Failed to query GID … No data available, UCX Address not valid). The summerwind RunnerDeployment CRD can't express hostNetwork, so the fix required
migrating this tier to the gha-runner-scale-set, whose template.spec is a full PodSpec.

Solution

Moves the 2-gpu-h100 runner — the only tier that runs PD disaggregation e2e tests — from the legacy summerwind RunnerDeployment to the GitHub gha-runner-scale-set, configured so PD KV transfer works over real RoCE RDMA. This fixes
the frequent e2e-2gpu-pd flakiness.

Changes

  • hostNetwork: true + dnsPolicy: ClusterFirstWithHostNet — pod runs in the host netns where the RoCE GID tables are populated.
  • privileged runner container — the runner runs as non-root (uid 1001), so capabilities.add: [IPC_LOCK] only reaches the bounding set (not effective), and the default 64 KiB RLIMIT_MEMLOCK blocks RDMA completion-queue allocation
    (ibv_create_cq → ENOMEM, hit by sglang/Mooncake). privileged makes IPC_LOCK effective and lifts the memlock cap. TODO: replace with node containerd default_ulimits memlock=-1 and drop privileged.
  • podAntiAffinity (one PD runner per node) — hostNetwork shares the host port space.
  • Env parity with the legacy runner — HF_HOME/HF_TOKEN (gated Llama download), OPENAI/ANTHROPIC/XAI_API_KEY, NODE_IP; resources cpu 32 / mem 128Gi.
  • dind DOCKER_TLS_CERTDIR="" — keeps dockerd on the unix socket (no stray :2376 host listener under hostNetwork).
  • Runner image v0.0.2 → v0.0.3 (actions/runner 2.335.1; 2.332.0 is deprecated and rejected by GitHub with a 403, causing a runner recreate loop).
  • minRunners: 2 / maxRunners: 8.

Test Plan

Run 27438486706: all three e2e-2gpu-pd variants pass over real RDMA — vllm (NIXL), vllm-mooncake, and sglang (Mooncake). (The run's other failures — unit-tests, openai-responses — are unrelated pre-existing flakes.)

Checklist
  • cargo +nightly fmt passes
  • cargo clippy --all-targets --all-features -- -D warnings passes
  • (Optional) Documentation updated
  • (Optional) Please join us on Slack #sig-smg to discuss, review, and merge PRs

Summary by CodeRabbit

  • Chores
    • Disabled GPU runner autoscaler for the H100 deployment
    • Increased CPU and memory allocations for runner containers
    • Bumped runner and init images to v0.0.3
    • Enabled host networking and improved pod scheduling to avoid host-port conflicts
    • Added management of API credentials for runner environments and adjusted sidecar runtime settings

@coderabbitai

coderabbitai Bot commented Jun 12, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Disable the H100 autoscaler by zeroing replica bounds in the autoscaler manifest and update the 2-gpu-h100 Helm values: scale bounds, hostNetwork/podAntiAffinity, runner image/security/env, increased resources, init image, and dind sidecar TLS and resource settings.

Changes

H100 GPU Runner Configuration

Layer / File(s) Summary
Disable H100 autoscaler
scripts/k8s-runner-resources/arc-runner-autoscaler.yaml
HorizontalRunnerAutoscaler for arc-runner-2-h100-autoscaler has minReplicas and maxReplicas set to 0.
Runner scale set autoscaling bounds
scripts/k8s-runner-resources/runner-values-2-gpu-h100.yaml
Update runnerScaleSet minRunners/maxRunners.
Pod scheduling and networking configuration
scripts/k8s-runner-resources/runner-values-2-gpu-h100.yaml
Enable hostNetwork + dnsPolicy: ClusterFirstWithHostNet and replace previous affinity with podAntiAffinity targeting actions.github.com/scale-set-name: 2-gpu-h100.
Runner container image, security, and environment configuration
scripts/k8s-runner-resources/runner-values-2-gpu-h100.yaml
Bump runner image to action-runner:v0.0.3, set securityContext.privileged: true, add HF_HOME and API-key env vars from Secrets, and raise CPU/memory requests.
Init and dind sidecar container updates
scripts/k8s-runner-resources/runner-values-2-gpu-h100.yaml
Update init-dind-externals image, set DOCKER_TLS_CERTDIR: "" for dind, and configure dind resource requests/limits.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • lightseekorg/smg#659: Earlier changes to runner-values-2-gpu-h100.yaml that introduce the scale-set Helm values this PR further modifies.
  • lightseekorg/smg#671: Related adjustments to runner container resource requests for the 2-gpu-h100 scale set.
  • lightseekorg/smg#797: Other edits to arc-runner-autoscaler.yaml affecting H100 autoscaler bounds.

Suggested labels

ci, tests

Suggested reviewers

  • CatherineSue
  • key4ng
  • slin1237
  • gongwei-130

Poem

🐰 In fields of YAML the runners lie,
I set their bounds to zero—oh my!
Anti-affinity scatters their feet,
Privileged boots make them complete.
Secrets snug, resources grown, hop on, compile, and fly!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(ci): fix 2-gpu runner for RDMA' directly corresponds to the main change: enabling RDMA support for the 2-gpu-h100 runner by moving it to host network namespace with appropriate configuration.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch xz/pod-host-network

Comment @coderabbitai help to get the list of available commands and usage tips.

@XinyueZhang369 XinyueZhang369 changed the title fix(ci) fix 2-gpu runner for RDMA fix(ci): fix 2-gpu runner for RDMA Jun 12, 2026
Comment on lines 44 to 46
affinity:
podAffinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Nit: preferredDuringSchedulingIgnoredDuringExecution is a soft constraint — the scheduler can still co-locate two 2-gpu runners on the same node when eligible nodes are scarce. With hostNetwork: true and maxRunners: 8, if the BM.GPU.H100.8 pool has fewer than 8 nodes available, co-scheduling will happen and the host-port collisions the comment warns about become real.

Consider using requiredDuringSchedulingIgnoredDuringExecution instead to make the one-runner-per-node guarantee hard, so the scheduler fails loudly (pod stays Pending) rather than silently causing port conflicts at runtime.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the Kubernetes runner configuration for the 2-GPU H100 tier, enabling host networking, configuring pod anti-affinity to prevent port collisions, upgrading the runner image, and adding several API keys and environment variables. It also scales down the legacy autoscaler to zero. Feedback suggests completely removing the obsolete legacy deployment and autoscaler configurations rather than just scaling them to zero. Additionally, the resource limits applied to the dind container (2 CPUs and 4Gi memory) may throttle heavy Docker-based CI workloads, so increasing or removing these limits is recommended.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +204 to +210
resources:
limits:
cpu: '2'
memory: 4Gi
requests:
cpu: '1'
memory: 2Gi

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The dind container is configured with a memory limit of 4Gi and CPU limit of 2.

In a GitHub Actions runner using Docker-in-Docker (DinD), any workflow steps that run inside Docker containers (e.g., docker run ..., docker build ..., or action steps using docker images) are executed by the dockerd daemon inside this dind container.

Because of this, those steps will be constrained by the dind container's resource limits (2 CPUs and 4Gi memory), rather than the runner container's larger allocation (32 CPUs and 128Gi memory). If your CI workflows run heavy ML workloads or large docker builds inside Docker, they may experience severe CPU throttling or OOM kills.

Consider increasing these limits or removing them to allow dind to utilize the pod's full resource capacity when needed.

Additionally, when using a Docker-in-Docker (DinD) setup, it is necessary to disable TLS for the Docker socket by setting the DOCKER_TLS_CERTDIR environment variable to an empty string.

References
  1. When using a Docker-in-Docker (DinD) setup, it is necessary to disable TLS for the Docker socket by setting the DOCKER_TLS_CERTDIR environment variable to an empty string.

Comment on lines +83 to +84
minReplicas: 0
maxReplicas: 0

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Since the 2-gpu-h100 runner tier has been migrated to the new gha-runner-scale-set (using runner-values-2-gpu-h100.yaml), the legacy summerwind RunnerDeployment (arc-runner-2-gpu-h100 in arc-runner-gpu.yaml) and this HorizontalRunnerAutoscaler are now obsolete.

Instead of just scaling them to 0, consider completely removing the arc-runner-2-gpu-h100 deployment and autoscaler blocks from the codebase to clean up dead configuration and prevent any accidental scale-ups in the future.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean, well-documented infrastructure change. The RDMA/hostNetwork rationale is sound, comments explain the 'why' clearly, and the migration from summerwind to gha-runner-scale-set is correctly wired (legacy autoscaler zeroed out, image version bumped consistently). One soft nit on the anti-affinity constraint strength — see inline comment.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6d71e9aae4

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +45 to 46
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Enforce one host-network runner per node

This new anti-affinity is only a scheduler preference, so when the scale set grows beyond the number of readily available H100 nodes (or the scheduler prioritizes satisfying the 2-GPU request), Kubernetes may still place multiple hostNetwork 2-GPU runners on the same host. In that case their DinD/PD jobs share the node network namespace and can hit the host-port collisions this block is intended to prevent; use required anti-affinity or otherwise partition the host ports if co-location must be impossible.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
scripts/k8s-runner-resources/runner-values-2-gpu-h100.yaml (1)

41-55: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

preferredDuringSchedulingIgnoredDuringExecution does not enforce the one-runner-per-node guarantee.

The comment says these PD runners must be one-per-node to avoid host-port collisions, but preferredDuringSchedulingIgnoredDuringExecution is only a soft preference; Kubernetes may still place multiple matching pods on the same hostname when it needs to. That would bring back the same nondeterministic host-network collisions this change is trying to remove. (kubernetes.io)

Suggested change
     affinity:
       podAntiAffinity:
-        preferredDuringSchedulingIgnoredDuringExecution:
-          - weight: 100
-            podAffinityTerm:
-              labelSelector:
-                matchExpressions:
-                  - key: actions.github.com/scale-set-name
-                    operator: In
-                    values:
-                      - 2-gpu-h100
-              topologyKey: kubernetes.io/hostname
+        requiredDuringSchedulingIgnoredDuringExecution:
+          - labelSelector:
+              matchExpressions:
+                - key: actions.github.com/scale-set-name
+                  operator: In
+                  values:
+                    - 2-gpu-h100
+            topologyKey: kubernetes.io/hostname
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/k8s-runner-resources/runner-values-2-gpu-h100.yaml` around lines 41 -
55, Replace the soft pod anti-affinity rule with a hard one so scheduling
enforces one 2-gpu-h100 runner per node: under affinity -> podAntiAffinity,
change preferredDuringSchedulingIgnoredDuringExecution to
requiredDuringSchedulingIgnoredDuringExecution and move the existing
podAffinityTerm (with labelSelector matchExpressions key
actions.github.com/scale-set-name operator In values [2-gpu-h100] and
topologyKey kubernetes.io/hostname) into that required list so Kubernetes will
refuse to place multiple matching pods on the same node.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@scripts/k8s-runner-resources/runner-values-2-gpu-h100.yaml`:
- Around line 41-55: Replace the soft pod anti-affinity rule with a hard one so
scheduling enforces one 2-gpu-h100 runner per node: under affinity ->
podAntiAffinity, change preferredDuringSchedulingIgnoredDuringExecution to
requiredDuringSchedulingIgnoredDuringExecution and move the existing
podAffinityTerm (with labelSelector matchExpressions key
actions.github.com/scale-set-name operator In values [2-gpu-h100] and
topologyKey kubernetes.io/hostname) into that required list so Kubernetes will
refuse to place multiple matching pods on the same node.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: fedd118a-f52d-4ec2-b0a8-2ceedb7383fd

📥 Commits

Reviewing files that changed from the base of the PR and between 50ce61f and 6d71e9a.

📒 Files selected for processing (2)
  • scripts/k8s-runner-resources/arc-runner-autoscaler.yaml
  • scripts/k8s-runner-resources/runner-values-2-gpu-h100.yaml

@mergify

mergify Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Hi @XinyueZhang369, this PR has merge conflicts that must be resolved before it can be merged. Please rebase your branch:

git fetch origin main
git rebase origin/main
# resolve any conflicts, then:
git push --force-with-lease

@mergify mergify Bot added the needs-rebase PR has merge conflicts that need to be resolved label Jun 12, 2026
Signed-off-by: XinyueZhang369 <zoeyzhang369@gmail.com>
Signed-off-by: XinyueZhang369 <zoeyzhang369@gmail.com>
Signed-off-by: XinyueZhang369 <zoeyzhang369@gmail.com>
@mergify mergify Bot removed the needs-rebase PR has merge conflicts that need to be resolved label Jun 12, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
scripts/k8s-runner-resources/runner-values-2-gpu-h100.yaml (1)

45-55: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Use hard anti-affinity here, not a preference.

preferredDuringSchedulingIgnoredDuringExecution does not guarantee one 2-gpu-h100 runner per node. When the H100 pool is constrained, the scheduler can still place two host-network pods on the same host and reintroduce the host-port collisions this PR is trying to eliminate. Switch this to requiredDuringSchedulingIgnoredDuringExecution so capacity shortages show up as Pending pods instead of flaky PD jobs.

Suggested change
     affinity:
       podAntiAffinity:
-        preferredDuringSchedulingIgnoredDuringExecution:
-          - weight: 100
-            podAffinityTerm:
-              labelSelector:
-                matchExpressions:
-                  - key: actions.github.com/scale-set-name
-                    operator: In
-                    values:
-                      - 2-gpu-h100
-              topologyKey: kubernetes.io/hostname
+        requiredDuringSchedulingIgnoredDuringExecution:
+          - labelSelector:
+              matchExpressions:
+                - key: actions.github.com/scale-set-name
+                  operator: In
+                  values:
+                    - 2-gpu-h100
+            topologyKey: kubernetes.io/hostname
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/k8s-runner-resources/runner-values-2-gpu-h100.yaml` around lines 45 -
55, The current podAntiAffinity uses
preferredDuringSchedulingIgnoredDuringExecution which is only a soft preference;
change it to requiredDuringSchedulingIgnoredDuringExecution so the scheduler
enforces one 2-gpu-h100 runner per node. Locate the podAntiAffinity block
containing preferredDuringSchedulingIgnoredDuringExecution and replace that
top-level key with requiredDuringSchedulingIgnoredDuringExecution while keeping
the existing podAffinityTerm, labelSelector (matchExpressions with key
actions.github.com/scale-set-name and value 2-gpu-h100), and topologyKey
kubernetes.io/hostname unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/k8s-runner-resources/runner-values-2-gpu-h100.yaml`:
- Around line 79-85: The runner container is marked privileged
(securityContext.privileged: true) which grants node-level access; remove
privileged: true from the runner's securityContext and instead confine elevated
privileges to a narrow helper: add a privileged initContainer or privileged
sidecar (e.g., the existing dind helper) that performs the RDMA/memlock setup,
or grant only the IPC_LOCK capability via securityContext.capabilities.add on
that helper container and raise RLIMIT_MEMLOCK there; update references to the
runner pod spec (securityContext) and any init/sidecar container definitions so
only the helper has privileged or IPC_LOCK rights while the main runner remains
non-privileged.

---

Outside diff comments:
In `@scripts/k8s-runner-resources/runner-values-2-gpu-h100.yaml`:
- Around line 45-55: The current podAntiAffinity uses
preferredDuringSchedulingIgnoredDuringExecution which is only a soft preference;
change it to requiredDuringSchedulingIgnoredDuringExecution so the scheduler
enforces one 2-gpu-h100 runner per node. Locate the podAntiAffinity block
containing preferredDuringSchedulingIgnoredDuringExecution and replace that
top-level key with requiredDuringSchedulingIgnoredDuringExecution while keeping
the existing podAffinityTerm, labelSelector (matchExpressions with key
actions.github.com/scale-set-name and value 2-gpu-h100), and topologyKey
kubernetes.io/hostname unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: da278327-d536-4fa4-b7ca-af26155afc3d

📥 Commits

Reviewing files that changed from the base of the PR and between 6d71e9a and 8b2b1b0.

📒 Files selected for processing (2)
  • scripts/k8s-runner-resources/arc-runner-autoscaler.yaml
  • scripts/k8s-runner-resources/runner-values-2-gpu-h100.yaml

Comment on lines +79 to +85
securityContext:
# The runner runs as non-root (uid 1001), so capabilities.add:
# [IPC_LOCK] only reaches the bounding set (not effective) and the
# default 64KiB RLIMIT_MEMLOCK blocks RDMA completion-queue
# allocation (ibv_create_cq -> ENOMEM, e.g. sglang/Mooncake).
# privileged makes IPC_LOCK effective and lifts the memlock cap.
privileged: true

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

Avoid making the runner container itself privileged.

This is broader than the existing privileged dind sidecar: every workflow step scheduled onto this scale set now runs inside a privileged, host-network container, which is effectively node-level access. If the real requirement is memlock/RDMA setup, keep that privilege in the narrowest possible helper/runtime path or otherwise treat this label as trusted-only; leaving the main runner privileged materially weakens the runner isolation model.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/k8s-runner-resources/runner-values-2-gpu-h100.yaml` around lines 79 -
85, The runner container is marked privileged (securityContext.privileged: true)
which grants node-level access; remove privileged: true from the runner's
securityContext and instead confine elevated privileges to a narrow helper: add
a privileged initContainer or privileged sidecar (e.g., the existing dind
helper) that performs the RDMA/memlock setup, or grant only the IPC_LOCK
capability via securityContext.capabilities.add on that helper container and
raise RLIMIT_MEMLOCK there; update references to the runner pod spec
(securityContext) and any init/sidecar container definitions so only the helper
has privileged or IPC_LOCK rights while the main runner remains non-privileged.

@XinyueZhang369
XinyueZhang369 merged commit 4ada957 into main Jun 13, 2026
54 of 56 checks passed
@XinyueZhang369
XinyueZhang369 deleted the xz/pod-host-network branch June 13, 2026 00:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant