넷플릭스의 사내 LLM 서빙 (새 탭에서 열림)
Netflix는 LLM을 별도 ML 시스템이 아니라 기존 JVM 기반 서빙 플랫폼과 GPU 추론 백엔드 안에서 운영한다. 엔진으로는 vLLM을 선택하고, Triton·OpenAI 호환 API·통합 배포 제어 plane을 결합해 연구에서 운영까지의 전환 비용을 낮췄다. 다만 운영 환경에서는 엔진 버전 불일치, 커스텀 모델 처리, JSON 제약 누락, 모델 버전과 입력 스키마 변경 간 조정 문제가 주요 리스크로 드러났다. ## Netflix의 통합 추론 아키텍처 - 통합 JVM 서빙 시스템이 라우팅, A/B 테스트, 후보 생성, feature 조회, 추론, 후처리, 로깅을 end-to-end로 처리한다. - 실시간 요청과 캐시된 배치 경로를 모두 지원한다. - 호출 방식은 다음 두 가지다. - 기존 서빙 시스템을 통한 gRPC - 최신 LLM 애플리케이션을 위한 직접 HTTP - 소형 CPU 모델은 원격 호출 비용을 피하기 위해 서빙 프로세스 내부에서 실행한다. - 대형 GPU 모델은 Model Scoring Service(MSS)로 추론을 위임한다. - MSS는 XGBoost, TensorFlow, PyTorch, LLM을 동일한 인터페이스로 제공하며, 내부 GPU 실행은 NVIDIA Triton Inference Server가 담당한다. - Java 기반 control plane은 모델 배포, 버전 관리, 상태 점검, 자동 확장, 멀티리전 롤아웃과 무중단 업그레이드를 관리한다. ## vLLM을 표준 추론 엔진으로 선택 Netflix는 원래 Triton과 통합된 TensorRT-LLM을 사용했지만, 2025년경 워크로드 변화에 맞춰 재평가했다. - 임베딩 생성, prefill-only 추론, autoregressive decoding, 복잡한 제약 로직이 필요한 커스텀 모델까지 지원해야 했다. - vLLM을 선택한 이유: - 별도의 다단계 컴파일 없이 커스텀 모델 아키텍처 로딩 가능 - 커스텀 디코딩 로직을 위한 확장 지점 제공 - 컴파일 중심 엔진보다 장애와 중간 상태를 디버깅하기 쉬움 - 연구자들이 이미 익숙하게 사용해 연구-운영 전환 비용이 낮음 - 전문화된 엔진과 오픈소스 엔진 간 성능 격차가 줄어든 점도 선택에 영향을 주었다. ## Triton과 vLLM의 패키징 방식 Triton에서 vLLM 모델을 패키징하는 방식은 유지보수성과 프론트엔드 변경의 결합도에 큰 영향을 준다. - **Python backend** - 패키징 시 입력·출력 tensor 스펙을 직접 정의한다. - 프론트엔드의 요청 생성 방식이 바뀌면 패키징 코드도 함께 수정해야 한다. - 변경이 맞물리지 않으면 런타임 요청이 실패한다. - 전처리, 후처리, 앙상블, 커스텀 토크나이징 등 비표준 로직이 필요한 모델에는 적합하다. - **vLLM backend** - 모델 가중치와 tokenizer 경로를 담은 JSON 설정만 제공한다. - Triton이 배포 시점에 I/O tensor 스펙을 동적으로 생성한다. - 모델 아티팩트와 프론트엔드를 독립적으로 발전시킬 수 있어 기본 선택으로 적합하다. ### 운영에서 드러난 패키징 문제 - Triton의 vLLM backend는 특정 vLLM API에 맞춰 컴파일된다. - 예를 들어 Triton 25.09가 vLLM 0.11.2에서 제거된 `vllm.engine.metrics`를 import하면 backend 자체가 로드되지 않는다. - 따라서 서비스 이미지 생성 시 Triton과 vLLM 호환 버전을 고정해야 한다. - 모델 작성자가 패키징 단계에서 vLLM 버전을 임의로 덮어쓰지 못하도록 제한할 필요가 있다. - 표준 HuggingFace 모델이 아닌 커스텀 실행 흐름은 여전히 Python backend를 사용해야 한다. ## OpenAI 호환 HTTP API Netflix는 LLM을 기존 모델과 다른 “특수 모델”로 취급하지 않기 위해 모든 모델을 동일한 gRPC 호출 체계로 제공한다. - 기존 클라이언트 라이브러리, 상태 점검, 배포 파이프라인을 LLM에도 재사용한다. - 동시에 생태계 표준이 된 OpenAI 호환 API를 추가 HTTP 프론트엔드로 제공한다. - 호스팅 모델에서 자체 파인튜닝 모델로 전환할 때 API를 그대로 유지할 수 있다. - 이에 따라 품질, 지연시간, 비용, 데이터 프라이버시를 이유로 자체 호스팅을 선택해도 애플리케이션 코드 변경이 거의 없다. ## Triton 프론트엔드와 JSON 출력 제약 구현에는 NVIDIA의 Triton OpenAI 호환 프론트엔드를 활용했다. - 내장 Triton 서버가 실행된다. - `TritonLLMEngine`이 HTTP 요청 스키마를 Triton 추론 요청으로 변환한다. - FastAPI를 통해 응답을 제공한다. - KServe HTTP/gRPC 프론트엔드도 활성화해 Java control plane이 동일한 Triton 인스턴스에 gRPC로 접근할 수 있다. - 운영 중 `response_format`이 스키마에서는 허용되지만 vLLM까지 전달되지 않는 문제가 발견됐다. - 그 결과 JSON 응답을 요청해도 guided decoding이 적용되지 않아 잘못된 JSON이 반환될 수 있었다. - Netflix는 프론트엔드를 git subtree로 가져와 `response_format`을 vLLM의 guided decoding 파라미터로 변환하도록 직접 패치했다. ## 무중단 배포와 스키마 변경 문제 GPU 모델은 CPU 서비스보다 기동 시간이 길고, 모델 버전 사이에 I/O 스키마가 달라질 수 있어 배포 시 추가 조정이 필요하다. ### Red-Black 배포 - 새 버전을 기존 버전과 동시에 실행한다. - 새 인스턴스가 health check를 통과하면 트래픽을 단계적으로 이동한다. - 새 버전이 확장되는 속도만큼 기존 버전을 축소한다. - 중간 단계에서 실패하면 원자적으로 롤백한다. - 모델 인터페이스가 안정적인 경우 적합하다. - 하지만 입력 tensor 차원 추가처럼 I/O 스키마가 바뀌면 문제가 생긴다. - upstream 소비자는 새 모델이 완전히 배포되기 전까지 설정을 바꿀 수 없다. - 마이그레이션 중 새 모델에 이전 형식 요청이 전달된다. - 결과적으로 요청이 실패한다. 제공된 글은 Red-Black 방식의 한계와 `Versioned` 배포 전략을 설명하기 시작한 지점에서 끝나므로, Versioned 전략의 구체적인 동작과 장단점은 확인할 수 없다. 실무적으로는 vLLM을 표준 경로로 삼되, Triton·vLLM 버전을 강하게 고정하고 Python backend라는 예외 경로를 유지하는 것이 현실적이다. 또한 OpenAI 호환 API를 도입할 때는 스키마가 실제 추론 엔진의 제약 기능까지 전달되는지 검증하고, 모델 배포와 입력 스키마 변경을 독립적으로 조정할 수 있는 버전 관리 전략을 마련해야 한다.