공개용 런북의 쉬운 설명판. 명령어와 수치는 그대로 두되, 각 단계마다 “이게 무엇이고 왜 필요한지"를 먼저 풀어 썼다. 주소·계정 같은 내부 정보는 <node1-ip> 식으로 가렸다.


0. 먼저, 큰 그림

이 프로젝트가 한 일을 한 문장으로 줄이면:

“혼자서는 못 드는 무거운 짐(대형 AI 모델)을 네 사람(GPU 컴퓨터 4대)이 나눠 들게 만들었다.”

  • 모델 이름은 GLM-5.3-Flash. 파라미터(모델의 ‘지식’을 담은 숫자) 3,200억 개짜리 초대형 모델이다. 압축(양자화)해도 185GB라 컴퓨터 한 대(메모리 128GB)에는 들어가지 않는다.
  • 그래서 모델을 4등분해서 컴퓨터 4대에 나눠 올린다. 이것을 텐서 병렬(Tensor Parallel, TP) 이라고 하고, 4대로 나누니 TP=4 다.
  • 문제는 이 4대가 한 글자를 만들 때마다 서로 계산 결과를 주고받아야 한다는 것. 그래서 일반 사무실 네트워크(1Gbps)로는 어림도 없고, 초고속 전용 네트워크(200Gbps, 200배 빠름)가 필요하다.
  • 구축 작업의 절반 이상이 바로 이 “초고속 전용 네트워크"를 만드는 일이었다.

전체 흐름:

[1] 컴퓨터 4대의 초고속 네트워크 카드 설정
[2] 초고속 스위치(허브) 설정  ← 가장 애먹은 부분
[3] 네 대가 서로 빠르게 통신되는지 검증
[4] 모델 파일 보관소(NAS)와 고속 임시 보관소(핫티어) 만들기
[5] 185GB 모델 다운로드 (인터넷이 느려서 5시간)
[6] 4대에 배포하고 실행
[7] 상태 감시 화면(모니터링) 설치

1. 용어 사전 — 이것만 알면 아래를 읽을 수 있다

용어쉬운 설명
노드(node)클러스터를 이루는 컴퓨터 한 대. 여기선 node1~node4
DGX Spark / GB10NVIDIA가 만든 책상 위 소형 AI 컴퓨터. GB10은 그 안의 칩 이름. Lenovo ThinkStation PGX는 같은 물건의 Lenovo판
통합 메모리 128GB이 컴퓨터는 CPU와 GPU가 메모리 128GB를 함께 쓴다. 즉 GPU 메모리 = 컴퓨터 메모리. 다른 프로그램이 메모리를 쓰면 AI 모델이 쓸 자리가 줄어든다 (뒤에서 문제가 됨)
NIC / 네트워크 카드컴퓨터의 랜 포트. 여기 컴퓨터엔 일반 1G 포트 외에 200G 초고속 포트가 2개 더 있다(제조사 Mellanox의 ConnectX-7, 줄여서 CX-7)
QSFP초고속 포트의 규격 이름. 일반 랜선 대신 굵은 전용 케이블(DAC)을 꽂는다
스위치(switch)여러 컴퓨터를 연결하는 허브. 여기선 MikroTik CRS804라는 400G 스위치
브레이크아웃(breakout) 케이블스위치의 400G 포트 하나를 200G 두 가닥으로 쪼개는 Y자 케이블. 스위치 포트 4개 × 2가닥 = 8가닥 → 컴퓨터 4대 × 2포트에 딱 맞는다
레인(lane)400G 포트는 내부적으로 50G짜리 회선 8개(레인)를 묶은 것. 200G 두 가닥 = 레인 4개씩 두 묶음
오토네고(auto-negotiation)양쪽 장비가 “우리 몇 G로 통신할까?“를 자동 협상하는 기능. 잘 되면 편하지만, 이번엔 자동 협상이 50G에 머물러서 수동으로 200G를 강제해야 했다
MTU 9000 / 점보 프레임데이터를 한 번에 보내는 봉투 크기. 기본 1500바이트 대신 9000바이트짜리 큰 봉투를 쓰면 대용량 전송 효율이 올라간다. 경로 위 모든 장비가 같은 크기로 맞춰야 한다
RDMA / RoCE한 컴퓨터가 다른 컴퓨터의 메모리에 CPU를 거치지 않고 직접 쓰는 기술. AI 노드 간 통신의 핵심. RoCE는 “이더넷 위에서 RDMA를 한다"는 뜻
플레인(plane) A/B컴퓨터마다 200G 포트가 2개라, 포트1끼리 묶은 망을 A, 포트2끼리 묶은 망을 B라고 부른다. 도로 2차선과 비슷
NCCLNVIDIA가 만든 “GPU끼리 데이터 주고받기” 라이브러리. 어떤 네트워크 카드를 쓸지 환경변수로 알려줘야 한다
vLLMAI 모델을 서버로 띄워주는 프로그램. ChatGPT처럼 질문을 보내면 답을 돌려주는 API가 된다
컨테이너 / 도커(Docker) / 이미지프로그램과 필요한 부품을 한 상자에 담아 어디서든 똑같이 실행하는 기술. 상자의 설계도가 “이미지”(31GB), 실제 실행 중인 상자가 “컨테이너”
양자화 / NVFP4모델의 숫자를 더 적은 비트로 표현해 크기를 줄이는 압축. NVFP4는 4비트 압축 방식. 덕분에 320B 모델이 185GB로 줄었다
KV 캐시모델이 긴 대화를 기억하기 위해 쓰는 작업 메모리. 클수록 긴 문서·많은 동시 사용자를 처리한다
NAS네트워크에 붙은 공용 하드디스크(Synology). 느리지만 용량이 크다(7.3TB)
NFS다른 컴퓨터의 디스크를 내 컴퓨터 폴더처럼 쓰게 해주는 규약
핫티어(hot tier)“자주 쓰는 것은 빠른 창고에, 나머지는 큰 창고에” 두는 2단 구조에서 빠른 창고. 여기선 node1의 NVMe SSD
tok/s (토큰/초)AI가 글자를 만드는 속도. 토큰은 대략 한국어 1~2글자. 37 tok/s면 사람이 읽는 속도보다 훨씬 빠르다

2. 하드웨어와 연결 구조

구성내용
컴퓨터DGX Spark(GB10) 4대. 각 128GB 메모리, 200G 포트 2개
초고속 스위치MikroTik CRS804-4DDQ-hRM (400G 포트 4개)
케이블400G → 200G×2 브레이크아웃 DAC 4본. 컴퓨터 한 대당 200G 두 가닥
일반 네트워크별도의 1G 스위치 (인터넷, 관리용)
저장소NAS(느림·큼) + node1 SSD 핫티어(빠름)

그림으로:

              [MikroTik CRS804 400G 스위치]
   포트1 ─Y─┬─ node1 포트A          포트3 ─Y─┬─ node3 포트A
            └─ node1 포트B                   └─ node3 포트B
   포트2 ─Y─┬─ node2 포트A          포트4 ─Y─┬─ node4 포트A
            └─ node2 포트B                   └─ node4 포트B

케이블 사양 (참고)

  • 제품: NADDOD Q2Q56-400G-CU 시리즈, 0.5m, 4본. “400G 한 개를 200G 두 개로 나누는” Y자 구리 케이블
  • 구리선 안에 신호선 8쌍(레인)이 들어 있고, 4쌍씩 나눠 200G 두 가닥이 된다. 전기 신호를 증폭하는 칩이 없는 패시브 케이블이라 소비전력이 거의 0(<0.1W), 대신 3m를 넘기지 못한다
  • 가격대: 1m 기준 약 138달러
  • 우리 환경에서의 교훈: 스위치가 이 케이블을 자동 인식하면 50G로만 붙는다. 양쪽 모두 “무조건 200G"로 고정해야 8가닥 전부 200G가 된다

3. 단계 1 — 컴퓨터 쪽 초고속 포트 설정

왜 필요한가

포트에 주소(IP)를 주고, 큰 봉투(MTU 9000)를 쓰게 하고, 200G로 고정해야 한다. 안 그러면 케이블을 꽂아도 “연결 없음"이다.

먼저 겪은 함정: 어느 포트가 진짜인가

컴퓨터 안에서는 초고속 포트가 4개로 보인다(enp1s0f0np0, enp1s0f1np1, enP2p1s0f0np0, enP2p1s0f1np1). 하지만 실제 뒷면 구멍은 2개다. 확인해 보니 이름에 f1이 들어간 두 개가 진짜 구멍이고, f0 두 개는 케이블이 없다고 나온다(ethtool -m 명령이 “No cable”). f0에 설정을 넣고 왜 안 되지 하고 헤매기 쉽다.

컴퓨터 안 이름실제용도
enp1s0f1np1뒷면 QSFP 포트 1플레인 A (예: 192.168.A.노드번호)
enP2p1s0f1np1뒷면 QSFP 포트 2플레인 B (예: 192.168.B.노드번호)
enp1s0f0np0, enP2p1s0f0np0구멍 없음사용 안 함

실제 설정 (Ubuntu의 NetworkManager 사용)

nmcli con add type ethernet ifname enp1s0f1np1 con-name roce0 \
  ipv4.method manual ipv4.addresses 192.168.A.N/24 ipv4.never-default yes \
  ipv6.method disabled 802-3-ethernet.mtu 9000 \
  802-3-ethernet.auto-negotiate no 802-3-ethernet.speed 200000 802-3-ethernet.duplex full

읽는 법: “이 포트에 고정 IP를 주고, 인터넷 기본 경로로는 쓰지 말고(never-default), IPv6는 끄고, 봉투 크기 9000, 자동 협상 끄고 200G 고정.” 포트 2(enP2p1s0f1np1)도 똑같이, 이름만 roce1.

추가로 넣은 것:

  • arp_ignore=1, arp_announce=2: 포트가 2개라 “누구세요?” 질문에 엉뚱한 포트가 대답하는 혼선을 막는다.
  • ethtool -A <포트> rx on tx on: 상대가 너무 빨리 보내면 “잠깐만"이라고 신호하는 기능(플로우컨트롤) 켜기.
  • vm.swappiness=0: 메모리가 꽉 찼을 때 디스크로 내보내는 동작을 최소화. AI 서빙 중엔 이게 켜져 있으면 컴퓨터가 멈출 수 있다고 레시피 저자가 경고.

4. 단계 2 — 스위치 설정 (가장 애먹은 부분)

배경

MikroTik 스위치는 RouterOS라는 자체 운영체제를 쓰고, WinBox라는 윈도우 프로그램이나 명령줄로 설정한다. 400G 포트 하나는 내부적으로 레인 8개로 되어 있고, 스위치는 이걸 qsfp56-dd-1-1 ~ qsfp56-dd-1-8처럼 8개의 가짜 포트로 보여준다.

200G 두 가닥으로 쪼개려면 레인 14를 묶은 -1과 레인 58을 묶은 -5, 이 두 개만 쓰면 된다. 나머지 6개는 꺼둔다.

실제로 벌어진 일 (순서대로)

  1. 처음: 케이블만 꽂으니 링크는 뜨는데 속도가 50G. 자동 협상이 “레인 하나짜리 속도"만 제안하고 있었다. 컴퓨터가 원하는 200G가 후보에 아예 없었다.
  2. 시도: “200G도 제안해"라고 설정(advertise=200G-baseCR4)했더니 제안 목록이 텅 비고 링크가 끊겼다. 이 스위치의 쪼갠 포트는 자동 협상으로는 200G를 제안하지 못한다.
  3. 해결: 자동 협상을 끄고 “무조건 200G” 로 고정. 컴퓨터 쪽도 같이 고정. → 8가닥 전부 200G로 연결.
  4. 두 번째 함정: 링크는 200G인데 컴퓨터끼리 ping이 안 됨. 스위치 안에서 포트들을 하나의 “가상 허브(브리지)“에 묶어야 서로 통하는데, 한 줄짜리 명령으로 8개를 한 번에 넣은 것이 아무 오류 없이 조용히 실패해 있었다. 하나씩 넣는 반복문으로 다시 추가하니 해결.

최종 명령

# 200G 서브포트 8개: 자동협상 끄고 200G 고정, 플로우컨트롤, 큰 봉투
/interface/ethernet/set [find name~"qsfp56-dd-[1-4]-(1|5)\$"] auto-negotiation=no speed=200G-baseCR4 fec-mode=auto rx-flow-control=on tx-flow-control=on l2mtu=9216 mtu=9000
# 안 쓰는 서브포트 24개 끄기
/interface/ethernet/set [find name~"qsfp56-dd-[1-4]-(2|3|4|6|7|8)\$"] disabled=yes
# 가상 허브 만들기 (STP 등 부가기능 끔)
/interface/bridge/add name=br-roce protocol-mode=none mtu=9000
# 8개 포트를 허브에 하나씩 추가 (한 줄로 넣으면 조용히 실패함)
:foreach i in=[/interface/ethernet/find name~"qsfp56-dd-[1-4]-(1|5)\$"] do={/interface/bridge/port/add bridge=br-roce interface=$i hw=yes}

확인: /interface/ethernet/monitor [find name~"qsfp56-dd-[1-4]-(1|5)\$"] once → 8개 모두 link-ok 200Gbps가 나와야 한다. 그리고 /interface/bridge/port/print에 8개가 보여야 한다.

혼잡 제어는 복잡한 PFC 대신 단순한 802.3x 플로우컨트롤 + 큰 봉투만 썼다. 이 스위치에 AI 전용 장비 4대만 물려 있어서 충분하다.


5. 단계 3 — 정말 빠른지 검증

  • ping 테스트: 8972바이트짜리 큰 봉투를 “쪼개지 말고 그대로 보내라(DF)” 옵션으로 보냈을 때 성공 → 경로 전체가 큰 봉투를 지원한다는 뜻.
  • RDMA 속도 테스트 (ib_write_bw): 두 플레인에서 동시에 측정하니 98 + 98 Gb/s. 200G 포트인데 왜 100G씩이냐 하면, 이 소형 컴퓨터는 네트워크 카드와 CPU 사이 통로(PCIe)가 좁아서 포트당 100G 근처가 한계다. 다른 사용자들 보고와 일치하는 정상 수치. 참고로 일반 사무실 네트워크(1G)의 약 200배.
  • rdma_cm 방식(-R)은 오류가 나서, RoCEv2용 주소 번호(GID index 3)를 직접 지정하는 방식으로 측정했다.

6. 단계 4 — 파일 보관 구조 (콜드/핫)

왜 두 단계인가

  • NAS는 7.3TB로 크지만 1G 네트워크라 초당 110MB. 185GB 모델을 4대가 각자 읽으면 30분씩 걸린다.
  • node1의 SSD를 초고속 네트워크 위에서 공유하면 초당 1GB 이상. 같은 파일을 3분이면 읽는다.

그래서:

인터넷 ──(느림, 1회만)──▶ NAS(원본 보관, 콜드)
                             │ 서빙할 모델만 복사
                             ▼
                       node1 SSD(핫티어) ──(200G 망)──▶ node2·3·4가 같은 폴더로 읽음

설정 요점

  • NAS: Synology의 NFS 기능을 켜고 공유폴더를 만든다. 기본은 오래된 NFS v3만 켜져 있어서 v4.1을 따로 켜야 리눅스에서 잘 붙는다. 4대 모두 /mnt/nas-models에 마운트.
  • 핫티어: node1에 NFS 서버를 설치하고 SSD 폴더를 초고속 망 주소로만 공개. 3대가 /mnt/hot-models로 마운트(nconnect=8 옵션 = 연결 8개로 병렬).
  • 실측: 핫티어 쓰기 942MB/s, 읽기 1.2GB/s.
  • 보안: 공개 대상은 노드 4대 IP만, root 권한 매핑은 끄는 것(root_squash)을 권장.

7. 단계 5 — 모델 다운로드와 배포

다운로드

  • 모델 본체(RedHatAI/GLM-5.3-Flash-NVFP4, 185GB)와 보조 모델(드래프터 incoai/GLM-5.3-Flash-DFlash2, 2.3GB), 그리고 실행 프로그램 이미지(31GB)를 node3 한 대에서만 받았다. 4대가 각자 받으면 안 그래도 느린 인터넷을 4등분하게 된다.
  • 인터넷이 약 100Mbps라 모델 185GB에 5시간, 이미지 31GB는 중간에 세 번 끊겨(unexpected EOF) 재시작. 다행히 받은 부분은 남아서 이어받기가 됐다.

배포 (여기서 초고속 망이 빛남)

  • 모델 185GB → 핫티어 복사: 6분
  • 드래프터 2.3GB → 3대: 1분 미만
  • 이미지 31GB → 3대: 한 대당 4분 (docker save | ssh | docker load, 이 방법은 암호화 때문에 200G를 다 못 쓰지만 충분)

드래프터가 뭔가

본 모델이 다음 글자를 만들기 전에, 작은 보조 모델이 “아마 이 7글자일 것"이라고 미리 찍고 본 모델이 한 번에 검사하는 기법(투기적 디코딩, 여기선 DFlash2). 맞으면 7글자를 한 번에 진행하니 속도가 오른다. 코드처럼 예측 쉬운 글은 잘 맞고, 자유로운 산문은 덜 맞는다.


8. 단계 6 — 실행

사용한 레시피

GitHub의 tonyd2wild/GLM-5.3-Flash-NVFP4-1M-KV-4x-DGX-Spark — 같은 컴퓨터 4대로 같은 모델을 돌린 사람이 정리한 실행 스크립트와 패치 모음. 이 컴퓨터 칩(GB10, 코드명 sm121)은 신형이라 표준 vLLM이 그대로는 안 돌아서, 저자가 고친 버전(이미지)을 배포한다.

핵심 설정을 말로 풀면

설정의미
rank 0 = node1(head), 1~3 = worker4대 중 한 대(head)가 질문을 받고 나머지에 일을 나눠준다
--tensor-parallel-size 4, --nnodes 4모델을 4등분해서 4대에
NCCL_IB_HCA=rocep1s0f1, NCCL_SOCKET_IFNAME=enp1s0f1np1“GPU끼리 통신할 때 이 초고속 포트(플레인 A)를 써라”
NCCL_IB_GID_INDEX=3, NCCL_IB_ROCE_VERSION_NUM=2RoCE 방식 중 v2, IPv4 주소 사용
--gpu-memory-utilization 0.80메모리 128GB 중 80%까지만 모델이 쓴다 (뒤의 실패담 참고)
--kv-cache-memory 24GiB, fp8_e4m3대화 기억용 메모리 24GB, 압축 형식
--max-model-len 1048576한 번에 다룰 수 있는 글 길이 최대 100만 토큰 (책 수십 권 분량)
DFlash2 num_speculative_tokens 7보조 모델이 7글자씩 미리 찍기

실행 순서 (레시피 저자가 강조한 규칙)

  1. 전부 끄고 시작: 이미 죽어가는 놈이 있으면 새로 뜬 놈이 그놈과 악수하려다 영원히 기다린다. 항상 4대 모두 docker rm -f vllm_glm53.
  2. 메모리 비우기: sync; echo 3 > /proc/sys/vm/drop_caches. 파일 캐시가 GPU 메모리 자리를 잡아먹고 있을 수 있다. 다운로드 같은 무거운 작업도 잠시 멈춘다.
  3. 플러셔 실행: 부팅 내내 1분마다 캐시를 비우는 작은 스크립트. 저자가 “이게 있어야 24GB KV 캐시가 안 죽는다"고 밝혀낸 핵심 비법.
  4. 일꾼 먼저, 대장 나중: node4 → node3 → node2 → node1 순으로 15~20초 간격.
  5. 확인: node1에서 curl localhost:8000/health가 200이면 성공. 로그에 GPU KV cache size: 3,895,606 tokens가 보인다.

실패담 (두 번 실패하고 세 번째 성공)

  • 오류 메시지: Free memory on device cuda:0 (103.12/121.63 GiB) ... less than desired (0.85, 103.38 GiB)
  • 뜻: “80%… 아니 85%를 쓰고 싶은데 지금 비어 있는 게 그보다 0.3GB 적다.”
  • 원인: 이 컴퓨터는 CPU와 GPU가 메모리를 같이 쓴다. 1차는 node3에서 돌던 다운로드가, 2차는 node1의 다른 서비스(웹 UI, 모니터링)가 그 0.3GB를 잡고 있었다.
  • 해결: 85% → 80% 로 낮추고 캐시를 비운 뒤 재시작. 실패하면 1~2분 안에 컨테이너가 죽으므로 금방 알 수 있다.
  • 또 하나: 4대 중 한 대가 죽으면 대장(node1)도 Connection closed by peer라며 같이 종료된다. 그래서 규칙 1(전부 끄고 시작)이 중요하다.

결과

  • 부팅: 컨테이너 시작부터 응답 가능까지 약 8분 (모델 읽기 216초 — 핫티어 덕분. 레시피 문서는 느린 NFS 기준 ~20분)
  • 첫 질문: 261토큰을 11.1초에 (처음엔 내부 컴파일이 있어 느림)
  • 그 다음 코드 생성: 400토큰을 10.7초, 초당 37토큰. 100만 토큰 문맥, 동시 3.7명 분량의 기억 공간.

9. 단계 7 — 감시 화면(모니터링)

  • 각 컴퓨터에 상태 보고 프로그램 2개를 붙인다: node_exporter(CPU·메모리·디스크), nvidia_gpu_exporter(GPU 온도·전력·클럭).
  • node1에 수집기(Prometheus)와 화면(Grafana)을 올리고, DGX Spark용으로 만들어진 대시보드(RodriMora/dgx-spark-grafana-dashboard)를 불러온다. 대시보드 쿼리의 vllm:을 vllm_로 바꿔야 우리 vLLM 지표 이름과 맞는다.
  • 이 컴퓨터만의 함정: node_exporter의 CPU 클럭 수집 기능(cpufreq)이 GB10에서 영원히 멈춘다. 멈춘 요청이 쌓여 “503 서비스 불가"가 뜬다. --no-collector.cpufreq로 끄면 정상.
  • Grafana 초기 비밀번호는 반드시 바꾸고, 기존 서비스와 포트가 겹치지 않게 한다.

10. 단계별 실제 걸린 시간

총 약 9시간. 그중 6시간은 인터넷 다운로드 대기였다.

단계걸린 시간한 줄 설명
컴퓨터 포트 설정 (대당)1~2분스크립트 한 번
스위치 설정약 25분명령 자체는 5분, 50G 문제·브리지 조용한 실패 찾느라 20분
속도 검증3분
프로그램 이미지 받기 25~31GB1~5.5시간인터넷 나눠 쓰면 더 느림, 세 번 끊김
모델 185GB 받기 (1대)약 5시간초당 10MB
모델 → 핫티어 복사약 6분초당 500MB
드래프터 → 3대1분 미만
이미지 → 3대대당 약 4분
AI 서버 부팅약 8분
부팅 실패 알아차리기1~2분
NAS 공유 + 4대 연결약 10분
핫티어 만들기약 5분
모니터링대당 3~5분 + 원인 찾기 10분

교훈: 인터넷이 병목이면 “한 대만 받고, 초고속 망으로 나눠준다” 가 전부다.


11. 다시 한다면 — 교훈 여섯 가지

  1. 진짜 포트는 f1이다. f0에 설정 넣고 “왜 링크가 안 뜨지” 하지 말 것.
  2. MikroTik 400G 쪼개기는 -1/-5 서브포트에 자동협상 끄고 200G 강제. 자동으로 두면 50G에 머문다.
  3. RouterOS에서 여러 포트를 한 줄로 브리지에 넣으면 조용히 실패할 수 있다. 넣고 나서 반드시 목록을 출력해 확인.
  4. 통합 메모리 컴퓨터에서 GPU 85%는 여유가 0.3GB뿐. 다른 서비스가 같이 살면 80%.
  5. 핫티어 하나로 부팅 시간이 절반 이하. SSD 한 개를 초고속 망으로 공유하는 것만으로 충분하다.
  6. 공개 문서에는 IP·계정·비밀번호·포트 목록·공유 설정을 쓰지 말 것. 이 문서는 그것들을 모두 지운 결과다.