먼저 알아둘 결론
두 DGX Spark는 승인된 QSFP/CX7 케이블 한 개로 직접 연결할 수 있습니다. 한 물리 케이블이 두 개의 논리 netdev로 보이며, NVIDIA는 케이블 하나만으로 200GbE 전체 대역폭을 낼 수 있다고 안내합니다. 이 가이드에서는 두 논리 경로를 동시에 측정하되 Linux bond로 묶지 않습니다.
01 · GB10 이해
DGX Spark와 GB10
DGX Spark는 Grace CPU와 Blackwell GPU를 결합한 GB10 Superchip, 128GB 일관된 통합 메모리, ConnectX-7 NIC를 탑재합니다. 단일 장비는 최대 200B급 모델을 대상으로 하며, NVIDIA는 두 장비 연결 시 최대 405B 모델 구성을 안내합니다.
| 항목 | 공식 사양 | 이 가이드에서의 의미 |
|---|---|---|
| 메모리 | 128GB LPDDR5x UMA · 273GB/s | CPU와 GPU가 같은 시스템 메모리를 사용하므로 일반 GPU의 VRAM 표와 다르게 봅니다. |
| AI 연산 | FP4 sparsity 기준 최대 1PFLOP | 이론 최고값이며 실제 token/s가 아닙니다. |
| 네트워크 | ConnectX-7 · 200Gbps · 후면 QSFP 포트 2개 | 두 Spark 사이의 모델 분산 통신 경로입니다. |
02 · QSFP 케이블 구입
QSFP28(100Gbps)·QSFP56(200Gbps)·QSFP112(400Gbps) 중 하나를 구입하세요
DGX Spark의 ConnectX-7 포트에는 QSFP28(100Gbps), QSFP56(200Gbps), QSFP112(400Gbps) 규격 중 하나를 사용합니다. 세 규격은 세대와 속도 등급이 다르지만 Spark 후면 QSFP/CX7 포트에 맞는 classic QSFP 계열입니다. GB10 포트는 200Gb/s로 제한되므로 QSFP112(400Gbps)의 초과 대역폭은 사용하지 못합니다.
구매 전 판매자에게 확인할 네 가지
- 규격이 QSFP28(100Gbps), QSFP56(200Gbps), QSFP112(400Gbps) 중 하나인가?
- 판매자가 Spark 후면 QSFP/CX7 포트에 맞는 QSFP 케이블이라고 확인해 주는가?
- 200GbE 이상 직접 연결 동작을 보증하고, 어댑터나 breakout 구성이 아닌 단일 케이블인가?
- DGX Spark의 ConnectX-7 직접 연결 호환 보증과 반품 조건을 제공하는가?
03 · QSFP 연결
같은 위치 연결을 권장하고, pull-tab(플라스틱 손잡이)은 위로 둡니다
- 두 장비의 후면에서 같은 위치의 ConnectX-7 포트를 고릅니다. 포트 자체는 서로 바꿔 쓸 수 있지만, NVIDIA playbook도 NCCL 테스트 혼선을 줄이기 위해 같은 physical port 사용을 권장합니다. 왼쪽은 왼쪽끼리·오른쪽은 오른쪽끼리 연결하면 이후 Path1/Path2 IP와 RDMA device 매핑도 양쪽에서 같은 순서로 기록하기 쉽습니다.
- 각 커넥터의 pull-tab(플라스틱 손잡이)이 장비 윗면을 향하게 둡니다.
- 포트와 수평을 맞춘 뒤 힘을 주지 말고 끝까지 밀어 넣습니다. 제대로 정렬되면 부드럽게 들어갑니다.
- 우리 케이블에서는 끝까지 들어갈 때 “딱” 하는 체결음이 났습니다. 소리보다 중요한 확인은 pull-tab(플라스틱 손잡이)을 건드리지 않은 채 커넥터 하우징을 아주 가볍게 당겼을 때 빠지지 않는 것입니다.
- 분리할 때만 pull-tab(플라스틱 손잡이)을 포트와 수평으로 곧게 당깁니다.
ibdev2netdev
ip -br link
for IFACE in $(ibdev2netdev | awk '$NF == "(Up)" { print $(NF-1) }'); do
echo "== $IFACE =="
ethtool "$IFACE" | grep -E 'Speed|Lanes|Duplex|Link detected'
done rocep1s0f0 port 1 ==> enp1s0f0np0 (Up)roceP2p1s0f0 port 1 ==> enP2p1s0f0np0 (Up)== enp1s0f0np0 ==Speed: 200000Mb/sDuplex: FullPort: Direct Attach CopperLink detected: yes== enP2p1s0f0np0 ==Speed: 200000Mb/sDuplex: FullPort: Direct Attach CopperLink detected: yesibdev2netdev에서 아무 인터페이스도 (Up)이 아니면 케이블을 다시 체결하고 두 장비를 정상 재부팅한 뒤 재확인합니다.
04 · 네트워크 설정
자동 IP를 먼저 확인하고, 필요할 때만 수동 /30을 넣습니다
NVIDIA Sync Cluster Assistant나 DHCP가 있는 QSFP 스위치 환경은 주소를 자동으로 만들 수 있습니다. Linux도 IPv6 link-local은 보통 자동으로 만들고, NetworkManager 프로필이 ipv4.method link-local이거나 fallback이면 169.254.x.y/16 IPv4 link-local을 자동으로 잡을 수 있습니다.
ibdev2netdev
for IFACE in $(ibdev2netdev | awk '$NF == "(Up)" { print $(NF-1) }'); do
echo "== $IFACE =="
ip -4 -br address show dev "$IFACE"
nmcli -f GENERAL.STATE,IP4.ADDRESS device show "$IFACE" | sed 's/^/ /'
done
ip -4 route == enp1s0f0np0 ==enp1s0f0np0 UP 169.254.72.31/16 GENERAL.STATE: 100 (connected) IP4.ADDRESS[1]: 169.254.72.31/16== enP2p1s0f0np0 ==enP2p1s0f0np0 UP 169.254.84.12/16 GENERAL.STATE: 100 (connected) IP4.ADDRESS[1]: 169.254.84.12/16169.254.0.0/16 dev enp1s0f0np0 proto kernel scope link src 169.254.72.31169.254.0.0/16 dev enP2p1s0f0np0 proto kernel scope link src 169.254.84.12이 가이드의 TCP·RoCE·RPC 명령은 두 경로의 알려진 IPv4 peer 주소를 필요로 합니다. 자동 주소가 없거나, 169.254/16 주소가 재부팅 때 바뀌거나, 두 논리 경로가 같은 route처럼 보여 peer를 고정하기 어렵거나, 기존 사내망·VPN·컨테이너 route와 겹치면 수동 주소를 넣습니다.
| 상태 | 판단 | 다음 단계 |
|---|---|---|
| NVIDIA Sync 또는 DHCP가 두 QSFP 경로에 안정적 IPv4를 부여 | 자동 사용 가능 | 그 IP를 qsfp-guide.env에 기록하고 수동 적용은 건너뜁니다. |
169.254.x.y/16만 있음 | 임시 통신은 가능하지만 peer·GID가 흔들릴 수 있음 | 재현 가능한 측정과 RPC에는 수동 /30을 권합니다. |
| IPv4 없음, route 충돌, ping 실패 | 수동 설정 필요 | 아래 기본값 또는 충돌 없는 private /30 두 개를 넣습니다. |
아래 기본값은 2026-07-23에 검증한 예시입니다. Path1은 10.42.0.0/30, Path2는 10.42.1.0/30입니다. 같은 대역이 이미 ip -4 route에 보이면 prompt에서 다른 private /30 두 개를 입력하세요.
ibdev2netdev
read -r -p "이 노드의 역할 (A/B): " NODE_ROLE
read -r -p "첫 번째 Up netdev (Path1): " QSFP_IF1
read -r -p "두 번째 Up netdev (Path2): " QSFP_IF2
case "$NODE_ROLE" in
A) DEFAULT_LOCAL_IP1=10.42.0.1; DEFAULT_PEER_IP1=10.42.0.2; DEFAULT_LOCAL_IP2=10.42.1.1; DEFAULT_PEER_IP2=10.42.1.2 ;;
B) DEFAULT_LOCAL_IP1=10.42.0.2; DEFAULT_PEER_IP1=10.42.0.1; DEFAULT_LOCAL_IP2=10.42.1.2; DEFAULT_PEER_IP2=10.42.1.1 ;;
*) echo "A 또는 B만 입력하세요." >&2; exit 1 ;;
esac
echo "기본값: Path1 10.42.0.0/30, Path2 10.42.1.0/30"
read -r -p "Path1 local IP [$DEFAULT_LOCAL_IP1]: " LOCAL_IP1
read -r -p "Path1 peer IP [$DEFAULT_PEER_IP1]: " PEER_IP1
read -r -p "Path2 local IP [$DEFAULT_LOCAL_IP2]: " LOCAL_IP2
read -r -p "Path2 peer IP [$DEFAULT_PEER_IP2]: " PEER_IP2
: "${LOCAL_IP1:=$DEFAULT_LOCAL_IP1}"
: "${PEER_IP1:=$DEFAULT_PEER_IP1}"
: "${LOCAL_IP2:=$DEFAULT_LOCAL_IP2}"
: "${PEER_IP2:=$DEFAULT_PEER_IP2}"
mkdir -p "$HOME/.config"
cat > "$HOME/.config/qsfp-guide.env" <<EOF
export NODE_ROLE="$NODE_ROLE"
export QSFP_IF1="$QSFP_IF1"
export QSFP_IF2="$QSFP_IF2"
export LOCAL_IP1="$LOCAL_IP1"
export LOCAL_IP2="$LOCAL_IP2"
export PEER_IP1="$PEER_IP1"
export PEER_IP2="$PEER_IP2"
EOF
chmod 600 "$HOME/.config/qsfp-guide.env"
cat "$HOME/.config/qsfp-guide.env" export NODE_ROLE="A"export QSFP_IF1="enp1s0f0np0"export QSFP_IF2="enP2p1s0f0np0"export LOCAL_IP1="10.42.0.1"export LOCAL_IP2="10.42.1.1"export PEER_IP1="10.42.0.2"export PEER_IP2="10.42.1.2"source "$HOME/.config/qsfp-guide.env"
ensure_profile() {
name="$1"; iface="$2"; address="$3"
if nmcli -t -f NAME connection show | grep -Fxq "$name"; then
sudo nmcli connection modify "$name" connection.interface-name "$iface" ipv4.method manual ipv4.addresses "$address/30" ipv4.gateway "" ipv4.dns "" ipv4.never-default yes ipv6.method link-local 802-3-ethernet.mtu 9000 connection.autoconnect yes
else
sudo nmcli connection add type ethernet ifname "$iface" con-name "$name" ipv4.method manual ipv4.addresses "$address/30" ipv4.gateway "" ipv4.dns "" ipv4.never-default yes ipv6.method link-local 802-3-ethernet.mtu 9000 connection.autoconnect yes
fi
sudo nmcli connection up "$name"
}
ensure_profile qsfp-primary "$QSFP_IF1" "$LOCAL_IP1"
ensure_profile qsfp-secondary "$QSFP_IF2" "$LOCAL_IP2" Connection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/12)Connection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/13)source "$HOME/.config/qsfp-guide.env"
ip -4 -br address show dev "$QSFP_IF1"
ip -4 -br address show dev "$QSFP_IF2"
ip route get "$PEER_IP1"
ip route get "$PEER_IP2"
ping -I "$QSFP_IF1" -M do -s 8972 -c 4 "$PEER_IP1"
ping -I "$QSFP_IF2" -M do -s 8972 -c 4 "$PEER_IP2" enp1s0f0np0 UP 10.42.0.1/30enP2p1s0f0np0 UP 10.42.1.1/3010.42.0.2 dev enp1s0f0np0 src 10.42.0.1 uid 100010.42.1.2 dev enP2p1s0f0np0 src 10.42.1.1 uid 10004 packets transmitted, 4 received, 0% packet loss, time 3001ms4 packets transmitted, 4 received, 0% packet loss, time 3001ms직접 연결에는 default gateway와 DNS를 넣지 않습니다. 두 logical netdev는 별도 /30에 두고 bond로 합치지 않습니다.
05 · QSFP 속도 확인
두 경로를 동시에, A→B와 B→A를 따로 측정합니다
먼저 수신 노드에서 server 블록을 실행하고, 바로 이어 송신 노드에서 client 블록을 실행합니다. A→B가 끝나면 역할을 바꿔 다시 실행합니다. 두 client 로그의 [SUM] receiver 값을 더한 것이 한 방향의 TCP 합계입니다.
source "$HOME/.config/qsfp-guide.env"
PORT1=5201; PORT2=5202
iperf3 -s -B "$LOCAL_IP1" -p "$PORT1" --one-off > /tmp/qsfp-path1-recv.log &
iperf3 -s -B "$LOCAL_IP2" -p "$PORT2" --one-off > /tmp/qsfp-path2-recv.log &
wait
cat /tmp/qsfp-path1-recv.log /tmp/qsfp-path2-recv.log -----------------------------------------------------------Server listening on 5201 (test #1)----------------------------------------------------------------------------------------------------------------------Server listening on 5202 (test #1)-----------------------------------------------------------source "$HOME/.config/qsfp-guide.env"
PORT1=5201; PORT2=5202
iperf3 -c "$PEER_IP1" -B "$LOCAL_IP1" -p "$PORT1" -P 16 -t 20 --zerocopy -w 16M > /tmp/qsfp-path1-send.log &
iperf3 -c "$PEER_IP2" -B "$LOCAL_IP2" -p "$PORT2" -P 16 -t 20 --zerocopy -w 16M > /tmp/qsfp-path2-send.log &
wait
grep -E '\[SUM\].*receiver' /tmp/qsfp-path1-send.log /tmp/qsfp-path2-send.log /tmp/qsfp-path1-send.log:[SUM] 0.00-20.04 sec 220 GBytes 94.1 Gbits/sec receiver/tmp/qsfp-path2-send.log:[SUM] 0.00-20.04 sec 224 GBytes 95.6 Gbits/sec receiver반대 방향은 노드 A에서 receiver 블록, 노드 B에서 sender 블록을 실행합니다. 직전 server가 완전히 종료된 뒤 같은 port를 재사용하세요.
| 측정 | 결과 | 기준·조건 | 상태 |
|---|---|---|---|
| TCP · A → B | 189.73 Gb/s | 두 경로 receiver 합계 | 검증됨 |
| TCP · B → A | 192.15 Gb/s | 두 경로 receiver 합계 | 검증됨 |
| RoCE · A → B | 196.08 Gb/s | 두 경로 합계 | 검증됨 |
| RoCE · B → A | 196.11 Gb/s | 두 경로 합계 | 검증됨 |
TCP 통과 기준은 방향별 두 경로 합계 180Gb/s 이상, RoCE는 190Gb/s 이상입니다. 이 값은 line rate가 아니라 이 환경의 acceptance 기준입니다.
RoCE도 196Gb/s 수준인지 확인하기
ib_write_bw를 실행하기 전에 netdev와 RDMA device·port·RoCE v2 IPv4 GID index가 같은 경로인지 확인해야 합니다. GID index를 추측하지 마세요.
source "$HOME/.config/qsfp-guide.env"
ibdev2netdev
rdma link show
show_gids
# 같은 netdev의 RDMA device, port, RoCE v2 IPv4 GID index를 기록하세요. rocep1s0f0 port 1 ==> enp1s0f0np0 (Up)roceP2p1s0f0 port 1 ==> enP2p1s0f0np0 (Up)link rocep1s0f0/1 state ACTIVE physical_state LINK_UP netdev enp1s0f0np0link roceP2p1s0f0/1 state ACTIVE physical_state LINK_UP netdev enP2p1s0f0np0DEV PORT INDEX GID IPv4 VER DEVrocep1s0f0 1 3 ::ffff:10.42.0.1 10.42.0.1 v2 enp1s0f0np0ENV_FILE="$HOME/.config/qsfp-guide.env"
source "$ENV_FILE"
ibdev2netdev
rdma link show
show_gids
read -r -p "Path1 RDMA device: " RDMA_DEV1
read -r -p "Path1 RDMA port: " RDMA_PORT1
read -r -p "Path1 RoCE v2 IPv4 GID index: " GID_INDEX1
read -r -p "Path2 RDMA device: " RDMA_DEV2
read -r -p "Path2 RDMA port: " RDMA_PORT2
read -r -p "Path2 RoCE v2 IPv4 GID index: " GID_INDEX2
TMP_ENV="$(mktemp)"
grep -vE '^export (RDMA_DEV1|RDMA_PORT1|GID_INDEX1|RDMA_DEV2|RDMA_PORT2|GID_INDEX2)=' "$ENV_FILE" > "$TMP_ENV"
cat >> "$TMP_ENV" <<EOF
export RDMA_DEV1="$RDMA_DEV1"
export RDMA_PORT1="$RDMA_PORT1"
export GID_INDEX1="$GID_INDEX1"
export RDMA_DEV2="$RDMA_DEV2"
export RDMA_PORT2="$RDMA_PORT2"
export GID_INDEX2="$GID_INDEX2"
EOF
install -m 600 "$TMP_ENV" "$ENV_FILE"
rm -f "$TMP_ENV" export RDMA_DEV1="rocep1s0f0"export RDMA_PORT1="1"export GID_INDEX1="3"export RDMA_DEV2="roceP2p1s0f0"export RDMA_PORT2="1"export GID_INDEX2="3"A→B는 노드 B에서 receiver를 먼저 실행한 다음 노드 A에서 sender를 실행합니다. 두 경로의 BW average[Gb/sec]를 더해 190Gb/s 이상인지 확인합니다.
source "$HOME/.config/qsfp-guide.env"
PORT1=18515; PORT2=18516
ib_write_bw -d "$RDMA_DEV1" -i "$RDMA_PORT1" -x "$GID_INDEX1" -F --report_gbits -D 20 -q 8 -s 1048576 -p "$PORT1" > /tmp/roce-path1-recv.log &
ib_write_bw -d "$RDMA_DEV2" -i "$RDMA_PORT2" -x "$GID_INDEX2" -F --report_gbits -D 20 -q 8 -s 1048576 -p "$PORT2" > /tmp/roce-path2-recv.log &
wait
cat /tmp/roce-path1-recv.log /tmp/roce-path2-recv.log ************************************* Waiting for client to connect... ************************************************************************** Waiting for client to connect... *************************************source "$HOME/.config/qsfp-guide.env"
PORT1=18515; PORT2=18516
ib_write_bw -d "$RDMA_DEV1" -i "$RDMA_PORT1" -x "$GID_INDEX1" -F --report_gbits -D 20 -q 8 -s 1048576 -p "$PORT1" "$PEER_IP1" > /tmp/roce-path1-send.log &
ib_write_bw -d "$RDMA_DEV2" -i "$RDMA_PORT2" -x "$GID_INDEX2" -F --report_gbits -D 20 -q 8 -s 1048576 -p "$PORT2" "$PEER_IP2" > /tmp/roce-path2-send.log &
wait
cat /tmp/roce-path1-send.log /tmp/roce-path2-send.log #bytes #iterations BW peak[Gb/sec] BW average[Gb/sec]1048576 4890 99.20 98.04#bytes #iterations BW peak[Gb/sec] BW average[Gb/sec]1048576 4893 99.36 98.04B→A는 노드 A에서 receiver, 노드 B에서 sender를 같은 순서로 실행합니다. 직전 process가 종료된 뒤 port를 재사용하세요. 2026-07-23에는 A→B 196.08Gb/s, B→A 196.11Gb/s를 확인했습니다.
06 · 문제가 있다면
증상 아래 계층부터 확인합니다
| 증상 | 먼저 확인 | 조치 |
|---|---|---|
| netdev가 모두 Down | 같은 위치 포트, 탭 방향, 완전 체결 | 케이블 재장착 후 양쪽 정상 재부팅 |
| 200000Mb/s·2 lanes가 아님 | 케이블 호환성, 포트 모드, firmware | IP 설정 전에 물리 링크부터 해결 |
| Jumbo만 실패 | 양쪽 MTU 9000, route get 결과 | NetworkManager profile과 주소 충돌 수정 |
| TCP 한 방향만 느림 | 송신 노드, 같은 방향 RoCE, CRC·discard·AER | 느린 송신 노드의 NIC·PCIe 상태 조사 |
| RoCE만 느림 | RDMA device·port·GID 매핑 | 같은 netdev의 RoCE v2 IPv4 GID로 재측정 |
| 모델만 실패 | binary·model SHA, RPC0, 메모리 | 네트워크 튜닝이 아니라 runtime을 점검 |
source "$HOME/.config/qsfp-guide.env"
STAMP="$(date +%F-%H%M%S)"
OUT="$HOME/qsfp-evidence/$(hostname)-$STAMP"
mkdir -p "$OUT"
{
date -Is
uname -a
ip -br link
ip -4 -br address
ip -4 route
ibdev2netdev
rdma link show
nvidia-smi
} > "$OUT/inventory.txt" 2>&1
for IFACE in "$QSFP_IF1" "$QSFP_IF2"; do
ethtool "$IFACE" > "$OUT/ethtool-$IFACE.txt" 2>&1
ethtool -S "$IFACE" > "$OUT/ethtool-stats-$IFACE.txt" 2>&1
done
sudo lspci -tv > "$OUT/lspci-tree.txt" 2>&1
sudo lspci -vv > "$OUT/lspci-vv.txt" 2>&1
sudo journalctl -k -b --no-pager > "$OUT/kernel-journal.txt" 2>&1
printf 'Evidence saved: %s
' "$OUT"
find "$OUT" -maxdepth 1 -type f -printf '%f
' | LC_ALL=C sort Evidence saved: /home/donghyeon/qsfp-evidence/spark-a-2026-07-23-153000ethtool-enP2p1s0f0np0.txtethtool-enp1s0f0np0.txtethtool-stats-enP2p1s0f0np0.txtethtool-stats-enp1s0f0np0.txtinventory.txtkernel-journal.txtlspci-tree.txtlspci-vv.txt우리가 실제로 겪은 방향성 병목
우리가 겪은 병목은 한 방향 TCP·RoCE가 약 16–25Gb/s까지 떨어지는 형태였습니다. qdisc, pause frame, ring 설정으로는 해결되지 않았고, Linux kernel과 NVIDIA driver update를 먼저 시도했습니다.
이후 firmware update를 적용한 뒤 속도가 정상 범위로 돌아왔습니다. 업데이트 전에는 journal, ethtool, lspci 증거를 먼저 저장하고, update 뒤에는 링크·Jumbo·TCP·RoCE를 처음부터 다시 측정합니다.
07 · 병렬화 후 LLM 추론
llama-server는 검증값을 싣고, vLLM은 별도 검증 절로 둡니다
추론 절은 두 갈래입니다. 현재 재현 가능한 실습은 llama.cpp RPC와 llama-server로 K-EXAONE Q4_K_M을 1:1 layer split 실행하는 경로입니다. vLLM은 같은 네트워크 acceptance를 통과한 뒤 별도 명령·모델·메모리 한계를 검증해 추가해야 하며, 여기서는 아직 추천 절차로 확정하지 않습니다.
llama.cpp RPC + llama-server: 검증 완료
1. 두 노드에 같은 llama.cpp 빌드
git clone https://github.com/ggml-org/llama.cpp.git "$HOME/llama.cpp-b10088"
cd "$HOME/llama.cpp-b10088"
git checkout b10088
cmake -B build -DGGML_CUDA=ON -DGGML_RPC=ON -DCMAKE_BUILD_TYPE=Release
cmake --build build -j"$(nproc)"
sha256sum build/bin/ggml-rpc-server
build/bin/llama-server --version <sha256> build/bin/ggml-rpc-serverversion: b100882. 노드 A에 모델 받기
hf CLI 로그인과 저장소 이용 조건 확인이 먼저 필요합니다. 최소 150GiB의 여유 공간을 확보하세요.
MODEL_DIR="$HOME/models/K-EXAONE-236B-A23B-GGUF"
MODEL_FILE="$MODEL_DIR/K-EXAONE-236B-A23B-Q4_K_M.gguf"
mkdir -p "$MODEL_DIR"
hf download LGAI-EXAONE/K-EXAONE-236B-A23B-GGUF K-EXAONE-236B-A23B-Q4_K_M.gguf --local-dir "$MODEL_DIR" >/dev/null
sha256sum "$MODEL_FILE"
stat -c 'size %s bytes' "$MODEL_FILE" 83f744c3ee7ee7f19a80c7e0a73769f66b23db1831dddbbdea168637d3f868be /home/donghyeon/models/K-EXAONE-236B-A23B-GGUF/K-EXAONE-236B-A23B-Q4_K_M.ggufsize 143471722240 bytes3. 노드 B에서 RPC server 실행
source "$HOME/.config/qsfp-guide.env"
[ "$NODE_ROLE" = B ] || { echo "노드 B에서 실행하세요." >&2; exit 1; }
RPC_BIN="$HOME/llama.cpp-b10088/build/bin/ggml-rpc-server"
LD_LIBRARY_PATH="$(dirname "$RPC_BIN")" "$RPC_BIN" -H "$LOCAL_IP1" -p 50052 -d CUDA0 create_backend: using CUDA0Starting RPC server on 10.42.0.2:500524. 노드 A에서 두 device 확인
source "$HOME/.config/qsfp-guide.env"
[ "$NODE_ROLE" = A ] || { echo "노드 A에서 실행하세요." >&2; exit 1; }
LLAMA_SERVER="$HOME/llama.cpp-b10088/build/bin/llama-server"
"$LLAMA_SERVER" --rpc "$PEER_IP1:50052" --list-devices Available devices: CUDA0: NVIDIA GB10 RPC0: 10.42.0.2:500525. 모델을 1:1로 분할해 실행
source "$HOME/.config/qsfp-guide.env"
MODEL="$HOME/models/K-EXAONE-236B-A23B-GGUF/K-EXAONE-236B-A23B-Q4_K_M.gguf"
LLAMA_SERVER="$HOME/llama.cpp-b10088/build/bin/llama-server"
"$LLAMA_SERVER" --model "$MODEL" --rpc "$PEER_IP1:50052" --gpu-layers 999 --split-mode layer --tensor-split 1,1 --ctx-size 4096 --parallel 1 --batch-size 512 --ubatch-size 128 --threads 10 --threads-batch 10 --flash-attn on --jinja --host 127.0.0.1 --port 8088 llama_model_loader: loaded meta data with 64 key-value pairsllama_model_load: model loadedllama server listening at http://127.0.0.1:8088curl -sS http://127.0.0.1:8088/v1/chat/completions -H 'Content-Type: application/json' -d '{"model":"K-EXAONE","messages":[{"role":"user","content":"1+1의 답만 말하세요."}],"temperature":0,"max_tokens":16}' | python3 -c 'import json, sys; data=json.load(sys.stdin); content=data["choices"][0]["message"]["content"].strip(); print("assistant content: " + content); print("sanity: ok" if content == "2" else "sanity: unexpected")' assistant content: 2sanity: ok| 측정 | 결과 | 기준·조건 | 상태 |
|---|---|---|---|
| K-EXAONE 생성 | 약 13.7 token/s | Q4_K_M · layer split 1,1 | 검증됨 |
- 모델 SHA-256:
83f744c3…f868be - local allocation 67,463MiB · remote allocation 약 69.1GiB
- 프롬프트
1+1의 답만 말하세요.→ 응답2 - generation 약 13.7 token/s
vLLM: 작성 중
vLLM은 후보 runtime으로 남깁니다. 다만 이 문서의 검증 데이터는 llama.cpp RPC 기준이므로, vLLM 절은 K-EXAONE 또는 대체 공개 모델의 실제 2노드 실행, 메모리 사용량, token/s, 실패 조건을 측정한 뒤 명령을 고정합니다.
08 · 병렬화 후 학습
학습 방법은 탐색 방향만 남기고 작성 중으로 둡니다
QSFP/RDMA가 통과하면 NCCL 기반 분산 학습을 탐색할 수 있습니다. 하지만 이 프로젝트에서는 아직 GB10 두 대로 실제 학습을 재현하지 않았으므로 권장 절차처럼 쓰지 않습니다.
- 먼저 NCCL all-reduce, PyTorch DDP/FSDP, 작은 공개 모델과 합성 데이터로 통신 안정성을 확인합니다.
- 성능 숫자나 설정값은 학습 workload를 실제로 실행한 뒤에만 고정합니다.
09 · 측정값 해석
측정값을 해석하는 법
모든 수치는 2026-07-23의 DGX Spark 2대, kernel 6.17.0-1026-nvidia, MTU 9000 환경에서 얻었습니다. 다른 케이블·소프트웨어·주소·모델에서는 다시 측정해야 합니다. TCP/RoCE 처리량과 token/s는 서로 다른 측정값이므로 직접 비교하지 않습니다.
10 · 운영 원칙
끝까지 지켜야 할 세 가지
- 인터페이스 이름과 GID는 예시를 복사하지 말고 현재 장비에서 다시 찾습니다.
- 속도는 두 경로 합계와 두 방향을 각각 기록합니다.
- 문제가 생기면 update보다 먼저 현재 boot의 journal과 counter를 저장합니다.