안녕하세요! 2부: KubeRay로 Ray 클러스터 구축하기에서는 LLM 서빙을 위한 기본 인프라인 RayCluster를 성공적으로 구축했습니다. 하지만 클러스터는 아직 비어있는 그릇일 뿐, 이제 그 위에 실제 애플리케이션을 올려야 합니다.
KubeRay는 RayCluster 위에서 실행되는 워크로드(Workload)를 관리하기 위해 두 가지 핵심적인 CRD, RayJob과 RayService를 제공합니다. 이번 3부에서는 두 CRD의 차이점을 명확히 이해하고, 우리의 목표인 LLM 서빙에 최적화된 RayService를 활용하여 vLLM 모델을 배포하는 과정을 실습해 보겠습니다.
1️⃣ KubeRay 워크로드 CRD 이해하기: RayJob vs RayService
RayCluster가 컴퓨팅 자원을 제공하는 '인프라'라면, RayJob과 RayService는 그 위에서 실행되는 '애플리케이션'입니다. 둘은 목적에 따라 명확하게 구분됩니다.
RayJob: 일회성 배치(Batch) 작업
언제 사용하나요?
시작과 끝이 명확한 작업을 실행할 때 사용합니다.
주요 사용 사례:
- 대규모 데이터셋 전처리
- 모델 파인튜닝
- 주기적 리포트 생성
워크플로우:
RayJob CR을 생성하면 KubeRay가 작업을 실행합니다. 기존 RayCluster를 지정하거나, Job 전용 임시 클러스터를 생성할 수 있습니다. 작업 완료 후에는 자원이 자동으로 정리됩니다.
RayService: 상시 운영되는 온라인 서빙
언제 사용하나요?
외부 요청을 지속적으로 처리해야 하는 서비스를 배포할 때 사용합니다.
주요 사용 사례:
- 머신러닝 모델 추론 API 서버
- 실시간 데이터 처리 애플리케이션
주요 기능:
- RayCluster와 Ray Serve 애플리케이션 통합 관리
- 자동 서비스 디스커버리 (쿠버네티스 서비스 자동 생성)
- 무중단 배포 (Zero-downtime deployment)
- 자동 확장 (Autoscaling)
이처럼 RayJob과 RayService는 목적은 다르지만, 모두 RayCluster라는 컴퓨팅 기반 위에서 동작합니다. RayCluster가 작업 공간을 제공하면, RayJob은 그 공간을 잠시 빌려 쓰고, RayService는 그 공간에 상주하며 계속 일을 하는 셈입니다.
LLM 모델을 API로 제공하는 것이 목표이므로, 상시 운영에 최적화된 RayService를 사용하는 것이 적합합니다.
2️⃣ vLLM과 Ray Serve로 고성능 추론 API 만들기
이제 2부에서 만든 RayCluster 위에 vLLM을 이용한 LLM 추론 API를 배포해 보겠습니다.
왜 vLLM인가요?
vLLM은 PagedAttention 기술을 활용하여 기존 대비 높은 처리량(Throughput)으로 LLM 추론이 가능한 라이브러리입니다. Ray Serve의 분산 처리 및 오토스케일링 기능과 결합하면 강력한 LLM 서빙 시스템을 구축할 수 있습니다.
ℹ️ vLLM에 대한 자세한 내용은 공식 GitHub 저장소에서 확인하실 수 있습니다.
단계 1: Ray Serve 애플리케이션 작성하기
vLLM 모델을 로드하고 API 요청을 처리하는 Python 스크립트를 작성합니다.
# serve_vllm.py
from ray import serve
from vllm.engine.arg_utils import AsyncEngineArgs
from vllm.engine.async_llm_engine import AsyncLLMEngine
from fastapi import FastAPI
# FastAPI 앱 정의 (Ray Serve가 내부적으로 사용)
app = FastAPI()
@serve.deployment(
num_replicas=1,
ray_actor_options={"num_gpus": 1}
)
@serve.ingress(app)
class VLLMDeployment:
def __init__(self, model_name: str):
# vLLM 엔진 설정
engine_args = AsyncEngineArgs(
model=model_name,
tensor_parallel_size=1, # GPU 수에 맞게 조절
trust_remote_code=True
)
self.engine = AsyncLLMEngine.from_engine_args(engine_args)
@app.post("/generate")
async def generate(self, prompt: str, sampling_params: dict):
from vllm.utils import random_uuid
from vllm.sampling_params import SamplingParams
# 샘플링 파라미터 파싱
sampling_params = SamplingParams(**sampling_params)
request_id = random_uuid()
results_generator = self.engine.generate(prompt, sampling_params, request_id)
# 비동기 스트리밍 처리
final_output = None
async for request_output in results_generator:
final_output = request_output
# 최종 결과 반환
text_outputs = [output.text for output in final_output.outputs]
return {"text": text_outputs}
# 모델 배포
app = VLLMDeployment.bind(model_name="EleutherAI/gpt-neo-125m")
이 스크립트는 /generate
엔드포인트를 통해 프롬프트와 샘플링 파라미터를 받아 추론 결과를 반환하는 API 서버를 정의합니다.
단계 2: RayService YAML 파일 작성하기
Python 스크립트를 실행할 RayService CR을 정의합니다.
# rayservice-vllm.yaml
apiVersion: ray.io/v1
kind: RayService
metadata:
name: vllm-llm-server
spec:
rayClusterConfig:
rayVersion: '2.9.0'
headGroupSpec:
rayStartParams:
dashboard-host: '0.0.0.0'
template:
spec:
containers:
- name: ray-head
image: rayproject/ray-ml:2.9.0-py310-gpu
# ... (Head 파드 설정)
workerGroupSpecs:
- replicas: 1
minReplicas: 1
maxReplicas: 1
groupName: gpu-worker-group
rayStartParams: {}
template:
spec:
containers:
- name: ray-worker
image: rayproject/ray-ml:2.9.0-py310-gpu
resources:
limits:
nvidia.com/gpu: "1"
requests:
nvidia.com/gpu: "1"
# ... (Worker 파드 설정, 2부의 nodeSelector, tolerations 등 포함)
# Ray Serve 애플리케이션 설정
serveConfigV2: |
import_path: serve_vllm:app
deployments:
- name: VLLMDeployment
user_config:
model_name: "EleutherAI/gpt-neo-125m"
주요 설정 설명:
-
import_path
: 실행할 Ray Serve 애플리케이션의 위치 (파일명:인스턴스명) -
user_config
: 스크립트의__init__
메서드로 전달될 파라미터
단계 3: 배포 및 확인
배포를 위해 Python 스크립트가 포함된 Docker 이미지를 빌드하거나, 테스트 목적으로는 kubectl cp
로 파일을 전송할 수 있습니다.
# RayService 배포
kubectl apply -f rayservice-vllm.yaml
# 서비스 상태 확인
kubectl get rayservice vllm-llm-server
# 생성된 서비스 확인
kubectl get svc vllm-llm-server-head-svc
# 로컬에서 테스트를 위한 포트포워딩
kubectl port-forward svc/vllm-llm-server-head-svc 8000:8000
이제 http://localhost:8000/generate
로 API 요청을 보내어 모델 추론을 테스트할 수 있습니다.
Top comments (0)