배치 vs 실시간 ML 서빙 아키텍처¶
개요¶
ML 모델 서빙 방식은 크게 배치(Batch)와 실시간(Real-time)으로 나뉜다. 비즈니스 요구사항, 레이턴시 요건, 비용에 따라 적절한 아키텍처를 선택해야 한다.
비교 요약¶
| 항목 | 배치 서빙 | 실시간 서빙 |
|---|---|---|
| 레이턴시 | 분~시간 | 밀리초~초 |
| 처리량 | 높음 | 중간 |
| 비용 | 낮음 | 높음 |
| 복잡도 | 낮음 | 높음 |
| 데이터 신선도 | 낮음 | 높음 |
| 사용 사례 | 추천, 리포트 | 사기 탐지, 챗봇 |
배치 서빙 (Batch Serving)¶
아키텍처¶
[Data Lake] → [ETL] → [Feature Store] → [Model] → [Prediction DB] → [Application]
↑ │
└────────────────── Scheduler (Airflow) ─────────────┘
핵심 구성 요소¶
| 구성 요소 | 역할 | 도구 예시 |
|---|---|---|
| Orchestrator | 작업 스케줄링 | Airflow, Dagster, Prefect |
| Feature Store | 피처 저장/서빙 | Feast, Tecton, Hopsworks |
| Compute | 대규모 연산 | Spark, Dask, Ray |
| Storage | 예측 결과 저장 | PostgreSQL, Redis, S3 |
구현 패턴¶
1. Pre-computed Predictions
# Airflow DAG 예시
@dag(schedule_interval="0 2 * * *") # 매일 새벽 2시
def batch_prediction_pipeline():
@task
def extract_features():
df = spark.read.parquet("s3://data/users/")
features = feature_engineering(df)
return features.write.parquet("s3://features/daily/")
@task
def run_predictions(features_path):
features = spark.read.parquet(features_path)
model = mlflow.load_model("models:/recommender/production")
predictions = model.predict(features)
predictions.write.jdbc("postgresql://predictions")
@task
def validate_predictions():
# 데이터 품질 검증
pass
features = extract_features()
run_predictions(features) >> validate_predictions()
2. Feature Materialization
# Feast를 이용한 피처 저장
from feast import FeatureStore
store = FeatureStore(repo_path="feature_repo")
# 배치 피처 생성 및 저장
store.materialize_incremental(end_date=datetime.now())
장점¶
- 높은 처리량 (대량 데이터 처리)
- 비용 효율적 (spot 인스턴스 활용)
- 단순한 인프라
- 디버깅 용이
단점¶
- 데이터 지연 (freshness 이슈)
- 실시간 반응 불가
- 스토리지 비용 (pre-compute 저장)
적합한 사용 사례¶
| 사용 사례 | 설명 | 갱신 주기 |
|---|---|---|
| 추천 시스템 | 상품/콘텐츠 추천 | 일 1회 |
| 고객 세분화 | RFM, 클러스터링 | 주 1회 |
| 이탈 예측 | 고객 이탈 확률 | 일 1회 |
| 신용 평가 | 신용 점수 계산 | 월 1회 |
| 리포트 생성 | 대시보드 데이터 | 시간별 |
실시간 서빙 (Real-time Serving)¶
아키텍처¶
[Request] → [API Gateway] → [Model Server] → [Response]
↓ ↓
[Rate Limit] [Feature Store]
↓ ↓
[Load Balancer] [Model Registry]
핵심 구성 요소¶
| 구성 요소 | 역할 | 도구 예시 |
|---|---|---|
| Model Server | 추론 서빙 | Triton, vLLM, TorchServe |
| API Gateway | 라우팅/인증 | Kong, Nginx, AWS API GW |
| Feature Store | 실시간 피처 | Feast Online, Redis |
| Monitoring | 모니터링 | Prometheus, Grafana |
구현 패턴¶
1. REST API 서빙
# FastAPI + vLLM 예시
from fastapi import FastAPI
from vllm import LLM, SamplingParams
app = FastAPI()
llm = LLM(model="meta-llama/Llama-3.1-8B-Instruct")
@app.post("/predict")
async def predict(request: PredictRequest):
# 실시간 피처 조회
features = await feature_store.get_online_features(
entity_rows=[{"user_id": request.user_id}]
)
# 추론
sampling_params = SamplingParams(temperature=0.7, max_tokens=512)
output = llm.generate([request.prompt], sampling_params)
return {"prediction": output[0].outputs[0].text}
2. gRPC 서빙 (Triton)
# Triton Inference Server 클라이언트
import tritonclient.grpc as grpcclient
client = grpcclient.InferenceServerClient("localhost:8001")
# 추론 요청
inputs = [grpcclient.InferInput("input", [1, 512], "FP32")]
inputs[0].set_data_from_numpy(input_data)
result = client.infer("model_name", inputs)
output = result.as_numpy("output")
3. Streaming (SSE)
# Streaming 응답
from fastapi.responses import StreamingResponse
@app.post("/stream")
async def stream_predict(request: PredictRequest):
async def generate():
for token in model.stream(request.prompt):
yield f"data: {token}\n\n"
return StreamingResponse(generate(), media_type="text/event-stream")
성능 최적화¶
| 기법 | 설명 | 효과 |
|---|---|---|
| Model Quantization | INT8/INT4 양자화 | 메모리 50-75% 감소 |
| Batching | 동적 배치 | 처리량 2-4x 증가 |
| Caching | KV Cache, 결과 캐시 | 레이턴시 감소 |
| Speculative Decoding | 투기적 디코딩 | LLM 속도 2-3x |
| Model Parallelism | 텐서/파이프라인 병렬 | 대형 모델 서빙 |
장점¶
- 즉시 응답 (저레이턴시)
- 최신 데이터 반영
- 동적 의사결정
- 개인화 가능
단점¶
- 높은 인프라 비용
- 복잡한 아키텍처
- 장애 대응 필요
- 확장성 고려 필수
적합한 사용 사례¶
| 사용 사례 | 설명 | 레이턴시 요건 |
|---|---|---|
| 사기 탐지 | 거래 승인 전 검증 | < 100ms |
| 챗봇/LLM | 대화형 AI | < 1s (TTFT) |
| 검색 랭킹 | 실시간 검색 결과 | < 50ms |
| 동적 가격 | 실시간 가격 책정 | < 200ms |
| 자율주행 | 객체 인식 | < 10ms |
하이브리드 아키텍처¶
Lambda Architecture¶
[Data] ─┬─→ [Batch Layer] ───→ [Batch View] ─┐
│ ├─→ [Serving Layer] → [Query]
└─→ [Speed Layer] ───→ [Real-time View]─┘
특징: - 배치 레이어: 정확한 전체 뷰 - 스피드 레이어: 최신 증분 데이터 - 서빙 레이어: 두 뷰 병합
Kappa Architecture¶
특징: - 스트림 처리만 사용 - 재처리 시 로그에서 리플레이 - 단순한 아키텍처
실무 하이브리드 패턴¶
# 하이브리드: 배치 피처 + 실시간 피처
class HybridFeatureService:
def __init__(self):
self.batch_store = FeastOfflineStore()
self.online_store = Redis()
async def get_features(self, user_id: str):
# 배치 피처 (일별 갱신)
batch_features = await self.batch_store.get([
"user_purchase_count_30d",
"user_avg_order_value"
], user_id)
# 실시간 피처 (초 단위)
realtime_features = await self.online_store.get([
"user_session_duration",
"user_cart_items"
], user_id)
return {**batch_features, **realtime_features}
선택 가이드¶
의사결정 플로우¶
레이턴시 요건?
├─ < 1초 필요
│ └─ 예측당 비용 민감?
│ ├─ 예 → 하이브리드 (배치 + 캐시)
│ └─ 아니오 → 실시간 서빙
│
├─ 분~시간 허용
│ └─ 배치 서빙
│
└─ 혼합
└─ Lambda/Kappa 아키텍처
비용 비교 (월간 추정)¶
| 항목 | 배치 | 실시간 | 하이브리드 |
|---|---|---|---|
| 컴퓨팅 | $500-2K | $2K-10K | $1.5K-5K |
| 스토리지 | $200-500 | $100-300 | $300-600 |
| 네트워크 | $50-200 | $500-2K | $300-1K |
| 총 비용 | $1-3K | $3-12K | $2-7K |
1M 예측/일 기준 추정치
참고 자료¶
마지막 업데이트: 2026-02-11