Skip to content

fix(slurm): resolve node IPs to non-loopback addresses - #179

Open
i-kosarev wants to merge 1 commit into
ROCm:developfrom
i-kosarev:fix/slurm-node-ip-loopback
Open

fix(slurm): resolve node IPs to non-loopback addresses#179
i-kosarev wants to merge 1 commit into
ROCm:developfrom
i-kosarev:fix/slurm-node-ip-loopback

Conversation

@i-kosarev

Copy link
Copy Markdown

Problem

The generated SLURM job script builds the peer list with:

SLURM_NODE_IPS=$(scontrol show hostname ${SLURM_JOB_NODELIST} | while read node; do
    getent hosts "$node" | awk '{print $1}'
done | ...)

On Ubuntu, /etc/hosts maps the machine's own hostname to 127.0.1.1, and
getent hosts returns that entry first. Since the loop runs on each node, every
node resolves itself to 127.0.1.1 while resolving its peers correctly — so
each node publishes a peer list with a loopback address in its own slot.

Anything that waits on all peers then hangs forever: no node listens on the
loopback address its peers were told to connect to. We hit this with the
sglang-disagg socket barrier, where two nodes sat in wait_for_all_ports() and
never launched their servers, which in turn collapsed the 32-rank rendezvous
(TCPStore ... shut down too early, client socket has timed out ... 5757).

The symptom is a multi-node run that hangs at bring-up with no error until the
SLURM time limit. It is load-dependent: the larger the allocation, the likelier
at least one node carries the /etc/hosts self-mapping, which makes it look
intermittent.

Fix

Resolve each node with getent ahostsv4, skipping 127.*, and fall back to the
primary non-loopback interface address for the local node.

Verification

  • Reproduced on 8-node runs: peer lists contained 127.0.1.1 in the local slot;
    after the fix every node resolves to a routable address and all nodes reach the
    barrier.
  • tests/unit/test_slurm_job_template.py — 21/21 pass.
  • The rendered script passes bash -n.

Made with Cursor

Copilot AI lite review requested due to automatic review settings September 1, 2026 13:48

Copilot AI 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.

Pull request overview

This PR updates the SLURM-generated SGLang-disaggregated job script to avoid advertising loopback (127.*) addresses in the per-node peer list, which can break multi-node rendezvous/barriers and cause silent hangs during bring-up.

Changes:

  • Switch node resolution from getent hosts to getent ahostsv4, selecting the first non-127.* IPv4.
  • Add a fallback that derives an address from local interfaces when name resolution yields only loopback output.
Suppressed comments (1)

src/madengine/deployment/slurm.py:964

  • The fallback (hostname -I ...) is applied for any node that fails to resolve via getent ahostsv4, but the comment says it should only be used for the local node. As written, an unresolvable peer hostname would silently get replaced with the current node’s IP, producing an incorrect peer list and hard-to-debug hangs. Also, because this uses a pipeline, exit 1 inside the while body wouldn’t reliably fail the overall command unless you avoid the pipeline or enable pipefail.

Consider restructuring the loop to avoid scontrol | while ... | tr | sed so you can reliably error out on unresolved non-local nodes, and only use the interface-address fallback when $node matches the local host.

SLURM_NODE_IPS=$(scontrol show hostname ${{SLURM_JOB_NODELIST}} | while read node; do
    node_ip=$(getent ahostsv4 "$node" | awk '$1 !~ /^127\\./ {{print $1; exit}}')
    if [ -z "$node_ip" ]; then
        node_ip=$(hostname -I | tr ' ' '\\n' | grep -vE '^127\\.' | head -n1)
    fi

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/madengine/deployment/slurm.py
Copilot AI review requested due to automatic review settings September 1, 2026 14:10

Copilot AI 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.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

Comment thread src/madengine/deployment/slurm.py Outdated

Copilot AI 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.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

Suppressed comments (1)

src/madengine/deployment/slurm.py:964

  • The fallback is applied to every unresolved hostname, not just the local node as the comment states. If a remote node has a transient NSS/DNS failure (or only an IPv6 record), each task inserts its own interface address into that remote slot, producing different/duplicate peer lists and recreating the barrier hang. Identify the local entry (for example via SLURM_NODEID/SLURMD_NODENAME) before using the local fallback, and fail the task if a remote entry—or the fallback itself—cannot be resolved; make sure that failure is not swallowed by the surrounding pipeline.
    if [ -z "$node_ip" ]; then
        node_ip=$(hostname -I | tr ' ' '\\n' | grep -vE '^127\\.' | head -n1)
    fi

Comment thread src/madengine/deployment/slurm.py Outdated
Copilot AI review requested due to automatic review settings September 1, 2026 14:25

Copilot AI 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.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

Comment thread src/madengine/deployment/slurm.py Outdated
Comment thread src/madengine/execution/container_runner.py Outdated
Copilot AI review requested due to automatic review settings September 1, 2026 14:52

Copilot AI 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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

Comment thread tests/unit/test_slurm_job_template.py Outdated
Comment thread tests/unit/test_slurm_job_template.py
Copilot AI review requested due to automatic review settings September 1, 2026 15:37
@i-kosarev
i-kosarev force-pushed the fix/slurm-node-ip-loopback branch from 422cd1d to 2b0068b Compare September 1, 2026 15:37

Copilot AI 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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

Comment thread src/madengine/deployment/slurm.py Outdated
@i-kosarev
i-kosarev force-pushed the fix/slurm-node-ip-loopback branch from 2b0068b to a631246 Compare September 1, 2026 17:35
Copilot AI review requested due to automatic review settings September 1, 2026 17:35

Copilot AI 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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

Copilot AI review requested due to automatic review settings September 2, 2026 09:53
@i-kosarev
i-kosarev force-pushed the fix/slurm-node-ip-loopback branch from a631246 to cdc0578 Compare September 2, 2026 09:53

Copilot AI 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.

🟢 Approval recommended

The fix is focused and covers the critical resolution and container handoff paths with regression tests.

Review details
  • Files reviewed: 4/4 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

coketaste
coketaste previously approved these changes Sep 8, 2026

@coketaste coketaste left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM

Copilot AI review requested due to automatic review settings September 8, 2026 19:49

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings September 9, 2026 11:36
@i-kosarev
i-kosarev force-pushed the fix/slurm-node-ip-loopback branch from c4c9676 to b8c3642 Compare September 9, 2026 11:36

Copilot AI 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.

🟡 Changes recommended

A failed node enumeration can still produce an empty peer list and bypass the new fail-fast guard.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 4/4 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment thread src/madengine/deployment/slurm.py
…o the container

The generated job script built SGLANG_NODE_IPS with `getent hosts "$node"`. On
Ubuntu, /etc/hosts maps the machine's own hostname to 127.0.1.1 and `getent
hosts` returns that entry first, so each node resolved *itself* to loopback
while resolving its peers correctly. Every node then published a peer list with
a loopback address in its own slot, and anything waiting on all peers hung
forever: nobody listens on the address its peers were told to connect to.

We hit this with the sglang-disagg socket barrier — two nodes sat in
wait_for_all_ports() and never launched their servers, which collapsed the
32-rank rendezvous (`TCPStore ... shut down too early`, `client socket has timed
out ... 5757`). The run then hangs at bring-up with no error until the SLURM
time limit, and looks intermittent because it only bites when an allocated node
carries the /etc/hosts self-mapping.

Changes:

- Resolve each node with `getent ahostsv4`, ignoring 127.* entries.
- Only the local node may fall back to its own address; doing that for a peer
  would publish this node's IP in the peer's slot, which is worse than having no
  entry. A node that still has no routable address aborts the job and names the
  offending hosts.
- Identify the local node by SLURMD_NODENAME, falling back to the hostnames.
  The list expands to NodeName values, which differ from the machine's hostname
  wherever the config sets NodeHostname; matching on hostname alone would treat
  the local loopback-only entry as an unresolvable peer and abort a job that has
  a valid address to advertise.
- Take the local address from the configured cluster interface
  (NCCL_SOCKET_IFNAME, first entry of a comma list), else from the source
  address the kernel would use for outbound traffic. Not `hostname -I`, which
  lists every interface in unspecified order and is not IPv4-only, so it can
  hand back a docker bridge or management address.
- Forward the generated SGLANG_* variables into the container. sglang-disagg
  takes the normal Docker path and ContainerRunner only copies an allowlist,
  which contained none of them — so the corrected peer list stopped at the job
  script and run.sh fell back to xP=1/yD=1 with IPADDRS=localhost. The
  allowlist moved to the module-level SLURM_PASSTHROUGH_ENV_VARS with a small
  _merge_slurm_env_from_shell() helper so it can be tested; no behaviour change.

Tests execute the rendered snippet against stubbed ip/getent/hostname/scontrol
on a node holding a docker bridge (172.17.0.1), a management address
(192.168.1.5) and the cluster interface (10.0.0.2): a resolvable peer keeps its
own address, the loopback-mapped local node advertises the cluster interface and
is still recognised when its NodeName differs from its hostname, a loopback-only
*peer* does not inherit our address, and an unresolvable peer makes the script
exit non-zero naming that host. The container handoff is covered separately,
both for the allowlist contents and for the copy into docker_env_vars.

tests/unit: 593 passed. The two failures in test_database_mongodb.py and
test_validators.py reproduce unchanged on upstream/develop (the sandbox raises
PermissionError where those tests expect FileNotFoundError).

Co-authored-by: Cursor <cursoragent@cursor.com>
Copilot AI review requested due to automatic review settings September 9, 2026 12:43
@i-kosarev
i-kosarev force-pushed the fix/slurm-node-ip-loopback branch from b8c3642 to 4de6d9b Compare September 9, 2026 12:43

Copilot AI 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.

🟢 Approval recommended

The implementation addresses the failure modes with focused end-to-end shell and environment handoff coverage.

Review details
  • Files reviewed: 4/4 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

@i-kosarev
i-kosarev requested a review from coketaste September 9, 2026 14:33
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.

3 participants