데이터 병렬 배포

데이터 병렬 배포 (Data Parallel Deployment)

모델이 GPU 하나에 다 안 들어가는 큰 경우, 모델을 여러 GPU에 쪼개 담는 게 일반적이에요. 그런데 모델이 충분히 작아서 GPU 하나에 들어간다면, 오히려 여러 GPU에 모델 가중치를 복제하고 각 GPU가 서로 다른 배치의 요청을 독립적으로 처리하게 하는 게 처리량 측면에서 유리할 수 있어요. 이게 데이터 병렬(Data Parallel) 배포예요. 이 페이지에서 자세히 알아볼게요.

출처: vLLM 공식 문서 — serving/data_parallel_deployment

vLLM은 데이터 병렬 배포를 지원해요. 모델 가중치를 별도의 인스턴스/GPU들에 복제하고, 요청의 독립적인 배치를 처리하게 하는 방식이에요.

이 방식은 dense 모델과 MoE 모델 모두에서 작동해요.

MoE 모델, 특히 DeepSeek처럼 MLA(Multi-head Latent Attention)를 쓰는 모델에서는, 어텐션 레이어에는 데이터 병렬을, 전문가 레이어에는 expert 병렬이나 tensor 병렬(EP 또는 TP)을 쓰는 게 유리할 수 있어요.

이런 경우 데이터 병렬 rank들은 완전히 독립적이지 않아요. forward pass가 정렬돼야 하고, DP rank 수보다 처리할 요청이 적어도 모든 rank의 전문가 레이어가 매 forward pass마다 동기화해야 해요.

기본적으로 전문가 레이어는 크기 DP × TP의 tensor parallel 그룹을 형성해요. 대신 expert 병렬을 쓰려면 (다중 노드의 경우 모든 노드에서) --enable-expert-parallel CLI 인자를 포함하세요. EP를 켰을 때 어텐션과 전문가 레이어가 어떻게 다르게 동작하는지는 Expert Parallel 배포를 참고하세요.

vLLM에서 각 DP rank는 별도의 "core engine" 프로세스로 배포되고, ZMQ 소켓을 통해 프론트엔드 프로세스와 통신해요. 데이터 병렬 어텐션은 tensor 병렬 어텐션과 결합할 수 있고, 그 경우 각 DP 엔진은 설정된 TP 크기만큼의 per-GPU worker 프로세스를 소유해요.

MoE 모델의 경우 어떤 rank에서든 진행 중인 요청이 있으면, 현재 요청이 스케줄되지 않은 모든 rank에서 빈 "dummy" forward pass를 수행해야 해요. 이는 모든 rank와 통신하는 별도의 DP Coordinator 프로세스와, 모든 rank가 유휴 상태가 되어 일시정지될 수 있는 시점을 판단하는 N 스텝마다 수행되는 collective 연산으로 처리돼요. TP를 DP와 함께 쓰면 전문가 레이어는 크기 DP × TP의 그룹을 형성해요 (기본은 tensor 병렬, --enable-expert-parallel이면 expert 병렬).

모든 경우에 DP rank 사이의 요청 부하 균형을 맞추는 게 좋아요. 온라인 배포에서는 각 DP 엔진의 상태(특히 현재 스케줄된·대기(큐) 중인 요청, KV cache 상태)를 고려해 균형을 최적화할 수 있어요. 각 DP 엔진은 독립적인 KV cache를 갖고 있고, 프롬프트를 지능적으로 라우팅하면 prefix caching의 이점을 최대화할 수 있어요.

이 문서는 온라인 배포(API 서버 포함)에 초점을 맞춰요. DP + EP는 오프라인 사용(LLM 클래스)도 지원해요. 예시는 examples/features/data_parallel/data_parallel_offline.py를 참고하세요.

온라인 배포에는 두 가지 방식이 있어요. 내부 로드 밸런싱이 있는 자체 포함(self-contained) 방식, 또는 외부에서 rank별 프로세스 배포와 로드 밸런싱을 하는 방식.

내부 로드 밸런싱 (Internal Load Balancing)

vLLM은 단일 API 엔드포인트를 노출하는 "자체 포함" 데이터 병렬 배포를 지원해요.

vllm serve 명령줄 인자에 --data-parallel-size=4 같은 걸 넣기만 하면 설정돼요. 4개의 GPU가 필요해요. tensor 병렬과 결합할 수도 있어요. 예를 들어 --data-parallel-size=4 --tensor-parallel-size=2는 8개의 GPU가 필요해요. DP 배포 크기를 정할 때, --max-num-seqsDP rank마다 적용된다는 걸 기억하세요.

반대로 admission control 한도인 --max-num-queued-reqs--max-num-queued-tokens는 DP rank마다가 아니라 전체 서버에 적용돼요. 각 API 서버 프로세스는 자기가 라우팅하는 모든 DP rank에 걸쳐 in-flight 요청과 prefill 백로그를 계산해요. 예를 들어 --data-parallel-size=4 --max-num-seqs=256 --max-num-queued-reqs=256이면, rank들이 합쳐서 1024개를 실행할 수 있는데도 총 in-flight가 256개가 되면 새 요청을 거부해요. rank들을 포화 상태로 유지하려면 한도를 대략 data-parallel-size * max-num-seqs에 원하는 큐 깊이를 더한 값으로 잡으세요.

단일 데이터 병렬 배포를 여러 노드에 걸쳐 실행하려면 각 노드에서 서로 다른 vllm serve를 실행하고, 해당 노드에서 실행할 DP rank를 지정해야 해요. 이 경우에도 HTTP 엔트리포인트는 하나예요. API 서버는 한 노드에서만 실행되지만, DP rank와 같은 곳에 있을 필요는 없어요.

이건 8개 GPU 한 노드에서 DP=4, TP=2로 실행하는 예시예요.

vllm serve $MODEL --data-parallel-size 4 --tensor-parallel-size 2

이건 DP=4를 head 노드에서는 rank 0,1로, 두 번째 노드에서는 rank 2,3으로 실행하는 예시예요.

# Node 0  (with ip address 10.99.48.128)
vllm serve $MODEL --data-parallel-size 4 --data-parallel-size-local 2 \
                  --data-parallel-address 10.99.48.128 --data-parallel-rpc-port 13345
# Node 1
vllm serve $MODEL --headless --data-parallel-size 4 --data-parallel-size-local 2 \
                  --data-parallel-start-rank 2 \
                  --data-parallel-address 10.99.48.128 --data-parallel-rpc-port 13345

이건 첫 번째 노드에는 API 서버만, 두 번째 노드에는 모든 엔진을 두어 DP=4를 실행하는 예시예요.

# Node 0  (with ip address 10.99.48.128)
vllm serve $MODEL --data-parallel-size 4 --data-parallel-size-local 0 \
                  --data-parallel-address 10.99.48.128 --data-parallel-rpc-port 13345
# Node 1
vllm serve $MODEL --headless --data-parallel-size 4 --data-parallel-size-local 4 \
                  --data-parallel-address 10.99.48.128 --data-parallel-rpc-port 13345

이 DP 방식은 --data-parallel-backend=ray를 지정해 Ray와 함께 쓸 수도 있어요.

vllm serve $MODEL --data-parallel-size 4 --data-parallel-size-local 2 \
                  --data-parallel-backend=ray

Ray를 쓸 때 몇 가지 눈에 띄는 차이가 있어요.

  • (어느 노드에서든) 단일 실행 명령으로 모든 로컬·원격 DP rank를 시작하므로, 각 노드에서 실행하는 것보다 편리해요.
  • --data-parallel-address를 지정할 필요가 없어요. 명령을 실행한 노드가 --data-parallel-address로 사용돼요.
  • --data-parallel-rpc-port를 지정할 필요가 없어요.
  • 단일 DP 그룹이 여러 노드를 필요로 할 때(예: 모델 레플리카 하나가 최소 2개 노드에서 실행돼야 할 때) VLLM_RAY_DP_PACK_STRATEGY="span"을 설정하세요. 그러면 --data-parallel-size-local은 무시되고 자동 결정돼요.
  • 원격 DP rank는 Ray 클러스터의 노드 리소스에 따라 할당돼요.

현재 내부 DP 로드 밸런싱은 API 서버 프로세스 내에서, 각 엔진의 실행·대기 큐를 기반으로 수행돼요. 향후 KV cache 인식 로직을 통합해 더 정교해질 수 있어요.

이 방식으로 큰 DP 크기를 배포하면 API 서버 프로세스가 병목이 될 수 있어요. 이 때는 직교하는 --api-server-count 명령줄 옵션으로 확장할 수 있어요 (예: --api-server-count=4). 이는 사용자에게 투명해요. 단일 HTTP 엔드포인트/포트가 여전히 노출되거든요. 이 API 서버 확장은 "내부적"이며 여전히 "head" 노드에 국한된다는 점에 주의하세요.

하이브리드 로드 밸런싱 (Hybrid Load Balancing)

하이브리드 로드 밸런싱은 내부와 외부 방식 사이에 있어요. 각 노드는 같은 노드에 있는 데이터 병렬 엔진에만 요청을 큐잉하는 자체 API 서버를 실행해요. 업스트림 로드 밸런서(예: ingress controller나 트래픽 라우터)가 사용자 요청을 각 per-node 엔드포인트로 분산해요.

이 방식은 여전히 모든 노드를 전역 데이터 병렬 크기로 실행하면서 --data-parallel-hybrid-lb로 활성화해요. 내부 로드 밸런싱과의 주요 차이는 이래요.

  • 각 노드가 자기가 소유한 rank를 알 수 있도록 --data-parallel-size-local--data-parallel-start-rank를 제공해야 해요.
  • 모든 노드가 API 엔드포인트를 노출하므로 --headless와 호환되지 않아요.
  • 로컬 rank 수에 따라 노드별로 --api-server-count를 조절해요.

이 구성에서 각 노드는 스케줄링 결정을 로컬로 유지하므로 크로스 노드 트래픽이 줄고, 큰 DP 크기에서 단일 노드 병목을 피할 수 있어요.

외부 로드 밸런싱 (External Load Balancing)

특히 더 큰 규모의 배포에서는 데이터 병렬 rank의 오케스트레이션과 로드 밸런싱을 외부에서 처리하는 게 말이 되는 경우가 많아요.

이 경우 각 DP rank를 별도의 vLLM 배포처럼, 자체 엔드포인트를 가진 것으로 취급하고, 외부 라우터가 그들 사이에서 HTTP 요청을 밸런싱하도록 하는 게 편리해요. 라우팅 결정을 위해 각 서버의 실시간 텔레메트리를 활용하죠.

비-MoE 모델에서는 이걸 아주 쉽게 할 수 있어요. 배포된 각 서버가 완전히 독립적이거든요. 그 경우 --data-parallel-* 인자 없이 독립적인 vLLM 인스턴스를 실행하면 돼요. 외부 DP CLI 옵션은 MoE 배포에만 지원돼요.

vLLM은 MoE DP+EP에 대해 동일한 토폴로지를 지원하고, 다음 CLI 인자로 설정할 수 있어요.

DP rank가 같은 위치(같은 노드/ip 주소)에 있으면 기본 RPC 포트를 쓰지만, 각 rank마다 다른 HTTP 서버 포트를 지정해야 해요.

# Rank 0
CUDA_VISIBLE_DEVICES=0 vllm serve $MODEL --data-parallel-size 2 --data-parallel-rank 0 \
                                         --port 8000
# Rank 1
CUDA_VISIBLE_DEVICES=1 vllm serve $MODEL --data-parallel-size 2 --data-parallel-rank 1 \
                                         --port 8001

다중 노드의 경우 rank 0의 주소/포트도 지정해야 해요.

# Rank 0  (with ip address 10.99.48.128)
vllm serve $MODEL --data-parallel-size 2 --data-parallel-rank 0 \
                  --data-parallel-address 10.99.48.128 --data-parallel-rpc-port 13345
# Rank 1
vllm serve $MODEL --data-parallel-size 2 --data-parallel-rank 1 \
                  --data-parallel-address 10.99.48.128 --data-parallel-rpc-port 13345

이 시나리오에서도 coordinator 프로세스가 DP rank 0 엔진과 같은 위치에서 실행돼요.

다이어그램에서 점선 박스 각각은 vllm serve의 개별 실행에 해당해요. 예를 들어 별도의 Kubernetes pod일 수 있어요.

더 알아보기 (Learn more)