GPUStack v2.1.1 멀티 노드 추론이 시작되지 않는 문제를 모두 해결한 이야기

GPUStack v2.1.1 멀티 노드 추론이 시작되지 않는 문제를 모두 해결한 이야기

5 min read

Tech Blog
GPUStack
Multi-Node vLLM
Tesla V100
Troubleshooting

GPU 클러스터 운영 메모

GPUStack v2.1.1 멀티노드 추론이 시작되지 않는 문제를 모두 해결한 이야기
2노드 4×V100으로 Qwen2.5-32B를 실행하기까지의 전체 기록 #

GPUStack v2.1.1 + vLLM 백엔드로 2대의 GPU 워커(각 Tesla V100-PCIE-32GB × 2장)를 사용한 멀티노드 분산 추론을 시도했을 때 연이어 다양한 문제가 발생했습니다. Ray 클러스터 연결 실패, Gloo 통신 오류, /dev/shm 부족, V100 고유의 CUDA 커널 오류까지, 모든 트러블과 해결 방법을 시간 순서대로 기록합니다.

GPU-Worker-A: / Tesla V100×2 | GPU-Worker-B: / Tesla V100×2 | GPU-Server: GPUStack Server | GPUStack v2.1.1 / vLLM 0.17.1 / CUDA 12.9

구성 개요 #

관리 노드(GPU-Server)가 GPUStack Server를 담당하고, 추론은 GPU 워커 2대에 분산됩니다. 모델은 NFS 공유 /models에 저장되어 있습니다. Qwen3.5-35B-A3B(VLM)에서 시작하여 최종적으로 Qwen2.5-32B-Instruct로 안정 동작을 확인했습니다.

타겟 모델 #

Qwen2.5-32B-Instruct(float16)
tensor-parallel×2 / pipeline-parallel×2

GPU 구성 #

Tesla V100-PCIE-32GB × 4장
(2노드 × 2장)
compute capability 7.0

최종 결과 #

16./s로 안정 가동
max-model-len: 8192
해결 방법 적용 완료

발생한 문제와 해결의 전체 개요 #

문제 1

Ray Placement Group이 무한 대기 상태가 됨 #

모델을 시작하면 Waiting for creating a placement group이 10초 → 30초 → 150초 → 630초로 계속 지속되어 모델이 시작되지 않습니다.

WARNING: Tensor parallel size (4) exceeds available GPUs (2).
INFO: Waiting for creating a placement group of specs for 630 seconds.
specs=[{'node:': 0.001, 'GPU': 1.0}, {'GPU': 1.0}, {'GPU': 1.0}, {'GPU': 1.0}]

원인:Ray는 헤드 노드 측 컨테이너 내에서만 동작하고 있으며, 다른 워커 노드의 GPU가 Ray 클러스터에 참여하지 못하고 있었습니다.

확인 명령:vllm runner 컨테이너 내에서 ray status --address=<HEAD_IP>:41000를 실행합니다.
Ray 버전 불일치 오류(2.54.0 vs 2.48.0)가 발생하면 GPUStack 이미지 버전 불일치가 의심됩니다.

문제 2

/etc/hosts의 127.0.1.1 항목으로 인한 Gloo 통신 실패 #

Ray 노드 간에는 연결되어 있지만, 모델 로드 후 분산 환경 초기화(Gloo의 connectFullMesh)에서 실패합니다.

(RayWorkerWrapper pid=1722) ERROR failed to connect, retry=4, retryLimit=3,
  local=[127.0.0.1]:35509, remote=[127.0.1.1]:51638$1,
  error=SO_ERROR: Connection refused

RuntimeError: Gloo connectFullMesh failed with timed out connecting:
  SO_ERROR: Connection refused, remote=[127.0.1.1]:51638$1

원인:Ubuntu/Debian의 cloud-init이 기본적으로 /etc/hosts127.0.1.1 호스트명을 기록합니다. Gloo는 이 항목을 참조하여 자신의 노드 IP를 127.0.1.1(루프백)로 판단하고, 다른 노드에 연결하려고 할 때 거부됩니다.

해결 방법:각 노드의 /etc/hosts에서 127.0.1.1을 실제 IP 주소로 수정합니다.

# GPU-Worker-A에서
sudo sed -i 's/127.0.1.1\s*GPU-Worker-A/ GPU-Worker-A/' /etc/hosts

# GPU-Worker-B에서
sudo sed -i 's/127.0.1.1\s*GPU-Worker-B/ GPU-Worker-B/' /etc/hosts
참고:본 환경에서는 cloud-init(manage_etc_hosts=True)가 자동으로 실제 IP를 설정했기 때문에 수정이 불필요했다. runner 컨테이너 내의 /etc/hosts를 직접 확인하여 127.0.1.1이 존재하지 않는지 확인한다.

문제 3

Qwen3.5-35B-A3B의 Visual Encoder가 V100에서 동작하지 않음 #

Gloo 문제 해결 후, 모델 로드는 성공하지만 profile_run 단계에서 즉시 크래시 발생.

File "vllm/model_executor/layers/conv.py", line 236, in _forward_conv
    x = F.conv3d(
        ^^^^^^^^^
RuntimeError: GET was unable to find an engine to execute this computation

원인:Qwen3.5-35B-A3B는 Vision Language Model이며, Visual Encoder의 Conv3D가 V100(compute capability 7.0)용 cuDNN 알고리즘을 지원하지 않는다. VLLM_DISABLE_MULTIMODAL=1 등의 환경 변수로는 회피 불가능. vLLM이 profile_run 시 반드시 Visual Encoder를 초기화하기 때문에 구조적으로 동작하지 않는다.

해결책:모델을 Qwen2.5-32B-Instruct(텍스트 전용)로 전환한다. Visual Encoder가 없기 때문에 이 문제가 발생하지 않는다.

문제 4

채팅 시 /dev/shm 부족으로 Raylet 크래시 발생 #

모델이 시작되고 채팅을 전송하는 순간 엔진이 다운된다.

WARNING (raylet) store_runner.cc:83: System memory request exceeds memory available in /dev/shm.
  The request is for 10200547328 bytes, and the amount available is 9663676416 bytes.
  If you are inside a Docker container, you may need to pass an argument with the flag '--shm-size'

CUDA error: no kernel image is available for execution on the device

원인:Docker의 기본 /dev/shm 크기는 64MB. Ray의 pipeline_parallel은 노드 간 통신에 대량의 shared memory를 사용하므로 추론 시 고갈된다. gpustack-worker 컨테이너에 --shm-size를 지정하지 않았다.

해결책:gpustack-worker 컨테이너를 --shm-size=16g 옵션과 함께 재시작한다.

docker stop gpustack-worker && docker rm gpustack-worker

docker run -d --name gpustack-worker \
  --restart=unless-stopped \
  --privileged \
  --network=host \
  --shm-size=16g \
  --volume /var/run/docker.sock:/var/run/docker.sock \
  --volume /models:/models \
  --volume /var/lib/gpustack-data:/var/lib/gpustack \
  --runtime nvidia \
  gpustack/gpustack:v2.1.1 \
  --server-url http://<SERVER_IP> \
  --token <TOKEN> \
  --cache-dir /models
주의:GPUStack v2.1.1의 「Mirrored Deployment」 기능은 gpustack-worker의 설정을 runner 컨테이너에 전달하지만, --shm-size는 전달되지 않는다. 이는 GPUStack 측의 문제이며, 후술할 환경 변수로 보완할 필요가 있다.

문제 5

runner 컨테이너 측의 shm이 전달되지 않아 RayChannelTimeoutError 지속 #

gpustack-worker 재시작 후에도 채팅 시 다운된다.

ray.exceptions.RayChannelTimeoutError: System error:
  If the execution is expected to take a long time,
  increase RAY_CGRAPH_get_timeout which is currently 300 seconds.
  Otherwise, this may indicate that the execution is hanging.

원인:vllm/ray의 runner 컨테이너는 GPUStack이 매번 새로 생성하기 때문에 gpustack-worker의 --shm-size 설정은 컨테이너 간에 전달되지 않는다. runner 컨테이너 자체의 shm이 64MB로 유지된다.

해결책:GPUStack UI의 모델 설정 → Environment Variables에 다음을 추가한다.

RAY_CGRAPH_get_timeout=600
RAY_memory_store_capacity=17179869184

문제 6

generation_config의 repetition_penalty 커널이 V100 미지원으로 반복 출력 발생 #

채팅에서 텍스트가 정상적으로 출력되지 않고, 기호(♡♡♡…)가 무한 루프됩니다. 또는 추론 중에 CUDA 커널 오류로 중단됩니다.

torch.AcceleratorError: CUDA error: no kernel image is available for execution on the device
# ↑ apply_penalties (repetition_penalty) 실행 시 발생

WARNING: Default vLLM sampling parameters have been overridden by the model's
  generation_config.json: {'repetition_penalty': 1.05, 'temperature': 0.7, ...}

원인:Qwen2.5의 generation_config.json에 설정된 repetition_penalty의 CUDA 커널이 V100용으로 컴파일되지 않았습니다.

해결 방법:GPUStack UI의 모델 설정 → Backend Parameters에 추가:

--generation-config vllm

이렇게 하면 모델의 generation_config.json을 무시하고 vLLM의 기본 설정을 사용합니다.

최종 안정 동작 설정 요약 #

gpustack-worker 시작 명령어(양쪽 노드 공통) #

docker run -d --name gpustack-worker \
  --restart=unless-stopped \
  --privileged \
  --network=host \
  --shm-size=16g \
  --volume /var/run/docker.sock:/var/run/docker.sock \
  --volume /models:/models \
  --volume /var/lib/gpustack-data:/var/lib/gpustack \
  --runtime nvidia \
  gpustack/gpustack:v2.1.1 \
  --server-url http://<SERVER_IP> \
  --token <TOKEN> \
  --cache-dir /models

GPUStack UI 모델 설정 #

Backend Parameters:

--tensor-parallel-size 2
--pipeline-parallel-size 2
--distributed-executor-backend ray
--max-model-len 8192
--enforce-eager
--generation-config vllm

Environment Variables:

RAY_CGRAPH_get_timeout=600
RAY_memory_store_capacity=17179869184

동작 확인 결과 #

Qwen2.5-32B-Instruct(float16)가 4×Tesla V100-PCIE-32GB(2노드 구성)에서 16./s로 안정 가동. VRAM 사용률은 각 GPU 93~96%. max-model-len은 8192 토큰.

V100(compute capability 7.0) 고유 제한 사항 #

❌ 동작하지 않는 것 #

  • Flash Attention 2(compute 8.0 이상 필수)→ Triton ATTN으로 폴백
  • bfloat16(→ float16으로 자동 변환)
  • Custom AllReduce(NVLink P2P 미지원)
  • SymmMemCommunicator
  • VLM의 Conv3D(Qwen3.5-35B-A3B 등)
  • generation_config의 repetition_penalty 커널

✅ 동작하는 것 #

  • Triton ATTN 백엔드(Flash Attention 대체)
  • NCCL 통신(nccl==2.27.5)
  • Ray 분산 실행(pipeline + tensor parallel)
  • torch.compile / CUDA Graphs(enforce-eager로 비활성화 권장)
  • Qwen2.5 등 텍스트 전용 모델의 추론

문제 해결 체크리스트 #

  • ray status(컨테이너 내)로 모든 노드의 GPU가 인식되는지 확인합니다
  • 각 노드의 /etc/hosts에서 호스트명이 127.0.1.1이 아닌 실제 IP로 해석되는지 확인합니다
  • gpustack-worker 컨테이너에 --shm-size=16g 이상을 지정합니다
  • 모델 설정의 Environment Variables에 RAY_CGRAPH_get_timeout=600RAY_memory_store_capacity=17179869184를 추가합니다
  • V100 환경에서는 --generation-config vllm을 Backend Parameters에 추가하여 generation_config.json을 무시합니다
  • VLM(Vision Language Model)은 V100에서 동작하지 않습니다. 텍스트 전용 모델을 사용합니다
  • V100에서는 --enforce-eager 추가를 고려합니다(CUDA Graph 관련 불안정성 회피)
  • VRAM이 부족할 경우 --gpu-memory-utilization 0.85로 KV 캐시 확보량을 조정합니다

참고 정보 #

본 기사는 실제 환경에서의 트러블슈팅 기록입니다. IP·토큰 등은 추상화되어 있습니다. V100 이외의 GPU 환경에서는 일부 문제가 발생하지 않을 수 있습니다. vLLM 및 GPUStack의 버전 업데이트에 따라 상황이 달라질 수 있습니다.

Updated on 2026年6月9日

What are your feelings

  • Happy
  • Normal
  • Sad