Skip to main content

단일 서버의 벽을 넘는 순간

지금까지는 한 대의 서버 안에서 여러 GPU를 묶는 방법을 살펴보았습니다. 4장짜리 RTX 4090 워크스테이션, 8장짜리 A100/H100 서버, 혹은 NVLink와 NVSwitch가 들어간 고급 장비까지, 단일 노드 안에서도 꽤 많은 일을 할 수 있습니다. 하지만 어느 순간에는 분명히 이런 질문이 찾아옵니다.

"이 모델은 서버 한 대에 안 들어가는데요?"

405B급 모델을 FP8로 올리면 가중치만 대략 405GB가 필요합니다. 671B급 MoE 모델은 더 복잡합니다. 활성화되는 파라미터는 그보다 적더라도 전체 전문가 가중치는 메모리에 있어야 하고, 긴 컨텍스트와 동시 요청까지 고려하면 단일 서버의 GPU 메모리는 금방 바닥납니다. Llama 3.1 405B FP8을 여러 A100 40GB 노드에 올리는 실습에서도 단순 계산상 3개 노드면 될 것 같지만, 실제로는 메모리 불균형과 런타임 오버헤드 때문에 5개 노드를 사용해야 안정적으로 실행되는 사례가 보고되어 있습니다.1

게다가 모델 가중치만 문제가 아닙니다. 긴 컨텍스트 서비스를 시작하면 KV 캐시가 조용히 VRAM을 먹어 들어갑니다. NVIDIA는 Llama 3 70B를 FP16으로 로드할 때 가중치에 약 140GB가 필요하고, 128K 컨텍스트를 단일 사용자에게 제공하는 KV 캐시만으로도 약 40GB가 추가될 수 있다고 설명합니다.2 70B에서도 이 정도라면 405B, 671B, 장문 RAG, 에이전트 워크플로우, 수백 명 동시 접속 서비스에서는 더 말할 필요가 없습니다.

멀티 노드 GPU 클러스터는 바로 이 지점에서 등장합니다. 단일 서버 안의 GPU를 모두 써도 부족할 때, 여러 서버를 하나의 거대한 추론 장치처럼 묶는 방식입니다. 다만 여기서부터는 문제가 단순히 "GPU를 더 꽂자"가 아닙니다. 이제는 네트워크, 컨테이너, 모델 파일, 런타임, 방화벽, 장애 복구까지 함께 설계해야 합니다.

멀티 GPU 워크스테이션이 고성능 자동차라면, 멀티 노드 클러스터는 철도 시스템에 가깝습니다. 기관차 한 대가 빠른 것도 중요하지만, 선로, 신호 체계, 역 간 간격, 운행 스케줄이 모두 맞아야 전체가 움직입니다.

멀티 노드 GPU 클러스터로 확장하기

기본 원칙: 노드 안에서는 TP, 노드 사이에서는 PP

멀티 노드 LLM 추론을 처음 설계할 때 가장 먼저 잡아야 할 원칙은 간단합니다.

노드 내부에서는 텐서 병렬화, 즉 TP를 우선 사용하고, 노드 사이에서는 파이프라인 병렬화, 즉 PP를 조합하는 것이 기본입니다.

TP는 한 레이어의 행렬 연산을 여러 GPU가 나누어 계산하는 방식입니다. 예를 들어 한 노드에 GPU가 8장 있다면, 각 레이어의 가중치를 8등분하고 모든 GPU가 동시에 같은 토큰의 서로 다른 조각을 계산합니다. 이 방식은 빠르지만 GPU끼리 자주 동기화해야 합니다. 그래서 NVLink, NVSwitch처럼 빠른 내부 인터커넥트가 있는 단일 노드 안에서 특히 잘 맞습니다.

반면 PP는 모델의 레이어를 구간별로 나누는 방식입니다. 앞쪽 레이어는 1번 노드, 중간 레이어는 2번 노드, 뒤쪽 레이어는 3번 노드가 맡습니다. 토큰은 공장 컨베이어 벨트처럼 앞 노드에서 뒤 노드로 흘러갑니다. PP는 TP보다 노드 간 통신 횟수가 적습니다. 매 레이어마다 전체 동기화를 하지 않고, 한 파이프라인 단계의 출력만 다음 단계로 넘기면 됩니다. 그래서 상대적으로 느린 노드 간 네트워크 위에서는 TP보다 PP가 더 현실적인 선택이 되는 경우가 많습니다.

vLLM 공식 문서도 같은 방향의 권장안을 제시합니다. 모델이 단일 GPU에 들어가면 분산 추론을 쓰지 않고, 단일 노드의 여러 GPU에 들어가면 TP를 쓰며, 단일 노드에도 들어가지 않을 때는 TP와 PP를 함께 사용하라고 설명합니다. 예를 들어 2개 노드에 노드당 8개 GPU가 있다면 --tensor-parallel-size 8, --pipeline-parallel-size 2처럼 잡는 식입니다.3

vllm serve /models/llama-3.1-405b-fp8 \
  --tensor-parallel-size 8 \
  --pipeline-parallel-size 2

이 명령은 "각 노드 안의 8개 GPU는 TP로 묶고, 2개 노드는 PP 단계로 나누겠다"는 뜻입니다. 즉 총 16개 GPU를 쓰지만, 모든 GPU가 매 레이어마다 노드 간 all-reduce를 하는 구조는 아닙니다. 통신 부담이 큰 TP는 노드 내부에 가두고, 노드 사이에는 레이어 출력만 넘기는 PP를 배치하는 것입니다.

물론 예외도 있습니다. 모델 구조, GPU 수, 레이어 수, 네트워크 품질, 동시 요청 패턴에 따라 TP를 노드 간으로 넓히거나 PP를 단일 노드 안에서도 사용할 수 있습니다. 하지만 출발점은 위 원칙이 가장 안전합니다. 멀티 노드에서 성능이 안 나올 때 많은 경우는 "느린 네트워크 위에 TP를 너무 넓게 펼친 것"에서 시작됩니다.

405B와 671B 모델은 왜 멀티 노드로 가는가

70B 모델은 양자화와 80GB급 GPU를 조합하면 단일 노드에서도 꽤 잘 다룰 수 있습니다. 하지만 405B와 671B급으로 올라가면 사정이 달라집니다.

405B dense 모델을 BF16으로 올리면 가중치만 약 810GB입니다. FP8로 줄여도 약 405GB입니다. 여기에 KV 캐시, CUDA 그래프, 런타임 버퍼, NCCL 통신 버퍼, 배치 여유 공간이 붙습니다. 8×H100 80GB 단일 노드라면 총 VRAM은 640GB이므로 FP8 가중치 자체는 들어갈 수 있어 보입니다. 하지만 긴 컨텍스트와 높은 동시성을 제공하려면 여유가 빠르게 줄어듭니다. H100 80GB가 8장 있다고 해서 640GB 전체를 모델 가중치에만 써도 되는 것은 아닙니다.

671B급 MoE 모델은 더 흥미롭습니다. MoE는 토큰마다 일부 전문가만 활성화하므로 계산량은 전체 파라미터보다 작을 수 있습니다. 하지만 추론 서버는 라우팅될 가능성이 있는 전문가 가중치를 어딘가에는 들고 있어야 합니다. 따라서 메모리 배치, 전문가 병렬화, 데이터 병렬화, KV 캐시 분산 전략이 함께 필요합니다. 10장에서 본 것처럼 MoE는 단순히 "큰 dense 모델"이 아니라 "전문가를 어디에 둘 것인가"의 문제입니다.

여기에 긴 컨텍스트가 붙으면 멀티 노드는 선택이 아니라 자연스러운 다음 단계가 됩니다. 예를 들어 문서 검색 기반 질의응답에서 사용자당 64K 또는 128K 컨텍스트를 허용하고, 동시에 수십~수백 요청을 처리하려면 KV 캐시가 모델 가중치만큼이나 중요한 자원이 됩니다. 최신 분산 추론 프레임워크들이 KV 캐시 라우팅, KV 오프로드, prefill/decode 분리 같은 기능에 집중하는 이유도 여기에 있습니다. NVIDIA Dynamo는 prefill과 decode 단계를 분리하고, KV 캐시 위치를 추적해 재사용 가능성이 높은 GPU로 요청을 보내며, 사용 빈도가 낮은 KV 블록을 CPU RAM, SSD, 오브젝트 스토리지 등으로 내리는 구조를 제시합니다.4

즉 405B와 671B급에서 멀티 노드는 단순히 "가중치가 커서" 쓰는 것이 아닙니다. 긴 컨텍스트, 고동시성, 높은 처리량, 빠른 첫 토큰 응답 시간, 장애 복구까지 모두 고려한 운영 단위가 여러 노드로 커지는 것입니다.

vLLM 멀티 노드: Ray 클러스터로 하나처럼 묶기

실전에서 가장 접근하기 쉬운 멀티 노드 추론 경로 중 하나는 vLLM과 Ray를 조합하는 방식입니다. vLLM은 단일 노드에서는 multiprocessing 백엔드를 사용할 수 있지만, 공식 문서 기준으로 멀티 노드 추론에는 Ray가 필요합니다.3 Ray는 여러 서버 위의 프로세스를 하나의 분산 실행 환경으로 묶어 주는 런타임입니다.

구성은 보통 head/worker 구조로 시작합니다. head 노드는 클러스터의 중심 주소를 제공하고, worker 노드들은 그 주소로 붙습니다. 중요한 점은 이 head가 반드시 가장 강한 서버일 필요는 없다는 것입니다. 다만 추론 API 서버를 head 노드에서 함께 띄우는 구성이 흔하기 때문에, 네트워크 접근성과 운영 편의성을 고려해 선택합니다.

개념적으로는 다음과 같습니다.

# head 노드
ray start --head --port=6379

# worker 노드들
ray start --address='10.0.0.10:6379'

컨테이너 환경에서는 각 노드에서 같은 vLLM 이미지를 실행하고, Ray head 또는 worker로 붙입니다. vLLM 문서는 Docker 기반 클러스터 실행 스크립트를 제공하며, 모든 노드에서 Python 환경, 모델 경로, 컨테이너 구성이 같아야 한다고 강조합니다.3

이 조건은 생각보다 중요합니다. 멀티 노드에서 가장 허무한 장애는 "한 노드에만 패키지 버전이 다름", "한 노드에서만 모델 경로가 다름", "한 노드의 CUDA 라이브러리만 다름" 같은 문제입니다. 단일 서버에서는 이런 차이가 드러날 기회가 없지만, 분산 실행에서는 한 프로세스만 실패해도 전체 모델 로딩이 실패합니다.

그래서 운영에서는 다음 원칙을 지키는 것이 좋습니다.

  • 모든 노드는 동일한 컨테이너 이미지를 사용한다.

  • 모델은 모든 노드에서 같은 경로로 보이게 한다.

  • Hugging Face 캐시, 토크나이저, 양자화 파일, 엔진 파일을 노드마다 다르게 두지 않는다.

  • NCCL, CUDA, 드라이버, vLLM, PyTorch 버전 조합을 문서화한다.

  • Ray head 주소는 모든 worker에서 접근 가능한 프라이빗 IP를 사용한다.

예를 들어 /models/llama-3.1-405b-fp8라는 경로를 사용한다면, head 노드에서도 worker 노드에서도 그 경로가 동일해야 합니다. 실제 파일이 로컬 NVMe에 복사되어 있든, NFS나 병렬 파일시스템으로 마운트되어 있든, 컨테이너 안에서 보이는 경로는 같아야 합니다. vLLM 문서도 모델을 모든 노드에 같은 경로로 다운로드하거나, 모든 노드가 접근 가능한 분산 파일시스템을 사용하라고 경고합니다.3

SLURM 환경에서 보는 TP와 PP의 실제 조합

HPC 클러스터에서는 Kubernetes보다 SLURM을 먼저 만나는 경우도 많습니다. 연구소, 슈퍼컴퓨팅 센터, 대학 클러스터에서는 sbatch, srun, salloc으로 GPU 노드를 할당받고, 그 위에서 Ray와 vLLM을 띄웁니다.

MeluXina의 Llama 3.1 405B FP8 vLLM 실습은 좋은 예입니다. 해당 예제는 5개 GPU 노드를 할당받고, 각 노드에 A100 40GB GPU 4개를 사용합니다. 스크립트에서는 TENSOR_PARALLEL_SIZE=4로 노드 안 GPU 수를 TP 크기로 잡고, PIPELINE_PARALLEL_SIZE=${SLURM_NNODES}로 노드 수를 PP 크기로 잡습니다.1

export TENSOR_PARALLEL_SIZE=4
export PIPELINE_PARALLEL_SIZE=${SLURM_NNODES}

vllm serve ${HF_MODEL} \
  --tensor-parallel-size ${TENSOR_PARALLEL_SIZE} \
  --pipeline-parallel-size ${PIPELINE_PARALLEL_SIZE}

이 구조는 멀티 노드 추론의 기본 감각을 아주 잘 보여줍니다. 노드 안에서는 빠른 GPU 간 연결을 믿고 TP를 사용합니다. 노드 사이는 네트워크 비용이 크므로 PP 단계로 나눕니다. 그리고 Ray head와 worker를 srun으로 각각 실행해, 할당받은 노드들이 하나의 Ray 클러스터로 동작하게 만듭니다.

여기서 배울 점은 "노드 수를 늘리면 자동으로 빠른 추론 서버가 된다"가 아닙니다. 오히려 반대입니다. 노드 수를 늘릴수록 모델은 들어가기 쉬워지지만, 운영 복잡도와 통신 비용은 같이 증가합니다. 따라서 첫 목표는 "가장 적은 노드로 안정적으로 모델을 올리는 것"이어야 합니다. 그다음 목표가 "동시성이나 처리량을 위해 노드를 늘리는 것"입니다.

multiprocessing, torchrun, MPI 계열 실행은 어디에 쓰나

vLLM의 멀티 노드에서는 Ray가 일반적인 선택이지만, 모든 분산 추론이 Ray로만 돌아가는 것은 아닙니다. TensorRT-LLM, Megatron 계열, PyTorch distributed, MPI 기반 연구 코드에서는 torchrun, mpirun, hostfile을 사용하는 방식이 흔합니다.

예를 들어 간단한 MPI 기반 다중 노드 추론 코드는 다음과 같은 형태로 실행될 수 있습니다.

mpirun -np 16 --hostfile hostfile ./run_inference.sh

여기서 hostfile에는 어떤 서버에서 몇 개 프로세스를 띄울지 적습니다. 4개 노드에 각 4개 GPU가 있다면 총 16개 rank가 생기고, 각 rank가 하나의 GPU 또는 하나의 병렬 조각을 담당합니다. 실제로 CUDA와 MPI를 사용해 4개 노드, 16개 GPU에서 GPT-2 추론을 구현한 공개 예제도 이런 실행 방식을 사용합니다.5

TensorRT-LLM 계열도 엔진 빌드 시점에 TP 크기를 정하고, 실행 시 여러 rank를 띄우는 구조를 사용합니다. 이 방식은 성능 최적화 여지가 크지만, 엔진 생성, rank 관리, 배포 자동화가 상대적으로 까다롭습니다. 반면 vLLM+Ray는 운영 편의성이 높고 OpenAI 호환 API 서버를 쉽게 띄울 수 있어 추론 서비스 입문에 좋습니다.

정리하면, multiprocessing 또는 torchrun/MPI 계열은 "프로세스와 rank를 명시적으로 관리하는 방식"이고, Ray 기반 vLLM은 "분산 런타임이 노드와 GPU 자원을 추상화해 주는 방식"에 가깝습니다. 어느 쪽이든 핵심은 같습니다. 모든 노드의 환경이 같아야 하고, rank들이 서로 통신할 수 있어야 하며, 모델 파일이 같은 위치에서 보여야 합니다.

모델 파일 동기화: 다운로드 병목을 우습게 보지 말자

멀티 노드 추론을 처음 구축할 때 많은 사람이 GPU와 네트워크만 걱정합니다. 그런데 실제 첫 장애는 종종 모델 다운로드에서 터집니다.

405B급 모델은 파일 크기가 수백 GB에 달합니다. TensorRT-LLM처럼 엔진 변환을 거치면 원본 모델보다 훨씬 큰 결과물이 생길 수도 있습니다. NVIDIA Triton과 TensorRT-LLM 멀티 노드 배포 가이드는 여러 노드의 pod가 같은 모델 shard와 엔진 파일을 로드할 수 있도록 공유 persistent volume을 준비해야 한다고 설명합니다. 예시에서는 Llama-3-70B의 원본 90GiB가 변환 후 300GiB 수준으로 커져, 전체 390GiB 저장 공간이 필요하다고 제시합니다.6

이 말은 곧 "모델을 매번 pod 시작 시 다운로드하면 안 된다"는 뜻입니다. 노드가 8개이고 각 노드가 같은 400GB 모델을 동시에 Hugging Face에서 내려받는다면, 시작 시간이 길어지는 것은 물론이고 외부 네트워크, 인증 토큰, 캐시 충돌, 저장 공간 부족 문제까지 한꺼번에 생깁니다.

실전에서는 보통 세 가지 방식 중 하나를 씁니다.

  • 각 노드의 로컬 NVMe에 모델을 미리 동기화한다.

  • NFS, Lustre, BeeGFS 같은 공유 파일시스템을 사용한다.

  • Kubernetes에서는 Persistent Volume을 사용해 모든 pod에 같은 모델 저장소를 마운트한다.

로컬 NVMe 방식은 로딩 속도가 빠르지만 동기화 관리가 필요합니다. 공유 파일시스템은 관리가 편하지만 동시 로딩 시 스토리지 병목이 생길 수 있습니다. Kubernetes PV 방식은 배포 자동화에 유리하지만, 스토리지 클래스와 읽기 성능을 잘 골라야 합니다.

가장 나쁜 방식은 "컨테이너 시작할 때마다 모델 다운로드"입니다. 작은 7B 모델에서는 괜찮아 보일 수 있지만, 405B급에서는 재시작 하나가 장애 시간이 됩니다. 멀티 노드에서는 재시작 시간이 곧 서비스 복구 시간입니다.

NCCL이 TCP를 쓰는지, InfiniBand를 쓰는지 확인하기

멀티 노드 성능의 심장은 NCCL입니다. NCCL은 NVIDIA GPU 간 collective communication을 담당하는 라이브러리입니다. TP에서 자주 등장하는 all-reduce, reduce-scatter, all-gather 같은 통신이 대부분 NCCL을 통해 이뤄집니다.

문제는 NCCL이 항상 우리가 기대한 고속 경로를 쓰지는 않는다는 점입니다. InfiniBand와 GPUDirect RDMA를 갖춘 서버라고 해도, 컨테이너 권한, 드라이버, 네트워크 인터페이스, NCCL 환경 변수, 방화벽 설정이 맞지 않으면 조용히 TCP Socket으로 떨어질 수 있습니다.

vLLM 문서는 이를 확인하는 명확한 방법을 제시합니다. NCCL_DEBUG=TRACE를 켜고 로그에서 네트워크 경로를 확인합니다. 로그에 [send] via NET/Socket이 보이면 NCCL이 일반 TCP Socket을 쓰고 있다는 뜻이고, 노드 간 TP에는 비효율적입니다. 반대로 [send] via NET/IB/GDRDMA가 보이면 InfiniBand와 GPUDirect RDMA 경로를 사용하고 있다는 뜻입니다.3

NCCL_DEBUG=TRACE vllm serve /models/llama-3.1-405b-fp8 \
  --tensor-parallel-size 8 \
  --pipeline-parallel-size 2

컨테이너 기반 실행에서는 환경 변수를 vllm serve 명령 앞에만 붙이는 것으로 부족할 수 있습니다. Ray worker 프로세스는 다른 노드에서 이미 떠 있기 때문입니다. 이런 변수는 클러스터를 만들 때 컨테이너 환경에 함께 주입하는 것이 안전합니다.

-e NCCL_SOCKET_IFNAME=ens5f0 \
-e NCCL_IB_HCA=mlx5 \
-e NCCL_DEBUG=INFO

NCCL_SOCKET_IFNAME은 NCCL이 사용할 네트워크 인터페이스를 지정합니다. 서버에 관리망, 스토리지망, 서비스망, IB/RoCE망이 함께 있으면 NCCL이 엉뚱한 인터페이스를 고를 수 있습니다. 이때 명시적으로 올바른 인터페이스를 지정해야 합니다.

NCCL_IB_HCA는 사용할 InfiniBand HCA, 즉 Host Channel Adapter를 지정합니다. Mellanox/NVIDIA ConnectX 계열 장비에서는 보통 mlx5라는 이름이 등장합니다. 정확한 이름은 ibv_devinfo, ls /sys/class/infiniband, nvidia-smi topo -m 등을 통해 확인합니다.

중요한 것은 "실제로 로그를 보는 습관"입니다. 장비 견적서에 InfiniBand가 적혀 있다고 해서 추론 프로세스가 IB를 쓰는 것은 아닙니다. NCCL 로그가 진실입니다.

InfiniBand와 GPUDirect RDMA가 왜 중요한가

노드 간 통신에서 가장 피하고 싶은 경로는 GPU 메모리의 데이터가 CPU 메모리로 올라갔다가, 커널 네트워크 스택을 거쳐 NIC로 나가고, 다시 상대 노드의 CPU 메모리를 거쳐 GPU로 내려가는 방식입니다. 데이터가 멀리 돌아갑니다. 복사도 많고 CPU 개입도 많습니다.

GPUDirect RDMA는 이 경로를 줄입니다. RDMA는 Remote Direct Memory Access의 약자로, 원격 노드의 메모리에 CPU 개입을 최소화하고 직접 접근하는 기술입니다. GPUDirect RDMA는 GPU 메모리와 네트워크 어댑터 사이의 직접 데이터 이동을 가능하게 해, 노드 간 GPU 통신 지연을 줄입니다.

LLM 추론에서 이 차이는 특히 decode 단계의 작은 반복 통신에서 민감하게 나타납니다. prefill은 큰 입력 프롬프트를 한 번에 처리하므로 계산량이 큽니다. decode는 토큰을 하나씩 생성하며, 매 토큰마다 레이어를 지나갑니다. 노드 간 TP를 넓게 쓰고 있다면 decode 단계에서 반복적인 all-reduce가 통신 병목으로 튀어나올 수 있습니다. 최근 멀티 노드 LLM 추론 연구에서도 all-reduce가 주요 병목으로 지목되며, 더 빠른 계층적 all-reduce 알고리즘이 지연 시간을 크게 낮출 수 있음을 보였습니다.7

그래서 실전에서는 가능하면 다음 순서를 따릅니다. 먼저 노드 내부 TP를 사용합니다. 그다음 노드 간 PP로 모델을 올립니다. 그래도 특정 모델 구조나 처리량 요구 때문에 노드 간 TP가 필요하다면, 그때는 InfiniBand와 GPUDirect RDMA 확인이 필수가 됩니다.

노드 수를 늘리면 함께 늘어나는 것들

클러스터 확장은 달콤해 보입니다. GPU가 부족하면 노드를 더 붙이면 될 것 같습니다. 하지만 노드 수를 늘리면 GPU 수만 늘어나는 것이 아닙니다. 보이지 않는 비용도 함께 늘어납니다.

첫째, 통신 오버헤드가 증가합니다. TP 범위가 넓어지면 collective communication 참여자가 늘어나고, PP 단계가 늘어나면 단계 사이의 데이터 이동이 많아집니다. 특히 네트워크가 균일하지 않거나 일부 노드가 다른 랙에 있으면 지연 시간이 흔들립니다. 추론 서비스는 평균 속도보다 꼬리 지연, 즉 p95와 p99가 더 중요할 때가 많습니다.

둘째, pipeline bubble이 커집니다. PP는 공장 라인과 비슷합니다. 첫 요청이 들어오면 1단계가 일하고, 그다음 2단계가 일하고, 마지막 단계까지 차례로 채워집니다. 이때 앞뒤로 빈 시간이 생기는데 이를 pipeline bubble이라고 부릅니다. 동시 요청이 많으면 bubble이 줄어들지만, 요청이 적거나 길이가 들쭉날쭉하면 GPU가 기다리는 시간이 늘어납니다. 그래서 PP는 단일 요청 지연 시간을 줄이는 도구라기보다, 모델을 메모리에 넣고 충분한 동시성에서 처리량을 끌어올리는 도구에 가깝습니다.

셋째, 장애 전파 범위가 커집니다. 단일 노드 서버에서는 GPU 하나가 죽으면 그 서버 하나만 재시작하면 됩니다. 하지만 8개 노드에 걸친 하나의 모델 병렬 그룹에서는 노드 하나가 실패해도 전체 모델 인스턴스가 멈출 수 있습니다. 분산 추론에서 한 rank가 사라지면 나머지 rank도 정상적으로 계속 계산하기 어렵습니다.

넷째, 재시작 시간이 길어집니다. 모델 파일 로딩, 엔진 초기화, Ray 클러스터 조인, NCCL communicator 생성, KV cache block 할당이 모두 필요합니다. 70B 모델 재시작과 405B 모델 재시작은 체감이 다릅니다. 멀티 노드에서는 가장 느린 노드가 전체 준비 시간을 결정합니다.

다섯째, 모델 다운로드와 스토리지 병목이 커집니다. 앞서 말한 것처럼 대형 모델은 수백 GB입니다. 노드가 많을수록 파일 동기화, 캐시 무결성, 디스크 공간, 공유 스토리지 처리량이 운영 이슈가 됩니다. 모델 파일 하나가 깨졌거나 일부 shard가 빠져 있으면 전체 로딩이 실패할 수 있습니다.

따라서 노드를 늘릴 때는 "GPU 총합"만 보지 말고 "가장 느린 링크, 가장 느린 디스크, 가장 불안정한 노드"를 함께 봐야 합니다. 클러스터는 평균 성능이 아니라 약한 고리의 성능으로 움직입니다.

head/worker 구조와 호스트 파일

멀티 노드 클러스터를 운영하려면 노드 목록을 명확히 관리해야 합니다. Ray에서는 head 주소와 worker 조인이 핵심이고, MPI나 torchrun 계열에서는 hostfile이 핵심입니다.

간단한 hostfile은 다음처럼 생겼습니다.

node-a slots=8
node-b slots=8
node-c slots=8
node-d slots=8

여기서 slots=8은 해당 노드에서 8개 프로세스, 보통 8개 GPU에 해당하는 rank를 실행할 수 있다는 의미입니다. mpirun은 이 정보를 보고 전체 world size를 구성합니다.

Ray 방식에서는 hostfile을 직접 쓰지 않더라도 같은 정보가 필요합니다. 어떤 노드가 head인지, 어떤 노드가 worker인지, 각 노드의 프라이빗 IP가 무엇인지, GPU가 몇 개인지, 해당 노드가 실제로 Ray cluster에 붙었는지를 확인해야 합니다.

ray status
ray list nodes

이 명령으로 기대한 노드 수와 GPU 수가 보이지 않으면, 아직 LLM 서버를 띄울 단계가 아닙니다. 먼저 클러스터 자원 인식부터 맞춰야 합니다. 분산 추론에서 가장 위험한 습관은 "일단 serve를 실행해 보고 에러를 보자"입니다. 모델 로딩에 수십 분이 걸리는 환경에서는 순서가 중요합니다. 먼저 네트워크, 다음 Ray, 다음 NCCL, 다음 모델 파일, 마지막이 추론 서버입니다.

프라이빗 네트워크와 방화벽

멀티 노드 GPU 클러스터는 반드시 프라이빗 네트워크를 기준으로 설계하는 것이 좋습니다. 노드 간 통신은 외부 인터넷을 거칠 이유가 없습니다. 지연 시간이 커지고 보안 위험도 늘어납니다.

클라우드에서는 같은 VPC 또는 VNet 안에 GPU 노드를 배치하고, 보안 그룹에서 필요한 포트만 엽니다. 온프레미스에서는 관리망과 데이터망을 분리하는 것이 좋습니다. SSH와 모니터링은 관리망, NCCL/Ray/스토리지 트래픽은 고속 데이터망 또는 IB/RoCE망을 사용합니다.

방화벽에서 고려해야 할 것은 단지 API 포트 8000번이 아닙니다. Ray head 포트, worker 통신 포트, object manager 포트, dashboard 포트, NCCL이 사용하는 동적 포트, SSH, 스토리지 마운트 포트가 얽힙니다. 보안상 모든 포트를 열 수 없다면, Ray와 NCCL의 포트 범위를 명시적으로 제한하고 그 범위만 열어야 합니다.

또 하나의 흔한 실수는 head 노드 주소에 공인 IP를 넣는 것입니다. worker 노드들이 공인 IP로 head에 붙을 수는 있어도, 경로가 NAT나 방화벽을 돌아가면서 지연 시간이 커지거나 연결이 끊길 수 있습니다. head 주소는 모든 worker가 직접 접근 가능한 프라이빗 IP를 사용해야 합니다. vLLM 문서도 head node IP는 worker가 접근 가능한 주소여야 하며, 각 worker의 VLLM_HOST_IP를 올바르게 지정해야 한다고 설명합니다.3

Kubernetes로 올릴 때 달라지는 것

Kubernetes를 사용하면 배포, 재시작, 스케줄링, 모니터링, secret 관리가 편해집니다. 하지만 LLM 모델 병렬 추론은 일반 웹 서비스보다 훨씬 상태ful합니다. 아무 GPU pod나 하나 더 띄운다고 모델 병렬 그룹이 자동으로 확장되는 것은 아닙니다.

Kubernetes에서 멀티 노드 LLM을 운영하려면 GPU 노드 라벨링, taint/toleration, NVIDIA Device Plugin, GPU Feature Discovery, 모델 저장소, secret, pull secret, 네트워크 정책을 모두 준비해야 합니다. NVIDIA Triton과 TensorRT-LLM의 멀티 노드 Kubernetes 가이드는 GPU 노드에 nvidia.com/gpu=present 같은 라벨과 taint를 설정하고, NVIDIA Device Plugin과 GPU Feature Discovery를 설치해 GPU 자원을 Kubernetes가 인식하도록 구성합니다.6

Kubernetes의 장점은 반복 가능성입니다. 한 번 YAML과 Helm chart를 잘 만들어 두면 같은 구성을 여러 환경에 재현할 수 있습니다. 또한 Persistent Volume을 통해 여러 pod가 같은 모델 파일이나 변환된 엔진 파일을 공유하도록 만들 수 있습니다.

하지만 주의할 점도 있습니다. 일반 Deployment는 pod 이름과 순서가 안정적이지 않을 수 있습니다. 모델 병렬 rank가 안정적인 네트워크 ID를 필요로 한다면 StatefulSet이나 전용 오케스트레이터가 더 적합할 수 있습니다. 또한 Kubernetes Service를 통한 로드밸런싱은 모델 병렬 그룹 내부 통신에는 적합하지 않을 수 있습니다. NCCL과 Ray가 실제 pod IP, host networking, CNI 성능에 민감하기 때문입니다.

즉 Kubernetes는 멀티 노드 LLM을 쉽게 만들어 주는 마법이 아니라, 복잡한 구성을 선언적으로 관리하게 해 주는 도구입니다. 기본 네트워크와 NCCL이 불안정한 상태에서 Kubernetes를 얹으면 문제는 사라지지 않고 더 추적하기 어려워집니다.

배포 전 점검 체크리스트

멀티 노드 LLM 추론을 띄우기 전에는 아래 항목을 하나씩 확인하는 것이 좋습니다. 이 목록은 귀찮아 보이지만, 405B 모델이 30분 동안 로딩되다가 마지막에 실패하는 경험을 한 번 하고 나면 절대 귀찮지 않게 느껴집니다.

하드웨어와 네트워크

  • 모든 노드의 GPU 모델, VRAM 크기, GPU 개수가 의도한 대로 같은지 확인한다.

  • nvidia-smi topo -m으로 노드 내부 GPU 연결 구조를 확인한다.

  • InfiniBand 또는 RoCE 장비가 OS와 컨테이너 안에서 보이는지 확인한다.

  • 노드 간 대역폭과 지연 시간을 iperf3, ib_write_bw, nccl-tests 등으로 측정한다.

  • NCCL 로그에서 NET/Socket인지 NET/IB/GDRDMA인지 확인한다.

운영체제와 런타임

  • NVIDIA 드라이버, CUDA, NCCL, PyTorch, vLLM 버전을 모든 노드에서 통일한다.

  • 컨테이너 이미지는 태그뿐 아니라 digest까지 고정하는 것이 좋다.

  • 컨테이너 런타임이 GPU와 IB 장치를 노출하는지 확인한다.

  • /dev/infiniband, GPU device, shared memory 설정이 컨테이너에 제대로 들어오는지 확인한다.

  • --ipc=host, --privileged, CAP_SYS_ADMIN 같은 권한이 필요한지 사전에 검토한다.

모델과 저장소

  • 모델 파일이 모든 노드에서 같은 경로로 보이는지 확인한다.

  • 토크나이저 파일, config 파일, 양자화 메타데이터, shard 파일 누락 여부를 확인한다.

  • 공유 스토리지 사용 시 동시 읽기 성능을 측정한다.

  • 로컬 NVMe 캐시 사용 시 체크섬 또는 파일 크기 검증 절차를 둔다.

  • Hugging Face token은 secret으로 관리하고, 컨테이너 로그에 노출되지 않게 한다.

클러스터 구성

  • Ray head IP는 프라이빗 네트워크 주소를 사용한다.

  • worker 노드가 head 노드에 붙은 뒤 ray status에서 GPU 수가 맞는지 확인한다.

  • hostfile 또는 노드 목록을 Git 등으로 버전 관리한다.

  • 방화벽과 보안 그룹에서 Ray, NCCL, SSH, API 포트를 명확히 허용한다.

  • 노드 이름, IP, GPU 수, 역할을 표로 관리한다.

추론 서버 설정

  • TP 크기는 우선 노드당 GPU 수로 잡는다.

  • PP 크기는 우선 노드 수로 잡는다.

  • gpu_memory_utilization을 너무 공격적으로 올리지 않는다.

  • 긴 컨텍스트와 동시 요청을 고려해 KV 캐시 여유를 남긴다.

  • 첫 배포에서는 작은 max_model_len, 낮은 동시성으로 시작해 점진적으로 올린다.

처음부터 거대한 클러스터로 가지 말 것

멀티 노드 추론은 매력적입니다. 2개 노드가 성공하면 4개 노드도 될 것 같고, 4개가 되면 16개도 가능해 보입니다. 하지만 좋은 운영자는 GPU를 많이 쓰는 사람이 아니라, 실패 범위를 작게 유지하면서 확장하는 사람입니다.

가장 좋은 시작 순서는 이렇습니다. 먼저 단일 노드에서 같은 컨테이너와 같은 모델 경로로 vLLM을 띄웁니다. 그다음 2개 노드에서 Ray 클러스터만 구성해 봅니다. 다음으로 NCCL 테스트를 돌립니다. 그다음 작은 모델로 멀티 노드 serve를 확인합니다. 마지막으로 목표 모델을 올립니다.

405B 모델을 처음부터 올려서 문제를 찾으려 하면, 매번 로딩 시간이 디버깅 시간을 잡아먹습니다. 작은 모델로 네트워크와 런타임을 검증하고, 큰 모델로 메모리와 성능을 검증해야 합니다.

또한 멀티 노드는 성능 최적화보다 운영 안정성이 먼저입니다. 모델이 겨우 올라가는 구성이 아니라, 재시작해도 다시 올라오는 구성이 필요합니다. 한 번 성공한 명령어가 아니라, 팀 전체가 재현할 수 있는 배포 절차가 필요합니다. 노트북에 적힌 임시 명령어가 아니라, 컨테이너 이미지, 환경 변수, 모델 경로, 포트, 노드 목록이 모두 기록된 운영 문서가 필요합니다.

멀티 노드 추론의 현실적인 목표

멀티 노드 GPU 클러스터는 단일 서버의 한계를 넘게 해 줍니다. 하지만 모든 문제를 해결해 주지는 않습니다. 노드를 늘리면 모델은 들어갑니다. 그러나 통신 오버헤드, pipeline bubble, 장애 전파, 재시작 시간, 스토리지 병목도 함께 따라옵니다.

그래서 목표를 분명히 해야 합니다.

모델이 단일 노드에 들어가지 않는다면, 멀티 노드의 첫 목표는 "올리는 것"입니다. 이때는 TP를 노드 내부에 두고 PP를 노드 간에 두는 기본 구성이 좋습니다. 모델이 올라간 뒤에는 "얼마나 많은 요청을 안정적으로 처리할 것인가"가 목표가 됩니다. 이때는 KV 캐시, 배치, prefill/decode 비율, 라우팅, 오토스케일링을 봐야 합니다. 마지막으로 "장애가 나도 얼마나 빨리 복구할 것인가"가 운영 목표가 됩니다.

멀티 노드 LLM 인프라는 한 번에 완성되지 않습니다. 작은 Ray 클러스터에서 시작해, NCCL 로그를 읽고, 모델 파일 동기화를 자동화하고, 네트워크 경로를 검증하고, 재시작 절차를 줄이는 식으로 성숙해집니다.

이제 우리는 단일 서버를 넘어섰습니다. 다음 장에서는 조금 다른 방향의 확장을 살펴봅니다. 거대한 데이터센터 GPU가 아니라, 여러 대의 Mac mini와 Apple Silicon 장비를 묶는 방식입니다. 성능의 왕도는 아니지만, 전력 효율과 통합 메모리, 조용한 운영이라는 독특한 장점이 있는 세계입니다.

참고

1MeluXina vLLM 멀티 노드 실습

2NVIDIA KV 캐시 오프로드 설명

3vLLM 분산 서빙 문서

4Azure AKS와 NVIDIA Dynamo 사례

5MPI 기반 멀티 노드 LLM 추론 예제

6NVIDIA Triton TensorRT-LLM 멀티 노드 배포 가이드

7멀티 노드 LLM 추론 all-reduce 병목 연구

책 목록

VRAM의 벽을 넘는 멀티 GPU LLM 운영 가이드

전체 목차 보기

이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.

틸노트는 사용자가 AI를 활용해 노트를 쉽게 작성하거나 편집할 수 있는 서비스입니다.

AI 스퀘어 단톡방에 함께해요

AI 활용과 틸노트 사용 팁을 나누는 오픈채팅방입니다. 입장 비밀번호는 tilnote입니다.

참여하기