쿠버네티스 vLLM 분산 환경 구축 및 GPU 리소스 최적화 완벽 가이드
프로덕션 환경에서 Llama 3나 Mistral과 같은 대규모 언어 모델(LLM)을 실시간 서비스로 제공하는 것은 일반적인 백엔드 API를 운영하는 것과 차원이 다른 난이도를 가집니다. 수십 기가바이트에 달하는 가중치를 로드해야 할 뿐만 아니라, 동시 요청이 몰릴 때 GPU의 KV 캐시(Key-Value Cache) 메모리가 급격하게 차오르며 속도 저하나 OOM(Out of Memory) 장애를 일으키기 쉽습니다. 실제로 초기 배포 시 단일 GPU 장비에 vLLM을 띄워 서비스하다가 동시 접속자가 조금만 늘어나도 시스템 전체가 응답 불가가 되는 문제를 많이들 겪습니다. 이러한 물리적 한계를 극복하고 동시 처리 성능(Throughput)을 높이려면 분산 환경으로 눈을 돌려야 합니다. 고성능 추론 엔진인 vLLM을 쿠버네티스(Kubernetes) 환경에서 Ray 클러스터와 결합하여 고가용성 멀티 노드 시스템을 구축하는 방안은 클라우드 비용을 통제하면서도 뛰어난 안정성을 보장합니다. 쿠버네티스 상에서 vLLM 분산 추론 환경을 직접 설계 및 배포하고, GPU 자원을 극한으로 쥐어짜는 핵심 튜닝 옵션들을 실무 코드 관점에서 단계적으로 공유합니다.
LLM 분산 서빙을 위한 vLLM 아키텍처 이해와 필요성
vLLM 분산 서빙은 단일 GPU의 물리적인 메모리 한계를 초과하는 거대 모델을 서빙하거나 대규모 사용자의 실시간 동시 추론을 지연 없이 처리하기 위한 고성능 분산 아키텍처입니다. 메모리 단편화를 제거하는 PagedAttention 기술을 기반으로 동작하여 동시 요청이 많아져도 지연 시간(Latency)을 최소화합니다.
단일 A100(80GB) GPU로도 70B(700억 파라미터) 이상의 대형 모델을 FP16 정밀도로 구동하는 것은 불가능에 가깝습니다. 모델 가중치만 140GB를 초과하기 때문입니다. 이때 텐서 병렬 처리(Tensor Parallelism, TP) 기술을 사용하여 모델 가중치 자체를 여러 GPU에 쪼개어 올리는 방식이 필수적이며, vLLM은 내부적으로 Ray를 통해 다중 GPU 간의 통신과 연산을 조율합니다. 분산 서빙 환경을 도입하면 자원 분산뿐만 아니라 전체 처리 속도를 대폭 개선할 수 있습니다.
vLLM은 요청의 토큰 생성 과정에서 동적으로 필요한 메모리를 페이지 단위로 나누어 관리합니다. 운영체제의 가상 메모리 관리 기법과 매우 유사하게, KV 캐시 데이터를 불연속적인 메모리 블록에 매핑하여 낭비되는 메모리 조각(External/Internal Fragmentation)을 거의 제로에 가깝게 줄여줍니다. 이를 분산 아키텍처에 확장 탑재함으로써 대형 프로덕션 환경에서도 리소스 효율을 유지한 채 빠르고 고가용성 있는 모델 서빙을 구현할 수 있습니다.
쿠버네티스 환경에서 Ray 클러스터와 vLLM 통합 설계
쿠버네티스 환경에서 vLLM을 분산 배치하기 위해서는 Ray Operator(KubeRay)를 활용해 헤드(Head) 노드와 워커(Worker) 노드 간의 유기적인 통신망을 설계하는 작업이 핵심입니다. KubeRay는 쿠버네티스 네이티브로 Ray 클러스터의 생성, 스케일링, 복구를 자동화하는 역할을 담당합니다.
다음 아키텍처는 쿠버네티스 위에 구성되는 분산 vLLM 시스템의 데이터 흐름과 노드 배치 구조를 명확히 보여줍니다.
graph TD
Ingress[Kubernetes Ingress] -->|Request| Service[Kubernetes Service]
Service -->|gRPC/HTTP| RayHead[Ray Head Pod]
RayHead -->|Tensor Parallelism / Orchestration| RayWorker1[Ray Worker Pod 1: GPU 0/1]
RayHead -->|Tensor Parallelism / Orchestration| RayWorker2[Ray Worker Pod 2: GPU 2/3]
RayWorker1 <-->|NCCL Communication| RayWorker2
클라이언트로부터 유입된 트래픽은 서비스와 인그레스를 거쳐 API 엔드포인트 역할을 수행하는 Ray Head 파드로 전달됩니다. Head 파드는 사용자의 프롬프트 요청을 큐잉하고, 스케줄링을 통해 분산 컴퓨팅 엔진인 Worker 파드 군에 연산을 고르게 배분합니다. 각 Worker 파드는 물리적인 GPU 디바이스에 다이렉트로 매핑되어 Tensor Parallelism 연산을 실시간으로 실행합니다. 이들 Worker 파드 간의 텐서 연산 교환은 NVIDIA의 GPU 간 초고속 통신 라이브러리인 NCCL을 통해 이루어지므로, 네트워킹 지연을 최소화하기 위한 네트워크 튜닝이 반드시 동반되어야 합니다.
vLLM GPU 메모리 최적화와 PagedAttention 파라미터 튜닝
GPU 메모리 부족으로 인한 파드 축출 및 OOM 장애를 방지하고 처리량을 늘리려면, 모델 구동 시점에 vLLM에 할당되는 PagedAttention 내부 파라미터를 하드웨어 스펙에 최적화하여 튜닝해야 합니다.
가장 주의해서 설정해야 할 매개변수는 --gpu-memory-utilization입니다. 이 매개변수는 vLLM이 모델을 로드한 후 남은 GPU 전체 메모리 중 KV 캐시 영역으로 사전 할당해 둘 크기를 0.0에서 1.0 사이의 비율로 정의합니다. 기본값은 0.90이며, 이는 남은 자원의 90%를 물리적으로 선점하여 가져가겠다는 의미입니다. 만약 동일 노드나 컨테이너에서 부가적인 로그 수집기나 메트릭 수집 스크립트가 실행 중이라면, 이 비율을 너무 높게 잡을 경우 커널 레벨에서 컨테이너 자체를 OOMKilled 시키는 참사가 발생할 수 있습니다.
쿠버네티스 환경에서는 파드의 메모리 제한 설정을 초과하면 컨테이너가 즉각 종료되는 특성이 있으므로, 실무적으로는 초기 배포 단계에서 0.80 또는 0.85로 보수적으로 설정하고 최적의 임계점을 찾는 과정을 거쳐야 합니다. 이와 관련된 구체적인 OOM 메커니즘과 일반적인 메모리 리소스 한도 설정 기법은 쿠버네티스 파드 메모리 초과 장애 해결 가이드를 통해 한층 깊이 있게 살펴보실 수 있습니다.
아래는 쿠버네티스 환경에서 vllm-serve 명령어를 수행할 때 GPU 자원을 최적화하여 전달하는 파드 정의 및 기동 스크립트의 프로덕션 레벨 YAML 설정 예시입니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: vllm-llama3-serving
namespace: llm-serving
labels:
app: vllm-inference
spec:
replicas: 1
selector:
matchLabels:
app: vllm-inference
template:
metadata:
labels:
app: vllm-inference
spec:
containers:
- name: vllm-container
image: vllm/vllm-openapi:latest
imagePullPolicy: IfNotPresent
command:
- "python3"
- "-m"
- "vllm.entrypoints.openai.api_server"
- "--model"
- "meta-llama/Meta-Llama-3-70B-Instruct"
- "--tensor-parallel-size"
- "4"
- "--gpu-memory-utilization"
- "0.85"
- "--max-model-len"
- "8192"
- "--block-size"
- "16"
resources:
limits:
nvidia.com/gpu: "4"
memory: "128Gi"
cpu: "16"
requests:
nvidia.com/gpu: "4"
memory: "96Gi"
cpu: "8"
ports:
- containerPort: 8000
name: http
volumeMounts:
- mountPath: /dev/shm
name: dshm
volumes:
- name: dshm
emptyDir:
medium: Memory
sizeLimit: "16Gi"
Triton Inference Server와 vLLM의 성능 및 구조 비교
대규모 머신러닝 인프라를 지탱하는 대표적인 서빙 엔진인 NVIDIA Triton Inference Server와 vLLM은 설계 철학부터 지향점까지 큰 차이를 보입니다. 기업의 인프라 요구 조건에 따라 적합한 엔진을 선택하는 것이 필수적입니다.
| 비교 항목 | vLLM | Triton Inference Server |
|---|---|---|
| 핵심 지향점 | LLM 단일 도메인 최적화 및 PagedAttention 극대화 | 다양한 프레임워크(PyTorch, ONNX, TensorRT) 통합 지원 |
| 추론 동시성 성능 | 매우 높음 (토큰 단위 동적 배치 및 캐싱 기능 우수) | 보통~높음 (정적/동적 배치 설정 커스터마이징 필요) |
| 배포 및 설정 편의성 | 매우 쉬움 (파이썬 스크립트 및 CLI 기반의 직관적 구동) | 어려움 (모델 리포지토리 구성 및 config.pbtxt 세부 정의 필수) |
| 메모리 오버헤드 | 적음 (KV 캐시 낭비를 제거하여 자원 효율성 극대화) | 보통 (인스턴스 설정 및 백엔드 종류에 따라 가변적) |
| 다양한 모델 혼용 | 단일 LLM 서빙에 집중 | 컴퓨터 비전, 오디오 등 멀티태스크 모델 통합 서빙 가능 |
Triton은 정통적인 엔터프라이즈 아키텍처에 강점을 가지며 오디오, 이미지 분류, 자연어 처리 등 여러 종류의 AI 모델들을 단일 인프라에서 효율적으로 오케스트레이션하고 싶을 때 최고의 만족을 줍니다. 그러나 오직 생성형 AI 및 LLM 서빙 측면의 초당 토큰 처리 속도(Tokens Per Second)와 자원 낭비 최소화 측면만 놓고 본다면 vLLM이 기술적으로 명확한 압승을 거둡니다. 특히 Llama나 Mistral처럼 구조가 정형화된 디코더 기반 모델의 경우, vLLM이 제공하는 Native PagedAttention 튜닝을 통해 최대 2~4배의 서비스 스루풋 한계 상승을 이끌어낼 수 있습니다.
분산 환경에서 자주 발생하는 트러블슈팅과 대책
쿠버네티스 분산 환경에서 다중 GPU 서빙을 운영하다 보면, 네트워크 파이프라인이나 스토리지 한계로 인해 예기치 못한 에러와 크래시가 잦은 빈도로 터져 나옵니다. 특히 분산 환경 특유의 병목 현상을 파악하지 못하면 수일간 삽질하기 십상입니다.
가장 흔하게 마주하는 첫 번째 이슈는 NCCL 통신 타임아웃에 따른 분산 학습/추론 락(Hang) 현상입니다.
NCCL WARN : [send] connection broken 또는 NCCL INFO: Call to connect returned Connection refused 같은 에러와 함께 GPU들 간에 응답을 주고받지 못해 무한 대기 상태에 빠지는 현상입니다. 이 경우 쿠버네티스의 네트워킹 스택 문제일 가능성이 큽니다. 칼리코(Calico)나 플라넬(Flannel) 같은 기본 CNI(Container Network Interface)가 다중 노드 간의 10Gbps 이상 급 고대역폭 멀티캐스트 통신을 방해하고 있을 수 있으므로, NCCL_SOCKET_IFNAME 환경 변수를 파드 내에 주입해 활성화된 네트워크 인터페이스 카드를 명확하게 선언해 줘야 합니다. 또한 프로덕션 레벨에서는 가능하면 CNI를 거치지 않고 호스트의 네트워크를 그대로 사용하는 hostNetwork: true 설정을 부여하여 지연율을 극도로 줄이는 전략도 깊이 고려할 만합니다.
두 번째 빈번한 에러는 공유 메모리(Shared Memory) 부족으로 인한 런타임 종료입니다.
PyTorch와 Ray가 분산 텐서 공유를 위해 호스트의 메모리 맵핑 장치인 /dev/shm 공간을 적극적으로 이용하기 때문에 발생합니다. 리눅스 기반 컨테이너는 기본적으로 /dev/shm 공간을 64MB라는 터무니없이 작은 크기로 제한하여 생성합니다. 이를 대비해 쿠버네티스 볼륨 마운트 시 emptyDir 볼륨의 medium: Memory를 사용해 컨테이너 안으로 고용량 공유 메모리 경로를 강제 맵핑해 주어야 크래시를 온전히 물리칠 수 있습니다.
자주 묻는 질문 (FAQ)
Q. vLLM 분산 서빙 시 텐서 병렬화(TP)와 파이프라인 병렬화(PP) 중 무엇을 써야 하나요?
실시간 지연 시간(Latency) 최소화가 최우선인 온라인 추론 환경에서는 단일 노드 내의 GPU 간 통신 속도가 극도로 빠른 텐서 병렬화(TP)를 1차적으로 적용하는 것이 압도적으로 유리합니다. 파이프라인 병렬화(PP)는 노드와 노드 사이를 건너뛰며 가중치를 넘겨주기 때문에 네트워크 전송 오버헤드가 발생해 실시간 추론 시 Latency가 급격히 상승할 수 있습니다. 70B 모델처럼 모델 크기가 너무 커 단일 노드의 GPU 메모리에 다 올릴 수 없는 불가피한 경우에만 TP와 PP를 섞어서 아키텍처를 설계하십시오.
Q. Ray 클러스터를 쓰지 않고 순수 Kubernetes Deployment로 분산 처리가 가능한가요?
단일 노드 안에서 다중 GPU(예: 8 x A100 80GB)를 전부 장착한 고성능 인스턴스 1대를 사용해 서빙하는 경우라면 Ray 클러스터 없이 순수 쿠버네티스 디플로이먼트 안에서 tensor-parallel-size=8 형태로 기동하여 정상 서비스할 수 있습니다. 그러나 물리적인 호스트 장비 2대 이상을 엮어 16개 이상의 GPU로 단일 대형 모델을 분산 구동해야 하는 멀티 노드 시나리오라면 Ray 클러스터 구조가 통신 제어와 분산 분기를 총괄해야 하므로 반드시 필수적으로 도입되어야 합니다.
Q. GPU 메모리가 누수되는 것처럼 계속 증가하다가 죽는데 원인이 무엇인가요?
vLLM은 시작 시점에 --gpu-memory-utilization 설정 비율만큼 KV 캐시용 영역을 통째로 미리 선점해 둡니다. 따라서 모니터링 툴 상에서는 처음부터 끝까지 GPU 메모리가 85% 이상 꽉 찬 상태로 보이는 것이 지극히 정상적인 아키텍처 형태입니다. 만약 85% 고정 상태를 넘어 실제 시스템 메모리가 서서히 증가해 파드가 죽는다면, 이는 vLLM의 캐싱 정책 문제가 아닌 파이썬 런타임 라이브러리나 API 통신 래퍼 서버 코드에서의 메모리 릭(Leak)일 가능성이 큽니다.
결론
쿠버네티스 vLLM 분산 환경 구축을 제대로 완수하기 위해서는 단순한 소프트웨어 설치를 넘어 GPU 하드웨어 통신과 캐시 매개변수의 조율이 유기적으로 합을 맞추어야 합니다. 특히 PagedAttention 설정 값을 자신의 타겟 모델 토큰 길이와 노드 하드웨어 스펙에 정밀하게 대입해 조정하는 노력이 수반되어야 실질적인 고성능 무장애 서비스를 실현할 수 있습니다. 지금 바로 기존에 단일 컨테이너로 소박하게 돌리던 LLM 인프라에 KubeRay를 이식하고 공유 메모리(/dev/shm) 용량 확보 조치부터 하나씩 수행해 강력한 고가용성 인프라의 초석을 다지시기 바랍니다.