Stage 6 · Ray Serve 서빙 — 로컬 모델 배포 · 스케일/동시성
📖 시리즈: Ray-Essential
(7 / 8)
한눈에 보기
Stage 5에서 학습한 모델을 실제로 서비스하는 단계입니다. Ray Serve는 모델을 HTTP 엔드포인트로 배포하는 도구로, 공식적으로 “local first” — 배포 전에 로컬에서 전체 deployment graph를 테스트하라고 문서가 장려합니다. 한 대의 노트북에서 serve.run()이면 충분히 실제 서버가 뜹니다.
이번 포스트에서 다루는 핵심 질문은 세 가지입니다.
@serve.deployment와serve.run으로 모델을 어떻게 HTTP로 배포하나?- deployment graph와 FastAPI 인그레스로 여러 모델을 어떻게 조합하나?
- 로컬 스케일/동시성(복제·배칭)은 어떻게 제어하고, 주의점은?
Deployment 기본
@serve.deployment를 클래스에 붙이면 서빙 단위(배포)가 됩니다. bind()로 인자를 고정해 앱을 만들고, serve.run으로 띄웁니다. 기본 HTTP 포트는 8000입니다.
pip install -U "ray[serve]"
import ray
from ray import serve
@serve.deployment
class EchoModel:
def __init__(self, suffix: str):
self.suffix = suffix
def __call__(self, request):
name = request.query_params["name"]
return {"result": f"hello {name} {self.suffix}"}
app = EchoModel.bind(suffix="!!")
serve.run(app, route_prefix="/") # http://localhost:8000/
HTTP로 호출합니다:
curl "http://localhost:8000/?name=ray"
# {"result": "hello ray !!"}
@serve.deployment: 서빙할 클래스.__call__이 요청 핸들러.bind(): 생성자 인자를 고정해 앱 조립.serve.run(app, route_prefix=...): 로컬에서 앱 시작. 데이터 복제/스케일이 가능.
실제 머신러닝 모델 배포
Hugging Face 파이프라인을 감싸는 전형적인 예시입니다.
@serve.deployment
class Sentiment:
def __init__(self):
from transformers import pipeline
self._pipe = pipeline("sentiment-analysis")
def __call__(self, request):
text = request.query_params["text"]
return self._pipe(text)
serve.run(Sentiment.bind(), route_prefix="/sentiment")
구성과 인그레스 — deployment graph
여러 모델 조합 — DeploymentHandle
여러 deployment를 조합하고 싶으면 DeploymentHandle 로 다른 deployment를 함수처럼 호출합니다. 이를 deployment graph라고 합니다.
@serve.deployment
class Upper:
def __call__(self, text: str):
return text.upper()
@serve.deployment
class Greeter:
def __init__(self, upper):
self._upper = upper
def __call__(self, request):
name = request.query_params["name"]
return self._upper(name) # DeploymentHandle 호출 (마치 함수)
app = Greeter.bind(Upper.bind())
serve.run(app, route_prefix="/")
- 그래프로 여러 모델/전처리를 파이프라인처럼 연결합니다.
- 각 deployment는 독립적으로 스케일(복제)할 수 있습니다.
FastAPI 인그레스 — @serve.ingress
복잡한 REST API가 필요하면 FastAPI 앱을 배포로 감쌉니다.
from fastapi import FastAPI
from ray import serve
app_fastapi = FastAPI()
@serve.deployment
@serve.ingress(app_fastapi)
class MyAPI:
@app_fastapi.get("/ping")
def ping(self):
return {"msg": "pong"}
@app_fastapi.post("/predict")
async def predict(self, body: dict):
return {"pred": body["x"] * 2}
serve.run(MyAPI.bind(), route_prefix="/")
@serve.ingress(app)로 FastAPI 라우트를 deployment에 노출.- async 핸들러는 replica 내부에서 이벤트 루프로 처리됩니다.
로컬 스케일/동시성 — 복제·배칭·주의점
num_replicas — 확장
@serve.deployment(num_replicas=2) # 프로세스 복제
class Sentiment:
...
# 또는 오토스케일
@serve.deployment(autoscaling_config={
"min_replicas": 1, "max_replicas": 4,
"target_num_ongoing_requests_per_replica": 2.0,
})
class Sentiment:
...
num_replicas=k: 같은 모델을 k개 프로세스로 복제해 병렬 처리. 로컬에선 물리 코어 이내로.
리퀘스트 배칭 — 벡터 연산 묶기
GPU 추론은 개별 요청 처리보다 배치로 묶는 게 효율적입니다.
import numpy as np
@serve.deployment
class BatchPredictor:
def __init__(self):
self._batch = []
@serve.batch(max_batch_size=16, batch_wait_timeout_s=0.05)
def predict_batch(self, batches): # list[list] 묶음
return [sum(b) for b in batches]
async def __call__(self, request):
x = request.query_params["x"]
return await self.predict_batch([int(x)])
serve.run(BatchPredictor.bind(), route_prefix="/")
@serve.batch: 다중 요청을 최대max_batch_size개/batch_wait_timeout_s초 동안 모아 한 번에 처리.
⚠️ 로컬 주의점
- 동시성 기본값: 서빙 deployment의 동시 요청 한도(
max_concurrent_queries, 과거max_ongoing_requests) 기본값은 Ray 2.x 사이에서 변경됐습니다. 설치 버전의 기본값을 확인하세요. - 복제 수 vs 코어:
num_replicas를 코어보다 크게 하면 오버서브스크라이빙으로 오히려 느려집니다. - 상태 보관 위치: replica
__init__에서 모델을 로드하면 replica별로 메모리가 중복됩니다. 상태를 공유해야 하면 액터(Stage 2) 패턴을 섞습니다.
로컬 활용 전략 — 실전 요약
- 학습 → 서빙 전환: Stage 5의 체크포인트를
RayTrainPredictor(또는 직접 로드)로@serve.deployment에 넣습니다. - 로컬에서 전체 그래프 테스트:
serve.run(app)으로 배포 전에 검증 → Serve의 공식 권장 워크플로. - 배칭으로 GPU 효율: 벡터 연산 모델엔
@serve.batch. - FastAPI 인그레스:
/predict,/health등 REST 엔드포인트로 클라이언트와 계약.
Summary
- Ray Serve는
@serve.deployment+serve.run으로 모델을localhost:8000HTTP 서비스로 배포합니다 — local first. - deployment graph(
DeploymentHandle)와 FastAPI 인그레스(@serve.ingress)로 여러 모델을 조합합니다. - 로컬 스케일/동시성은
num_replicas,@serve.batch배칭, 동시 요청 한도로 제어하며, 기본값이 버전에 따라 바뀌는 점과 코어 한도를 주의합니다.
다음 학습 (Next Learning)
- Stage 7 · 실전 통합 — Data→Train→Tune→Serve를 하나의 파이프라인으로 조립하고 concurrency를 종합합니다.
- Stage 5 · Ray Train 분산 학습 — 서빙할 모델을 분산 학습·체크포인트로 준비.
- Stage 4 · Ray Tune 튜닝 — 튜닝된 하이퍼파라미터를 서빙 deployment에 반영.