DevOps/Study

AI Datacenter Network Study 3주차 — RDMA Write Deep Dive + Congestion Control(ECN·PFC·DCQCN), Load Balancing, AI Fabric Topology

juyeon22 2026. 7. 4. 19:50

AI Datacenter Network Study 3주차 — RDMA Write Deep Dive + Congestion Control(ECN·PFC·DCQCN), Load Balancing, AI Fabric Topology

AI Datacenter Network 스터디 3주차 정리입니다. 2주차가 "무손실 이더넷을 PFC·ECN·DCQCN으로 만든다"는 개념과 ROD·RUD 토폴로지에서 끝났다면, 이번 주는 그 아래로 내려갑니다. RDMA Write 한 번이 성립하기까지의 실제 절차, 임계값 네 개의 순서가 fabric의 성패를 가르는 이유, elephant flow를 나누는 세 가지 방법, 그리고 rail을 물리 스위치에 담는 방식까지 다룹니다.

1. 개요

이 글은 AI Datacenter Network 스터디 3주차 내용을 바탕으로, Toni Pasanen의 Deep Learning for Network Engineers 8~13장을 함께 읽고 정리한 기록입니다. 이 중 교재 8장의 병렬화 전략은 1주차에서 개요를 이미 다루었으므로 여기서는 재설명하지 않고, 각 전략이 이번 주에 다루는 fabric 장치들과 어떻게 연결되는지의 관점으로만 가져옵니다.

지난 두 주의 흐름을 짧게 되짚어 보겠습니다. 1주차에서는 병목이 연산이 아니라 메모리와 네트워크에 있으며, GPU 간 통신에는 TCP/IP 대신 RDMA가 필요하다는 결론에 이르렀습니다. 2주차에서는 그 RDMA를 회선에 올리는 InfiniBand와 RoCEv2를 비교했고, RoCEv2가 이더넷 위에서 무손실을 만들기 위해 PFC·ECN·DCQCN을 조합한다는 것, 그리고 수천 장의 GPU를 ROD·RUD 토폴로지로 배선한다는 것까지 확인했습니다.

다만 2주차까지의 이해에는 빈칸이 남아 있습니다. RDMA Write가 "상대 메모리 주소를 알고 바로 쓰는" 동작이라면, 아무나 아무 메모리에 쓸 수 있다는 뜻이 되어 버립니다. 당연히 그렇지 않습니다. 또한 "ECN을 먼저, PFC를 나중에"라는 원칙은 알았지만, 스위치에서 그 순서를 실제로 무엇이 결정하는지는 보지 않았습니다. 마지막으로, elephant flow 때문에 ECMP가 고르게 분산하지 못한다는 문제를 확인만 하고 해법은 다루지 않았습니다.

이번 주는 이 세 빈칸을 채우는 순서로 구성했습니다. 먼저 RDMA Write 한 번이 성립하기까지의 절차를 처음부터 끝까지 따라가고(2-4장), AI 트래픽이 fabric을 무너뜨리는 구체적인 방식을 확인한 뒤(5장), DCQCN을 개념에서 임계값과 스위치 설정 레벨로 내려 봅니다(6-8장). 이어서 elephant flow를 나누는 세 가지 방법을 비교하고(9-10장), 서버 내부의 NIC 구조부터 Multi-Pod까지 토폴로지를 확장한 뒤(11-12장), 마지막으로 교재 8장의 병렬화 전략이 이 장치들 중 무엇을 먼저 시험대에 올리는지 연결하며 마무리합니다(13장). 2주차와 겹치는 개념 설명은 반복하지 않고, 필요한 지점에서 연결만 하겠습니다.

2. RDMA는 주소만 알고 바로 쓰는 기술이 아니다 — 메모리 등록과 세 개의 키

RDMA Write의 첫인상은 위험해 보입니다. 원격 NIC가 상대 서버의 메모리에 CPU 개입 없이 직접 쓴다면, 잘못된 주소나 악의적인 쓰기를 무엇이 막아 주는가 하는 의문이 생깁니다. 답은 "쓰기 전에 해야 할 일이 많다"입니다. RDMA Write 한 번이 성립하기까지는 네 단계를 거칩니다. 메모리 등록, Queue Pair(QP) 생성, 연결 초기화, 그리고 Work Request 처리입니다. 이 장에서는 첫 단계인 메모리 등록을 봅니다.

시작은 Protection Domain(PD) 할당입니다. PD는 등록된 메모리 블록과 QP 같은 RDMA 객체들이 소속되는 격리 단위입니다. 네트워크 엔지니어에게는 VRF에 비유하면 이해가 빠릅니다. VRF가 IP 주소와 라우팅 테이블을 테넌트별 전용 공간에 가두듯, PD는 RDMA 객체들을 논리적 보호 영역에 가둡니다. 서로 다른 PD에 속한 객체는 섞이지 않습니다.

PD를 만든 뒤 물리 device memory에서 메모리 블록을 할당하고 RNIC(RDMA NIC)에 등록합니다. 등록 과정에서 블록 크기와 접근 권한을 정의합니다. 책의 예제에서는 데이터를 보내는 클라이언트 노드(CCN)의 메모리를 Local Read 권한으로, 데이터를 받는 서버 노드(SCN)의 메모리를 Remote Write 권한으로 등록합니다. 한 가지 흥미로운 점은, 할당된 물리 메모리가 연속적이지 않을 수 있다는 것입니다. 등록 과정이 이를 가상으로 연속된 블록처럼 보이게 만듭니다. 페이지가 흩어져 있어도 가상 주소 공간에서는 연속으로 보이는, OS 가상 메모리와 같은 발상입니다.

등록의 결과물이 키입니다. 등록된 메모리마다 로컬 접근 키인 L_Key가 생성되고, Remote Write 권한으로 등록된 SCN 메모리에는 원격 접근 키인 R_Key가 추가로 생성됩니다. 이 R_Key는 별도의 management connection을 통해 CCN에 전달되며, 이후 CCN이 RDMA Write를 요청할 때 이 키를 제시해야 SCN의 해당 메모리에 접근할 수 있습니다. R_Key는 원격 메모리에 대한 권한 토큰인 셈입니다. 토큰이 없거나 틀리면 쓰기는 성립하지 않습니다.

Key 의미 사용 위치
L_Key 로컬 메모리 접근 권한 로컬 RNIC가 자기 버퍼를 읽고 쓸 때
R_Key 원격 메모리 접근 권한 상대 RNIC가 내 메모리에 접근할 때
P_Key 파티션 멤버십(3장에서 다룸) 모든 RDMA 메시지에 포함되어 수신 측에서 검증

여기까지가 "어느 메모리에, 어떤 권한으로"의 준비입니다. 다음은 그 메모리를 오갈 통로, 즉 Queue Pair입니다.

3. QP 생성과 연결 초기화 — P_Key와 REQ·REP·RTU 3-way

2주차에서 QP를 소켓에 비유했습니다. 이번에는 그 QP가 만들어지고 연결 상태가 되기까지를 봅니다.

Work Queue(WQ)는 NIC와 device memory 사이의 가상 통신 채널로, send queue와 receive queue 두 종류가 있습니다. 이 두 큐의 쌍이 Queue Pair(QP) 입니다. 작업의 완료는 별도의 Completion Queue(CQ) 를 통해 애플리케이션에 통지됩니다. QP를 생성할 때는 몇 가지를 함께 결정합니다. 어느 PD에 소속시킬지, send/receive queue를 어느 CQ에 연결할지, 서비스 타입은 무엇으로 할지, 그리고 최대 Work Request 수와 최대 메시지 크기입니다. 예제의 서비스 타입은 Reliable Connection(RC)입니다. AI 학습의 gradient 동기화나 스토리지 트래픽처럼 신뢰성이 필요한 워크로드에서 RC가 주로 쓰입니다.

QP가 만들어졌다고 아무하고나 통신할 수 있는 것은 아닙니다. 통신하려는 두 NIC 포트가 같은 파티션에 속해야 합니다. 각 NIC 포트는 RDMA 도메인의 엔드포인트로서 Partition Key(P_Key) 테이블을 가집니다. 애플리케이션은 QP를 만든 뒤 사용할 포트의 테이블에서 P_Key를 조회하고, QP 상태를 INIT으로 바꾸면서 그 값을 설정합니다. 이후 송신 측은 모든 RDMA 메시지에 P_Key를 포함하고, 수신 측은 도착한 datagram의 P_Key가 대상 QP의 P_Key와 일치하는지 검증합니다. VXLAN의 VNI가 세그먼트를 식별하듯, P_Key는 QP 간 가상 연결의 세그먼트 식별자 역할을 합니다.

메모리와 QP가 준비되면 연결 초기화가 진행됩니다. CCN 애플리케이션이 SCN 애플리케이션에 REQ(Request for Communication) 를 보내는 것으로 시작합니다. REQ에는 NIC를 식별하는 CA GUID, 이 연결 요청을 식별해 뒤따르는 REP·RTU를 대응시키는 Local Communication ID, 로컬 QP 번호(예: 0x12345678), 서비스 타입(RC), 시작 PSN(Packet Sequence Number) (예: 0x000abc), P_Key(예: 0x8012), payload size가 담깁니다. SCN은 자신의 QP 번호와 PSN 등을 담은 REP 로 응답하고, CCN이 RTU(Ready to Use) 를 보내면 연결이 완료됩니다. 이 과정에서 QP 상태는 INIT에서 RTR(Ready to Receive)을 거쳐 RTS(Ready to Send)로 전환됩니다.

TCP 3-way handshake와 겹쳐 보이지만 교환하는 내용이 다릅니다. TCP가 시퀀스 번호와 윈도우를 합의한다면, RDMA 연결 초기화는 상대 QP 번호, 시작 PSN, 파티션 멤버십을 합의합니다. 여기서 합의된 PSN이 이후 패킷 순서 확인, ACK/NAK, 손실 감지의 기준이 됩니다. 2주차에서 본 go-back-N 재전송도 이 PSN 위에서 동작합니다.

4. RDMA Write 한 번의 실제 — Work Request, BTH, RETH, 그리고 수신 측 검증

연결이 완료되면 드디어 쓰기입니다. 애플리케이션은 Work Request(WR) 를 만들어 QP의 send queue에 게시합니다. RNIC에 넣는 작업 지시서라고 보면 되고, 내용은 다음과 같습니다.

Work Request Identifier   완료 통지 시 CQ에서 이 WR을 식별하는 포인터
OpCode                    작업 종류, 여기서는 RDMA Write
Local Buffer Address/Len  로컬 device memory의 시작 위치와 길이
L_Key                     로컬 버퍼 접근 키
Send Flag                 완료를 CQ로 통지할지 여부
Remote Buffer Address     SCN의 대상 메모리 위치
R_Key                     management connection으로 미리 받아 둔 원격 키

RNIC는 send queue에서 WR을 가져와 패킷을 만듭니다. 먼저 IB BTH(Base Transport Header) 를 구성하는데, 여기에 연결 초기화에서 합의한 P_Key와 목적지 QP 번호, PSN, OpCode가 들어가고, RC 서비스이므로 Ack Required가 Yes로 설정됩니다. RDMA Write에는 RETH(RDMA Extended Transport Header) 가 추가로 필요합니다. RETH가 원격 버퍼 주소, R_Key, 데이터 길이를 담으며, 수신 RNIC는 이 헤더를 보고 payload를 상대 메모리의 어느 위치에 쓸지 판단합니다. 최종 패킷은 2주차에서 본 그대로 Eth/IP/UDP(목적지 포트 4791)/BTH/RETH/Data로 캡슐화되어 fabric을 지나갑니다.

수신 측 처리가 이 절차의 핵심입니다. SCN의 RNIC는 패킷을 받으면 먼저 P_Key를 확인하고, 패킷의 R_Key가 자신이 management connection으로 CCN에 전달해 둔 값과 일치하는지 검증합니다. 검증을 통과해야 가상 device memory 주소를 물리 주소로 변환해 지정된 위치에 payload를 직접 씁니다. 쓰기가 끝나면 CQ를 통해 애플리케이션에 완료가 통지됩니다. 이 전 과정에서 SCN의 CPU는 payload 복사에 관여하지 않습니다. 1주차에서 "RDMA는 데이터 경로에서 CPU를 제거한다"고 했던 문장의 실체가 바로 이것입니다.

전체 흐름을 한 장으로 정리하면 다음과 같습니다.

sequenceDiagram
    participant CA as Client App
    participant CN as Client RNIC
    participant SN as Server RNIC
    participant SM as Server GPU Memory
    participant SA as Server App

    Note over CA,SA: 1단계 메모리 등록
    CA->>CN: 메모리 등록, L_Key 생성
    SA->>SN: 메모리 등록, R_Key 생성
    SA-->>CA: management connection으로 R_Key, 주소, 길이 전달
    Note over CA,SA: 2~3단계 QP 생성과 연결 초기화
    CA->>SA: REQ (QP No, PSN, P_Key)
    SA->>CA: REP
    CA->>SA: RTU
    Note over CA,SA: 4단계 RDMA Write
    CA->>CN: WR 게시 (L_Key, 원격 주소, R_Key)
    CN->>SN: Eth/IP/UDP 4791/BTH/RETH/Data
    SN->>SN: P_Key, R_Key 검증
    SN->>SM: 메모리에 직접 Write (CPU 미개입)
    SN-->>CN: ACK
    CN->>CA: CQ로 완료 통지

Figure 4.1 RDMA Write가 성립하기까지의 네 단계

정리하면 RDMA Write는 보호 도메인, 두 종류의 메모리 키, 파티션 키, 큐, 연결 상태, 패킷 헤더가 전부 맞아떨어져야 동작하는 절차입니다. "주소만 알면 쓴다"가 아니라 "권한과 상태를 전부 합의한 뒤에야 쓴다"에 가깝습니다. 그리고 이 잘 준비된 쓰기가 수천 개의 QP에서 동시에 터져 나올 때, fabric 쪽의 문제가 시작됩니다.

5. AI 트래픽이 fabric을 무너뜨리는 네 가지 방식

2주차에서 "한 장이 늦으면 집합 통신 전체가 멈추고 JCT가 늘어난다"는 결론까지 왔습니다. 이번에는 그 지연이 네트워크의 어디에서, 어떤 메커니즘으로 만들어지는지를 구체적인 시나리오로 봅니다. 책이 드는 예제는 rail-optimized 클러스터에서의 data parallel 학습, 즉 backward pass 이후 GPU들이 gradient를 교환하는 순간입니다.

첫째, egress interface congestion 입니다. Gradient 동기화는 전형적인 many-to-one 패턴을 만듭니다. 예제에서는 모든 호스트가 200Gbps 링크로 rail switch에 연결되어 있고, GPU들은 회선 속도로 전송할 수 있습니다. 여러 GPU가 동시에 Host-2의 GPU-0으로 gradient를 보내면, rail switch에서 Host-2 방향 인터페이스 하나로 egress 트래픽이 겹칩니다. GPU 네 개만 겹쳐도 4 x 200Gbps = 800Gbps입니다. 200G 파이프에 800G가 들어오는 셈이니 버퍼가 감당하지 못하면 드롭입니다. 링크가 200G라서 충분한 것이 아니라, collective 순간에 몇 개의 GPU가 같은 egress로 fan-in되는지가 용량 계산의 기준이라는 뜻입니다.

둘째, 단일 장애점 민감성 입니다. 학습은 장시간 반복 작업이고 통신 문제는 단일 GPU에만 영향을 줘도 전체 job을 세웁니다. Host, NIC, transceiver, cable의 장애는 GPU 하나를 고립시키지만, rail switch 하나의 장애는 그 rail에 연결된 모든 GPU를 한꺼번에 고립시킵니다. 장애 도메인의 크기가 곧 위험의 크기이며, 이 관점은 12장의 rail-per-switch 설계에서 다시 등장합니다.

셋째, head-of-line blocking 입니다. 2주차에서 본 HOL blocking은 PFC가 같은 priority의 무고한 flow까지 멈추는 현상이었습니다. 여기서 말하는 것은 다른 종류로, 서버 내부에서 발생합니다. NCCL은 토폴로지에 따라 GPU를 경유지로 씁니다. 예제에서는 GPU-0의 gradient를 NVLink로 GPU-1에 넘긴 뒤 GPU-1의 NIC로 내보내는 경로를 만드는데, 이때 GPU-1이 이미 자기 gradient를 전송 중이면 GPU-0의 트래픽은 같은 PCIe와 NIC 자원을 두고 그 뒤에서 대기합니다. 많은 GPU가 하나의 GPU나 NIC를 forwarding 지점으로 공유할수록 이 대기가 길어지고, iteration time이 늘어납니다.

넷째, ECMP hash polarization 입니다. 두 GPU가 QP를 열면 gradient 동기화 트래픽 전체가 그 QP로 흐르고, 네트워크 관점에서는 하나의 5-tuple flow입니다. Gradient는 모델 크기에 따라 수백 MB에서 GB 단위이므로 이 flow는 elephant flow가 됩니다. 예제에서는 두 개의 200Gbps flow가 모두 같은 spine 방향 uplink로 hash되어, 한쪽 링크는 400Gbps로 포화되고 다른 링크는 유휴 상태가 됩니다. 일반 DC에서는 flow가 많고 작아서 hash의 통계적 분산이 동작하지만, flow가 적고 큰 AI backend에서는 이 가정이 깨집니다. 2주차에서 한 문장으로 언급했던 "ECMP가 고르게 분산하지 못한다"의 실체입니다.

문제 발생 지점 메커니즘 결과
Egress congestion Rail switch egress Collective의 many-to-one fan-in Buffer overflow, drop
SPOF 민감성 Host~spine 전 구간 동기식 장기 job, rail 단위 고립 Job 지연·중단
HOL blocking 서버 내부 PCIe/NIC NCCL의 GPU 경유 forwarding Queueing delay, iteration 지연
Hash polarization Leaf-spine uplink QP당 하나의 elephant flow 링크 사용률 불균등

네 가지 중 앞의 것은 68장의 congestion control이, 마지막 것은 910장의 load balancing이, 그리고 장애 도메인은 11~12장의 topology가 각각 답을 맡습니다.

6. DCQCN, 개념에서 임계값으로 — xON < WRED Min < WRED Max < xOFF

2주차에서 DCQCN의 원칙을 이렇게 정리했습니다. ECN이 부드러운 브레이크, PFC가 급브레이크이며, ECN threshold를 PFC XOFF threshold보다 낮게 둔다는 것입니다. 이번에는 그 임계값이 실제로 몇 개이고, 각각 무엇을 트리거하며, 순서가 깨지면 무엇이 무너지는지를 봅니다.

먼저 ECN 쪽입니다. 스위치의 egress queue에는 WRED 기반 임계값 두 개가 있습니다. Queue 깊이가 WRED Min 을 넘으면 지나가는 패킷 일부를 무작위로 CE(Congestion Experienced, ECN 11)로 marking하기 시작하고, WRED Max 를 넘으면 모든 패킷을 marking합니다. 그 위의 Drop threshold에 이르러서야 비로소 드롭이 시작됩니다. 즉 marking 강도가 queue 깊이에 비례해 점진적으로 올라가는 구조이고, 이 점진성이 송신자가 반응할 시간을 벌어 줍니다.

CE 표시를 받은 수신 NIC는 CNP(Congestion Notification Packet) 를 만들어 송신자에게 돌려보냅니다. 2주차에서 개념으로 언급한 그 CNP인데, 내부를 들여다보면 설계 의도가 보입니다. BTH의 OpCode를 0x81로 설정해 CNP임을 식별하고, 혼잡을 유발한 원래 QP 번호를 재사용해 feedback이 정확히 그 송신 컨텍스트에 도달하게 합니다. 그리고 IP 헤더의 DSCP를 48로 설정해 데이터 트래픽(DSCP 24)과 구분합니다. 이 구분이 중요한 이유는 큐 배치 때문입니다.

트래픽 DSCP QoS Group Queue 처리
RoCEv2 data 24 3 Queue 3 대역폭 보장 + WRED/ECN
CNP 48 7 Queue 7 Strict priority

CNP가 데이터 트래픽과 같은 큐에서 대기하면, 혼잡할수록 혼잡 신호가 늦게 도착하는 모순이 생깁니다. 신호가 늦으면 송신 NIC의 감속이 늦고, 그 사이 queue는 계속 차오릅니다. 그래서 CNP는 strict priority 큐로 분리합니다. CNP를 받은 송신 NIC는 해당 QP의 패킷 간 간격을 늘려 전송률을 낮추고, CNP가 더 오지 않으면 천천히 다시 올립니다. 2주차에서 말한 대로 이 속도 계산은 스위치가 아니라 NIC에서 이루어집니다.

다음은 PFC 쪽 임계값입니다. Ingress buffer 사용량이 xOFF 를 넘으면 upstream에 pause frame을 보내고, xON 아래로 내려가면 재개 신호를 보냅니다. 네 임계값을 한 줄에 놓으면 DCQCN의 설계 원칙이 그대로 드러납니다. xON·xOFF는 ingress buffer, WRED Min/Max는 egress queue에서 재는 값이라 측정 위치는 다르지만, 설계의 핵심은 어느 쪽이 먼저 트리거되느냐의 순서입니다.

flowchart LR
    A["xON
<br>PFC 재개"] --> B["WRED Min
<br>ECN marking 시작"]
    B --> C["WRED Max
<br>전 패킷 marking"]
    C --> D["xOFF
<br>PFC pause"]
    D --> E["Drop threshold"]

Figure 6.1 Queue가 차오르는 방향으로 본 임계값 순서

이 순서가 깨져 ECN marking보다 PFC pause가 먼저 발동하면 세 가지가 연쇄됩니다. 송신자가 감속할 기회 없이 트래픽이 급정지하고, 급격한 backpressure와 HOL blocking이 생기며, queue가 xON 아래로 빠지는 순간 멈췄던 트래픽이 한꺼번에 재개되어 다시 xOFF를 치는 stop-and-go 진동이 반복됩니다. GPU 입장에서는 step time이 널뛰는 것으로 나타납니다. 결국 DCQCN 튜닝의 절반은 이 네 값의 순서와 간격을 지키는 일입니다.

7. PFC를 켠다는 것의 실제 비용 — Pause frame 해부, headroom, cascading

PFC는 2주차에서 개념을 정리했으니, 이번에는 pause frame 자체와 운영 비용을 봅니다.

Pause frame은 Ethertype 0x8808의 MAC control frame입니다. 내부의 opcode 0x0101이 priority 기반 pause임을 나타내고, 8비트 Class Enable Vector 의 각 비트가 어느 priority queue를 멈출지 지정합니다. 비트 3이 켜져 있으면 priority 3, 즉 RoCEv2 데이터 큐만 멈추고 나머지는 흐르게 둡니다. 멈추는 시간은 quanta 값으로 정의되는데, 1 quanta는 512 bit time입니다. 400Gbps 인터페이스라면 1 quanta가 약 1.28ns이고, 최대값 65535(0xFFFF)면 약 84마이크로초입니다. Quanta가 0인 frame은 반대로 재개 신호입니다.

PFC가 실제로 드롭을 막으려면 buffer headroom 이 필요합니다. Pause frame이 upstream에 도달해 전송이 실제로 멈추기까지, 이미 회선 위에 떠 있는 in-flight 트래픽은 계속 도착합니다. 이를 받아낼 여유 버퍼가 priority별로 예약되어 있지 않으면, pause를 보내 놓고도 드롭이 납니다. Headroom은 링크 속도와 케이블 길이의 함수이므로 계산 없이 기본값으로 두면 안 되는 항목입니다.

가장 큰 비용은 cascading 입니다. 책의 시나리오를 따라가 보면 이렇습니다. Rail Switch C에서 GPU-3 방향 egress가 막히면 Rail C의 ingress queue가 차오르고, xOFF를 넘는 순간 Rail C가 Spine 1에 pause를 보냅니다. Spine 1은 해당 priority 전송을 멈추는데, 그러면 Spine 1의 egress queue가 drain되지 못하면서 이번에는 Spine 1의 ingress가 차오릅니다. 결국 Spine 1이 Rail A와 Rail B에도 pause를 보내고, GPU 하나로 가는 병목이 소스 스위치 여러 대의 priority 3 트래픽 전체를 멈추게 됩니다.

flowchart RL
    C["Rail Switch C
<br>GPU-3 방향 egress 혼잡"] -->|"pause priority 3"| S["Spine 1
<br>egress 정체 → ingress 포화"]
    S -->|"pause priority 3"| A["Rail Switch A"]
    S -->|"pause priority 3"| B["Rail Switch B"]

Figure 7.1 하나의 병목이 상류로 전파되는 PFC cascading

혼잡이 풀리는 방향도 같은 경로를 되짚습니다. Rail C의 GPU 방향 queue가 drain되어 xON 아래로 내려가면 quanta 0 frame으로 Spine 1에 재개를 알리고, Spine 1이 전송을 재개하면서 상류의 pause도 순차적으로 풀립니다. 이 회복이 정상적으로 일어나지 않고 특정 큐가 pause 상태에 갇히는 경우를 대비한 것이 2주차에서 언급한 PFC Watchdog입니다. 정리하면 PFC는 드롭을 막는 장치일 뿐 병목의 원인을 없애지는 못하며, 켜는 순간 headroom 계산과 watchdog이 세트로 따라와야 합니다.

8. 스위치에 DCQCN을 올리는 여섯 단계

원칙을 알았으니 배치입니다. 장비마다 설정 문법은 다르지만 DCQCN을 fabric에 올리는 구조는 어느 벤더든 같은 여섯 단계로 정리됩니다. 전제는 NIC가 RoCEv2 데이터를 DSCP 24로, CNP를 DSCP 48로 marking한다는 것입니다.

1단계는 분류 입니다. Ingress에서 DSCP 값을 기준으로 RoCEv2 데이터와 CNP를 서로 다른 트래픽 클래스로 식별합니다. 2단계는 내부 marking 입니다. 분류된 트래픽을 스위치 내부의 QoS 그룹, 즉 하드웨어 큐를 결정하는 내부 라벨에 매핑합니다. 예제 기준으로 데이터는 그룹 3, CNP는 그룹 7, 나머지는 기본 그룹입니다. 3단계는 egress queuing 입니다. CNP 큐를 strict priority로 두어 어떤 혼잡 상황에서도 먼저 나가게 하고, 데이터 큐에는 대역폭 보장과 함께 WRED Min/Max 임계값을 설정합니다.

여기에 함정이 하나 있습니다. WRED는 본래 임계값에서 패킷을 확률적으로 드롭하는 AQM(Active Queue Management)입니다. ECN marking으로 동작하게 하려면 해당 큐에서 ECN 동작을 명시적으로 켜야 하며, 이 설정이 빠지면 스위치는 임계값에서 marking 대신 드롭을 수행합니다. 설정은 다 있는데 lossless가 아닌 fabric의 흔한 원인이므로, 리뷰 시 반드시 확인해야 하는 항목입니다.

4단계는 lossless class 정의 입니다. RoCEv2 클래스에 MTU를 설정하고, PFC를 해당 priority에만 활성화합니다. CNP는 pause 대상이 아니라 빨리 전달되어야 하는 트래픽이므로 PFC 대상에서 제외합니다. 5단계는 이 분류·큐잉·lossless 정책들을 시스템 전역 QoS에 bind 하는 것이고, 6단계는 인터페이스 적용 입니다. 포트별로 PFC 협상을 켜고, watchdog을 활성화하고, ingress에 분류 정책을 붙입니다.

한 가지 덧붙이면, PFC 협상은 LLDP의 DCBX TLV로 이루어지며 표준 IEEE 모드와 구형 CEE 모드가 있습니다. 양단 장비의 DCBX 모드와 DSCP-to-priority 매핑이 일치하지 않으면, 의도한 큐가 pause되지 않거나 엉뚱한 트래픽이 멈춥니다. 결국 이 여섯 단계는 스위치 한 대의 설정이 아니라, host NIC부터 super-spine까지 전 구간에서 같은 매핑을 유지하는 작업입니다. "lossless로 설정했다"와 "lossless로 동작한다"가 다른 문장인 이유이며, incast를 재현하는 부하 테스트에서 ECN marking이 PFC pause보다 먼저 관측되는지, drop counter가 0에 머무는지를 확인해야 같은 문장이 됩니다.

9. Elephant flow를 나누는 세 가지 방법 — Flow, Flowlet, Packet

5장에서 확인한 hash polarization으로 돌아옵니다. QP 하나가 하나의 5-tuple flow이고, flow-based ECMP에서는 한 flow의 모든 패킷이 같은 경로에 고정됩니다. 경로가 혼잡해져도 ECMP는 적응하지 않습니다. 그렇다면 분산의 단위를 flow보다 작게 쪼개면 되지 않을까요. 실제로 그 방향으로 두 가지 대안이 있는데, 어디까지 쪼갤 수 있는지는 RDMA 패킷 구조가 결정합니다.

먼저 그 구조부터 봅니다. 큰 RDMA Write는 하나의 패킷에 담기지 않으므로 RDMA Write First / Middle / Last 로 분할됩니다. 4장에서 본 RETH, 즉 원격 주소·R_Key·길이를 담은 헤더는 First 패킷에만 들어갑니다. Middle과 Last에는 주소 정보가 없고, First가 만든 컨텍스트와 PSN 순서에 의존해 이어지는 메모리 위치에 쓰입니다. 즉 이 구조에서는 패킷 순서가 곧 정확성입니다. Out-of-order로 도착하면 수신 NIC는 즉시 처리하지 못하고 buffering해야 하며, 고속 환경에서는 그 buffering 자체가 압박과 드롭 위험이 됩니다.

이 제약을 염두에 두고 세 방식을 비교합니다.

Flowlet 기반 adaptive routing 은 하나의 flow를 패킷 간 gap이 충분한 burst 단위, 즉 flowlet으로 나눕니다. 처음에는 hash대로 경로를 타다가, inter-switch link 사용률이 임계값을 넘으면 이후 flowlet을 덜 혼잡한 경로로 보냅니다. 같은 flowlet 내부의 순서는 유지되므로 reordering 위험이 낮고, flow 고정보다 링크 활용률이 좋습니다. 순서 보장과 분산 사이의 현실적인 절충안입니다.

Packet spraying 은 같은 flow의 패킷을 per-packet으로 여러 경로에 뿌립니다. 링크 활용률은 가장 세밀하게 균등해지지만, 경로마다 지연이 다르므로 out-of-order 도착이 사실상 전제됩니다. First/Middle/Last 구조를 그대로 spraying하면 수신 NIC의 reordering 부담이 병목이 됩니다.

flowchart TB
    subgraph FML ["First / Middle / Last"]
        direction LR
        F1["First
<br>RETH 있음"] --> M1["Middle
<br>컨텍스트 의존"] --> L1["Last
<br>컨텍스트 의존"]
    end
    subgraph WO ["RDMA Write Only"]
        direction LR
        W1["패킷 1
<br>RETH 포함"]
        W2["패킷 2
<br>RETH 포함"]
        W3["패킷 3
<br>RETH 포함"]
    end
    FML -->|"순서가 곧 정확성 → spraying 곤란"| X["경로 분산"]
    WO -->|"각 패킷이 self-contained → spraying 가능"| X

Figure 9.1 분산 단위를 결정하는 패킷 구조

이 문제를 푸는 것이 RDMA Write Only 입니다. NVIDIA ConnectX-5 이후 NIC가 지원하며, 모든 패킷이 자체 RETH를 포함합니다. 각 패킷이 스스로 목적지 메모리 위치를 알고 있으므로 앞 패킷의 컨텍스트에 대한 의존이 크게 줄고, 서로 다른 경로로 도착해도 각자 올바른 위치에 쓸 수 있습니다. Per-packet load balancing은 이 전제가 갖춰졌을 때 비로소 현실적인 선택지가 됩니다.

방식 분산 단위 장점 한계 전제
Flow ECMP Flow 순서 보장, 단순 Elephant flow polarization 없음
Flowlet adaptive routing Flowlet 활용률 개선, reordering 위험 낮음 Gap·임계값 튜닝 필요 스위치의 adaptive routing 지원
Packet spraying Packet 가장 균등한 분산 Out-of-order 전제 NIC의 Write Only 지원

10. 구현의 현재와 표준의 방향 — Adaptive Routing과 Ultra Ethernet

세 방식이 실제로 구현되는 위치를 정리해 두겠습니다. Flowlet 기반 adaptive routing은 주요 데이터센터 스위치 ASIC들이 제공하는 기능으로, 링크 사용률이나 큐 상태를 보고 flowlet 단위로 경로를 바꿉니다. Per-packet spraying은 스위치 혼자서는 완성되지 않습니다. 스위치가 패킷을 뿌리는 쪽이라면, 흩어져 도착한 패킷을 감당하는 쪽은 NIC이기 때문입니다. 9장에서 본 RDMA Write Only 같은 self-contained 패킷 구조와 NIC의 out-of-order 처리 능력이 갖춰져야 성립합니다.

도입 시 공통 주의점은 두 가지입니다. 첫째, 경로 분산 정책이 바뀌면 큐가 차오르는 지점과 패턴이 달라지므로, 6~8장의 ECN·PFC 임계값이 기존 트래픽 패턴 기준으로 튜닝되어 있었다면 재검증이 필요합니다. 둘째, 장비에 따라 동적 분산이 적용된 트래픽에는 QoS·ACL·미러링 같은 다른 기능의 적용 방식이 달라질 수 있으므로, 도입 전에 해당 플랫폼 문서에서 제약을 확인하고 트러블슈팅 절차를 함께 고쳐 두어야 합니다. Load balancing 모드를 바꾸는 것은 트래픽 분산만 바꾸는 것이 아니라 관측과 정책 적용 방식까지 바꾸는 일입니다.

한편 이 문제를 벤더 기능이 아니라 표준으로 풀려는 흐름이 Ultra Ethernet Consortium(UEC)입니다. 2025년 6월 공개된 UEC 1.0 사양의 전송 계층 UET(Ultra Ethernet Transport)는, RoCEv2가 전송 바깥의 부가 장치로 감당하던 두 문제를 전송 설계 안으로 흡수합니다. 하나는 경로입니다. UET는 하나의 flow가 목적지까지의 모든 경로를 동시에 쓰는 multi-path packet spraying을 기본 동작으로 삼고, 단순 ECMP hash 대신 실시간 혼잡 정보를 반영해 경로를 고릅니다. 다른 하나는 순서입니다. UET는 전달의 신뢰성과 순서 보장 수준을 워크로드가 선택하게 합니다. 순서와 신뢰성을 모두 보장하는 Reliable Ordered Delivery, 도착을 보장하되 순서는 보장하지 않는 Reliable Unordered Delivery, 멱등 연산을 위한 변형인 Reliable Unordered Delivery for Idempotent Operations, 그리고 둘 다 보장하지 않는 Unreliable Unordered Delivery입니다. 이 가운데 packet spraying의 짝은 Reliable Unordered Delivery입니다. 수신 endpoint가 out-of-order 도착을 별도의 재정렬 버퍼 없이 처리하도록 설계되어, 여러 경로로 흩어져 도착하는 패킷이 그 자체로 병목이 되지 않습니다. 이번 장에서 본 "패킷이 스스로 주소를 알아야 spraying이 성립한다"는 논리가 표준 차원으로 확장된 셈입니다.

표기에 대해 한 가지 일러두겠습니다. UEC 원문 자료에서는 이 전달 모드들을 약어로 줄여 부르는데, 공교롭게도 그 철자가 2주차에서 rail-optimized와 ToR-optimized 토폴로지를 편의상 부르던 ROD·RUD와 겹칩니다. 약어만 보고 토폴로지 이야기로 읽으면 문맥이 완전히 어긋나므로, 이 글에서는 UEC의 전달 모드를 약어 없이 전부 풀어 썼습니다. UEC 자료를 직접 읽을 때는 같은 철자가 토폴로지가 아니라 전송 계층의 순서 보장 수준을 가리킨다는 점을 기억해 두시기 바랍니다.

정리하면 현재의 fabric은 RoCEv2 위에서 DCQCN을 튜닝하고 flowlet·spraying으로 경로를 나누는 조합이고, 표준의 방향은 전송 계층이 다중 경로와 순서 문제를 처음부터 스스로 다루는 쪽입니다. 어느 쪽이든 이 장의 결론은 같습니다. 분산의 단위를 얼마나 잘게 쪼갤 수 있는지는 스위치가 아니라 패킷 구조와 수신 측 처리 능력이 결정합니다.

11. 서버에서 Multi-Pod까지 — NIC 구조와 non-blocking 계산

이제 물리 구조입니다. 2주차에서 rail 개념, ROD·RUD, 1:1 논블로킹의 필요성까지 다뤘으므로, 이번에는 그 앞뒤에 있는 두 가지를 봅니다. 서버 내부에서 GPU와 NIC를 어떻게 묶는가, 그리고 rail 위로 어떻게 확장하는가입니다.

먼저 서버 내부입니다. 8-GPU H100 서버에서 GPU 간 통신은 NVSwitch로 GPU당 최대 900GB/s 수준까지 가능합니다. 문제는 밖으로 나가는 길입니다. Shared NIC 구조에서는 여러 GPU가 PCIe 스위치를 거쳐 ConnectX-7 200GbE NIC 하나를 공유합니다. GPU당 PCIe Gen5 x16 경로는 방향당 약 64GB/s인데, 공유되는 200GbE NIC는 약 25GB/s입니다. 병목은 명백히 NIC입니다. 개발 환경이나 작은 모델, 비용 민감 환경에는 맞지만, 여러 GPU가 동시에 inter-node RDMA 트래픽을 만드는 학습에는 부족합니다. 반대로 Dedicated NIC per GPU 는 GPU마다 전용 NIC를 붙입니다. GPU별 네트워크 대역폭이 예측 가능해지고, GPUDirect RDMA를 안정적으로 쓸 수 있으며, rail 구조와 자연스럽게 맞습니다. 대가는 NIC·포트·케이블·전력·랙 설계 복잡도입니다.

참고로 원문과 책의 PCIe 수치에는 오기가 있습니다. PCIe Gen5 x16을 "양방향 64GB/s, 방향당 32GB/s"로 적었는데, full-duplex이므로 방향당 약 64GB/s, 양방향 합산 약 128GB/s로 읽는 것이 맞습니다. 2주차에서 정리했던 수치와도 이쪽이 일치합니다. 어느 쪽으로 읽어도 shared NIC 구조의 병목이 25GB/s짜리 NIC라는 결론은 달라지지 않습니다.

Rail switch와의 연결에서는 가용성 축이 추가됩니다. Single rail switch 설계는 스위치 한 대에 여러 호스트의 GPU NIC를 연결하고 포트를 rail별 VLAN으로 나눕니다. 단순하고 싸지만 스위치 한 대가 단일 장애점입니다. Dual-rail switch 설계는 GPU마다 dual-port NIC를 두고 두 포트를 서로 다른 rail switch에 연결합니다. 스위치·링크·NIC 포트 어느 하나가 죽어도 GPU가 고립되지 않으며, 두 스위치를 하나의 논리 장비처럼 보이게 하려고 MLAG 계열 기술(벤더마다 구현과 명칭이 다릅니다)이나 표준 기반의 BGP EVPN ESI multihoming을 씁니다. 예제 구성에서는 GPU당 2 x 200Gbps MLAG로 400Gbps를 확보합니다. 대가는 스위치와 케이블 2배, 그리고 control plane 복잡도입니다.

Rail 위로의 확장은 2주차의 1:1 원칙을 수치로 반복하는 일입니다. 책의 Pod 예제에서 rail switch 하나가 호스트 방향으로 32 x 100Gbps = 3.2Tbps를 받는다면, spine 방향 uplink도 합계 3.2Tbps를 맞춰야 논블로킹입니다. Spine당 native 800Gbps 링크를 쓰거나 2 x 400Gbps L3 port-channel로 묶어 이 합을 채웁니다. 용어를 하나 정리해 두면, two-tier 는 물리 스위치 계층 수(rail + spine)를, three-stage Clos 는 패킷 경로 관점(leaf-spine-leaf)을 가리킵니다. 같은 구조를 다른 시점에서 부르는 이름일 뿐 모순이 아닙니다. Pod를 여러 개 연결하면 super-spine 계층이 추가되어 three-tier, five-stage Clos가 되고, spine에서 super-spine으로 올라가는 uplink에도 같은 3.2Tbps 계산을 반복합니다.

flowchart TB
    SS1["Super-Spine"] --- SP1["Spine · Pod 1"]
    SS1 --- SP2["Spine · Pod 2"]
    SP1 --- R1["Rail/Leaf x8
<br>down 3.2T = up 3.2T"]
    SP2 --- R2["Rail/Leaf x8
<br>down 3.2T = up 3.2T"]
    R1 --- H1["GPU Hosts"]
    R2 --- H2["GPU Hosts"]

Figure 11.1 Pod에서 Multi-Pod로 계층이 늘어도 반복되는 1:1 계산

Oversubscription을 피하는 이유는 5장과 이어집니다. Collective의 east-west 트래픽에서 uplink가 모자라면, 6~8장의 모든 장치가 더 자주, 더 세게 동작해야 합니다. Congestion control은 topology의 부족함을 숨기지 못합니다.

12. Rail을 물리 스위치에 어떻게 담을 것인가 — Failure Domain의 가격

마지막 결정입니다. 2주차의 ROD·RUD가 "GPU와 리프를 어떻게 짝짓는가"였다면, 이번 질문은 다른 축입니다. 논리 rail을 물리 스위치 몇 대에 담을 것인가입니다. 같은 rail 구조라도 이 선택에 따라 장애 도메인이 완전히 달라집니다.

Multi-Rail-per-Switch 는 물리 스위치 한 대 안에 여러 논리 rail을 VLAN과 subnet으로 나눠 담습니다. 스위치 수가 가장 적어 CapEx가 낮고 lab이나 PoC에 맞습니다. 하지만 논리적으로만 나뉘었을 뿐 물리 스위치는 하나이므로, 하드웨어 장애나 설정 실수 하나가 여러 rail에 동시에 미칩니다. Dual-Rail-per-Switch 는 스위치 한 대에 rail 두 개를 담는 중간형입니다. 구성이 단순해지고 비용도 절충되지만, 두 rail이 같은 장애 도메인에 묶인다는 본질은 그대로입니다. Single-Rail-per-Switch 는 rail 하나당 물리 스위치 한 대를 씁니다. 가장 비싸지만 장애가 해당 rail에만 갇히고, subnet 설계와 트러블슈팅이 단순해지며, NVIDIA의 Scalable Unit 방식과 가장 잘 맞습니다.

항목 Multi-Rail-per-Switch Dual-Rail-per-Switch Single-Rail-per-Switch
물리 스위치당 rail 수 여러 개 2개 1개
격리 수준 Logical only Logical only Physical
Failure domain 가장 큼 여전히 큼 작고 예측 가능
CapEx 가장 낮음 중간 가장 높음
적합한 환경 Lab, PoC 중간 규모, 비용 절충 대규모, mission-critical

판단 기준은 5장에서 본 그대로입니다. AI 학습에서 rail 장애는 링크 다운으로 끝나지 않고 NCCL timeout과 job failure로 나타납니다. 규모가 커질수록 Single-Rail-per-Switch의 추가 비용은 사치가 아니라 장애 격리를 사는 값에 가깝습니다. 반대로 개발용 소규모 클러스터라면 Multi-Rail-per-Switch로 비용을 아끼는 것이 합리적입니다.

책은 이 결정들을 문서화하는 틀도 제시합니다. GPU 호스트와 rail switch를 묶은 Segment, segment들을 spine으로 묶은 Pod, pod들을 super-spine으로 묶은 Multi-Pod, 그리고 control plane·load balancing·congestion control·MTU 같은 공통 정책을 담는 Global AI Fabric Profile 입니다. 결국 rail 설계는 단독 결정이 아니라, 68장의 QoS·DCQCN 정책과 910장의 분산 정책이 fabric 전체에서 일관되게 적용되는지를 함께 묻는 일입니다.

13. 병렬화 전략은 fabric의 어느 지점을 먼저 때리는가

마무리 전에 교재 8장과의 연결을 정리합니다. 8장은 데이터·텐서·파이프라인·MoE 같은 병렬화 전략을 다루는 장이고, 1주차에서 그 개요를 이미 정리했습니다. 여기서 다시 짚는 이유는 하나입니다. 어떤 병렬화 전략을 쓰느냐가 통신 패턴을 결정하고, 통신 패턴이 이번 주에 본 장치들 중 무엇이 먼저 시험대에 오르는지를 결정하기 때문입니다. Fabric 설계는 워크로드와 무관한 일반해가 아니라, 병렬화 전략이라는 입력에 대한 답입니다.

병렬화 전략 통신 패턴 먼저 부딪히는 문제 이번 주의 대응 지점
Data Parallelism Backward 이후 주기적 gradient AllReduce burst Egress fan-in congestion, elephant flow(5장) ECN·CNP rate control(6장), 1:1 non-blocking 계산(11장)
Tensor Parallelism 레이어 내부의 잦은 교환, 지연에 민감 Pause나 queueing 한 번이 step time에 직결 NVLink 도메인 안 배치, 임계값 순서 검증(6장)
Pipeline Parallelism Stage 간 activation·gradient의 point-to-point 전달 특정 경로 hotspot, 경유 지점의 HOL blocking(5장) Flowlet adaptive routing(9장), rail 구조를 고려한 배치(11~12장)
MoE Expert 간 All-to-All, many-to-many Hash polarization, incast(5장) 경로 다양성 확보, packet spraying과 그 전제(9~10장)

표를 가로로 읽으면 설계 우선순위가 나옵니다. Data parallel 중심 클러스터라면 collective 순간의 fan-in 계산과 ECN 튜닝이 먼저이고, tensor parallel 비중이 크다면 통신을 가능한 한 NVLink 도메인 안에 가두는 배치가 먼저입니다. MoE처럼 all-to-all이 지배하는 워크로드라면 flow 단위 ECMP로는 버티기 어려우므로 경로 분산 방식 자체를 먼저 결정해야 합니다. 같은 fabric이라도 워크로드가 바뀌면 병목의 위치가 바뀐다는 것, 이것이 교재 8장을 이번 주 내용 앞에 두는 이유입니다.

14. 결론 — 무손실은 약속이 아니라 검증의 결과다

지난 두 주의 결론을 다시 놓고 보겠습니다. 1주차는 AI 인프라 최적화가 빠른 연산기에 끊임없이 데이터를 공급하는 문제라는 것이었고, 2주차는 그 공급이 끊기지 않도록 수천 장의 GPU가 서로를 기다리지 않게 만드는 것이 네트워크 설계의 목적이라는 것이었습니다. 이번 주는 그 두 문장을 지탱하는 구체적인 장치들을 확인한 기록입니다.

RDMA Write는 주소만 알면 쓰는 동작이 아니라, 보호 도메인과 세 개의 키, 큐, 연결 상태, 헤더가 전부 합의된 뒤에야 성립하는 절차였습니다. 그 잘 준비된 쓰기가 collective 순간에 한꺼번에 몰릴 때 fabric은 egress fan-in, 단일 장애점, HOL blocking, hash polarization이라는 네 가지 방식으로 무너질 수 있습니다. 이를 막는 congestion control의 실체는 xON < WRED Min < WRED Max < xOFF라는 임계값 순서와, CNP를 strict priority로 분리하는 큐 설계, 그리고 WRED에 ECN 동작을 명시하는 설정 한 줄 같은 디테일이었습니다. 경로 쪽에서는 분산 단위를 flow에서 flowlet, packet으로 좁혀 갈수록 활용률이 좋아지지만, 그 끝의 packet spraying은 RDMA Write Only처럼 패킷이 스스로 주소를 아는 구조가 전제였습니다. 그리고 이 모든 것의 물리적 상한을 정하는 topology에서는, NIC를 GPU마다 전용으로 둘지, uplink를 1:1로 맞출지, rail을 물리 스위치에 몇 개씩 담을지가 성능과 장애 도메인을 결정했습니다.

한 문장으로 요약하면 이렇습니다. Lossless AI fabric은 선언이 아니라 검증의 결과이며, 검증 대상은 키, 임계값의 순서, 큐 매핑의 일관성, 그리고 배선의 산수입니다. 이 넷은 한 세트로 설계되고 한 세트로 검증되어야 합니다. Congestion control은 topology의 부족함을 숨기지 못하고, topology는 congestion control 없이 무손실이 되지 못합니다.