주요 컨텐츠로 이동

대규모 환경에서 안정적인 LLM 추론

안정적인 LLM 추론 인프라 구축 경험에서 얻은 교훈

작성자: Ying Chen, Wendy Hu, 안킷 마투르, Mike Eastham, 페이-룬 라이오, Wai Wu , 아르준 디쿠냐

  • 멀티테넌트 LLM 서빙은 워크로드 전반의 용량에 대한 추론을 필요로 합니다. "모델 유닛"은 고객별로 GPU 리소스를 할당하고, 라우팅하며, 확장할 수 있게 해주는 VM과 유사한 추상화를 제공합니다.
  • 모델 유닛을 기반으로 구축된 비용 효율적인 로드 밸런싱 및 자동 스케일링은 지연 시간 목표를 유지하면서 정적 프로비저닝 대비 GPU 비용을 80% 이상 절감했습니다.
  • 블랙박스 상태 확인과 같은 런타임 안정성 메커니즘은 자동적으로 무응답 오류를 감지하고 복구하며, 다중 모드 병목 현상 프로파일링을 통해 3배의 처리량 향상을 달성했습니다.

Databricks는 Kimi 및 Qwen과 같은 오픈 소스 모델부터 OpenAI, Gemini, Claude와 같은 독점 모델에 이르기까지 모든 최신 모델을 지원하는 독자적인 추론 플랫폼을 구축했습니다. 저희는 Superhuman, Yipit Data, Fox Sports 등 세계 최대 규모의 에이전트 기반 애플리케이션을 위한 추론을 지원하고 있습니다. 현재 월 125조 개 이상의 토큰을 처리하고 있습니다.

대규모 LLM 서빙을 어렵게 만드는 요인은 안정성입니다. 에이전트가 우리의 업무 및 생활 방식의 인터페이스가 되면서 추론 수요는 기하급수적으로 증가하고 있습니다. 근무 시간 동안 최고조에 달하는 매우 불규칙한 수요 곡선을 보입니다.

그림 1: LLM 서빙을 사용하는 최대 고객 중 한 곳의 2일간 트래픽. 몇 시간 내에 트래픽이 급격히 증가하는 것을 볼 수 있습니다.
그림 1: LLM 서빙을 사용하는 최대 고객 중 한 곳의 2일간 트래픽. 몇 시간 내에 트래픽이 급격히 증가하는 것을 볼 수 있습니다.

대규모 LLM 추론 실행의 과제

안정적인 추론 플랫폼이란 무엇을 의미할까요? 계약은 간단해 보입니다. 가용성은 요청을 처리할 수 있는지 여부입니다. 그러나 실제로는 다양한 사용 사례가 현저히 다른 지연 시간 요구 사항을 가지며, 이는 가용성에 영향을 미칩니다. 가장 진보된 에이전트들은 p95 첫 토큰 생성 시간(TTFT)과 초당 출력 토큰(OPTS)이 저하되는 것을 용납할 수 없습니다.

LLM 서빙을 위한 다중 테넌트 시스템에서 안정성과 지연 시간 모두를 달성하는 것은 어렵습니다.

안정성

최신 성능을 위해서는 KV 캐시 전송을 위한 고대역폭 인터커넥트가 있는 최신 GPU가 필요합니다. 이러한 컴퓨팅 설정은 기존 CPU 시스템보다 근본적으로 안정성이 떨어지며 비용이 많이 듭니다. 모든 노드 간 통신이 필요하기 때문에 단일 노드의 다운타임은 분리된 프리필/디코드 설정에서 여러 다른 노드의 재구성을 필요로 합니다. 최고 대역폭 네트워킹은 단일 물리적 랙 내에서 단일 스파인 연결(예: NVL72 시스템)을 요구합니다. 이는 단일 데이터센터 랙 내의 특정 시스템 오류가 광범위한 서비스 중단을 초래할 수 있음을 의미합니다. 다중 AZ 또는 백업 인스턴스 유형 활용과 같은 분산 시스템의 표준적인 방법은 값비싼 백업 GPU를 유휴 상태로 유지하는 것을 의미하며, 이는 비용적으로 부담스러운 옵션입니다. 과도한 프로비저닝은 또 다른 고전적인 방법이지만, 컴퓨팅 공급이 매우 제한적이기 때문에 극도로 비싸고 비실용적입니다. 따라서 시스템은 심한 부하에서도 계속 작동해야 합니다.

이러한 제약 조건 하에서도 출시 속도를 높게 유지해야 합니다. 저희의 추론 수요는 매년 여러 자릿수 증가했으며, 혁신적인 기능을 출시하면서 이러한 성장을 뒷받침하는 것은 어려웠습니다. 이미지, 비디오, 안전 분류와 같은 기능은 각각 다른 전처리 시스템을 필요로 하며, 이 모든 시스템은 독립적으로 확장되어야 합니다.

마지막으로, 동급 최고의 성능을 달성하고 새로운 모델 아키텍처를 지원하려면 커스텀 커널부터 독점 추론 엔진에 이르는 광범위한 최적화가 필요합니다. 아키텍처가 미묘하게 변경됨에 따라 새로운 저수준 소프트웨어가 종종 도입되는데, 이는 대규모 환경에서 불투명한 방식으로 실패할 수 있으며, 서버 중단부터 GPU 충돌에 이르는 어려운 디버깅 시나리오로 나타날 수 있습니다.

지연 시간

다양한 부하 패턴에서 지연 시간을 제어하는 것은 어렵습니다. 이는 요청 처리 비용이 매우 가변적이며 사전에 예측하기 어렵기 때문입니다. 심지어 정상적인 서버도 더 많은 부하를 받으면 모든 요청을 더 느리게 처리하여, 처리량(따라서 비용 효율성)과 제품이 처리해야 하는 가장 빠른 지연 시간 사이에 상충 관계를 드러냅니다. 이는 서버에 할당된 요청의 조합에 따라 예기치 않게 비정상 상태로 매우 빠르게 전환될 수 있으므로 안정성 문제로도 나타날 수 있습니다.

그림 2: 대규모 고객의 고객 지원 에이전트 워크로드 기반의 실제 동시성 대 지연 시간 벤치마킹.

또한, 지연 시간은 출력 토큰 생성에 의해 좌우되지만, 모델이 얼마나 오랫동안 응답할지 예측하기 어렵기 때문에 사전 비용 추정은 어렵습니다. 따라서 낮은 지연 시간 서빙을 위해서는 복잡한 용량 관리, 로드 밸런싱 및 요청 우선순위 지정 시스템이 필요합니다.

전반적인 아키텍처

이러한 문제들을 해결하는 구체적인 방법에 대해 자세히 알아보기 전에, 저희 서빙 인프라의 개요를 살펴보겠습니다.

데이터 플레인에서는,

  • 추론 런타임(오픈 소스 및 독점 자체 엔진)은 최신 GPU에 배포됩니다.
  • 모델 배포 전반의 트래픽을 처리하기 위해 데이터 플레인은 동일 모델의 복제본 간에 부하를 분산하는 라우터(저희는 Axon이라고 부릅니다)와 복제본 수를 조정하는 오토스케일러를 실행합니다.

컨트롤 플레인에서는,

  • 요청은 데이터 플레인에 도달하기 전에 속도 제한을 거칩니다.
  • 요청 메트릭을 기반으로 용량 관리 알고리즘이 각 워크로드가 얻는 GPU 용량을 결정하며, 오토스케일러가 이를 적용합니다.
컨트롤 플레인 및 데이터 플레인

용량 파악하기

우리는 용량에 대해 대략적으로 파악할 수 있어야 합니다. 즉, 우리가 얼마나 가지고 있고, 얼마나 판매했으며, 고객이 얼마나 사용하고 있는지 말입니다. 이를 위해 우리는 "모델 유닛"이라는 추상화를 도입했습니다. 복제본이 분당 고정된 수의 모델 유닛(예: 100개)을 처리할 수 있다고 가정하면 다음과 같은 가정을 할 수 있습니다.

  • 입력 또는 출력이 긴 요청은 동일한 시간 내에 더 적은 수의 요청만 완료될 수 있으므로 더 많은 모델 유닛을 소비합니다.
  • 프리필 및 디코드는 처리량 특성이 다르므로, 출력이 긴 요청은 입력이 긴 요청보다 비용이 더 많이 듭니다.
그림 3: 요청 비용은 입력 및 출력 토큰 분포에 따라 비선형적이고 복잡한 다차원 방식으로 달라집니다. 이는 요청당 지연 시간이 대략적으로 균일하게 분포되는 기존 AI 시스템과는 극명한 대조를 이룹니다.
그림 3: 요청 비용은 입력 및 출력 토큰 분포에 따라 비선형적이고 복잡한 다차원 방식으로 달라집니다. 이는 요청당 지연 시간이 대략적으로 균일하게 분포되는 기존 AI 시스템과는 극명한 대조를 이룹니다.

따라서 우리는 다음과 같은 다차원 함수를 사용하여 요청 비용을 모델링합니다.

계수 α, β, γ는 각 하드웨어 유형의 각 모델에 대한 자동 벤치마킹을 통해 결정됩니다. 모델 유닛은 접두사 캐싱과 같은 최적화를 위해 추가로 조정될 수 있으며, 멀티모달리티와 같은 기능도 고려해야 합니다.

이러한 추정치는 구조적으로 완벽하지는 않지만, 다중 테넌트 시스템을 클라우드 VM과 유사한 더 관리하기 쉬운 형태로 분할하는 방법으로 사용됩니다. VM은 특정 고객에게 할당될 수 있는 예측 가능한 성능을 제공하는 바람직한 특성을 가지고 있습니다. 프로덕션 에이전트 기반 워크로드의 경우, 낮은 지연 시간과 용량에 대한 보장을 제공하는 것이 중요하며, 이러한 할당 시스템이 없으면 너무 많은 고객이 시스템을 사용할 경우 회수될 수 있는 "최선 노력" 용량을 제공하는 것이 최선입니다.

비용 기반 로드 밸런싱 및 오토스케일링

요청이 서버에 미치는 영향이 매우 가변적이므로 거의 최적의 라우팅 결정을 내리는 것이 중요합니다. 일반적으로 로드 밸런싱은 P2C(power of two choices)와 같은 통계적 접근 방식에 의존하는 경향이 있습니다. 이는 큐 크기를 기반으로 부하를 추정하고 샘플링을 활용하여 가능한 모든 대상을 이해하는 데 드는 메모리 및 지연 시간 오버헤드를 줄입니다. 그러나 LLM 지연 시간은 높은 경향이 있고, 서버 수는 스케일 아웃된 CPU 시스템보다 적으며, 잘못된 라우팅의 비용은 심각합니다. 따라서 LLM 서빙은 다른 접근 방식을 필요로 합니다.

오늘날 저희는 Databricks의 자동 샤더인 Dicer를 사용하여 서버 전반에 걸쳐 워크로드를 동적으로 라우팅합니다. 부하 인식 라우팅이 없으면 긴 컨텍스트 요청으로 인해 개별 서버가 핫스팟이 되고 다른 서버는 활용도가 낮아집니다. 저희는 모델 유닛을 Dicer와 통합하여 라우팅 결정이 기존 요청 기반 휴리스틱 대신 모델 유닛의 서버 부하를 기반으로 이루어지도록 했습니다. Dicer는 또한 상태 저장 세션을 제공하여 요청 라우팅을 고정시킵니다. 워크로드의 요청은 서버의 일부에만 전달되어 캐시 적중률을 향상시키고(코딩 에이전트와 같이 지연 시간에 민감한 워크로드에 중요) 영향 범위를 제한합니다.

더 많은 것을 학습함에 따라 부하 지표를 조정하고, 더 높은 정확도의 비용 지표를 기반으로 미래에는 더 최적화된 라우팅 시스템을 사용할 수도 있습니다.

그림 4: 라우터와 오토스케일러는 모두 서버 부하를 소비하므로, 소수의 비용이 많이 드는 긴 컨텍스트 요청이 많은 저렴한 짧은 요청과 다른 라우팅 및 스케일링 결정을 유발할 수 있습니다.
그림 4: 라우터와 오토스케일러는 모두 서버 부하를 소비하므로, 소수의 비용이 많이 드는 긴 컨텍스트 요청이 많은 저렴한 짧은 요청과 다른 라우팅 및 스케일링 결정을 유발할 수 있습니다.

오토스케일링에서도 비슷한 문제가 발생합니다. 대기 중인 요청 수만으로는 실제 부하를 반영하지 못합니다. 긴 컨텍스트 요청의 급증은 짧은 요청의 급증과 동일하게 보이며, CPU 및 메모리 지표도 실제 GPU 활용도와 유사하게 상관관계가 없습니다.

모델 단위를 사용하면 오토스케일러는 모델 단위 활용률을 기반으로 스케일 업 또는 다운을 결정할 수 있습니다. 추론 엔진이 최대 모델 단위의 특정 비율(하드웨어 유형 및 워크로드 형태에 따라 결정됨)에 가깝게 실행될 때, 이는 최대 처리량에 도달하고 있음을 의미하며 스케일 업을 트리거합니다. 그 반대는 스케일 다운을 트리거합니다. 각 모델에 대해 오토스케일링 규칙을 수동으로 조정하는 대신, 이 접근 방식은 모델에 구애받지 않는 스케일링 인프라를 가능하게 합니다.

LLM 추론 패턴을 기반으로 오토스케일링을 구축함으로써 항상 최대 복제본으로 스케일링하는 것을 피할 수 있었습니다. 트래픽이 급증하는 모델의 경우, 오토스케일링은 복제본 수를 실제 수요에 가깝게 유지하여, 피크 시 정적 프로비저닝과 비교하여 80% 이상의 GPU 절감 효과를 가져왔습니다.

런타임 안정성

스마트 라우팅과 스케일링은 훌륭한 기반을 제공했지만, 엔진 수준의 장애를 방지하지는 못합니다. 어떤 추론 엔진을 배포하든(자체 개발 엔진이든 인기 있는 오픈소스 옵션이든), 프로덕션 규모에서는 엣지 케이스와 리소스 경합이 발생합니다. 우리는 장애를 자동으로 감지하고 복구하는 메커니즘이 필요합니다.

무응답 장애 감지 및 복구

우리가 겪는 한 가지 장애 모드는 무응답 상태입니다. 엣지 케이스(구조화된 출력, 멀티모달 입력)와 관련된 요청은 추론 엔진의 다중 프로세스 아키텍처에서 처리되지 않은 오류를 트리거하여, 서버가 오류를 표시하지 않고 응답을 중단하게 만들 수 있습니다.

우리는 주기적인 블랙박스 상태 확인을 통해 이를 감지합니다. 최근에 실제 요청이 완료되지 않았을 때 최소한의 종단 간 요청을 보냅니다. 상태 확인에 실패하면 Kubernetes 활성 프로브가 서버를 다시 시작합니다. 이는 내부 구현 방식과 관계없이 모든 엔진에서 작동합니다.

그러나 높은 부하에서는 상태 확인 자체도 시간 초과될 수 있으며, 이로 인해 활성 프로브가 실제로는 정상인 서버를 종료시킬 수 있습니다. 이는 연쇄 장애의 위험을 초래합니다. 이를 해결하기 위해 우리는 상태 확인 요청에 가장 높은 스케줄링 우선순위를 부여하여, 높은 부하에서도 완료되도록 보장합니다. 우선순위가 부여된 상태 확인을 통해 무응답 상태를 감지하고, 비정상 서버를 종료하고, 복구하는 전체 주기는 5분 미만이 소요됩니다. 잘못된 활성 프로브 실패는 주당 여러 건에서 0건으로 감소했습니다.

멀티모달 요청으로 인한 예기치 않은 부하 처리

대규모 멀티모달 요청 배치가 도착했을 때, 우리는 완전히 다른 원인으로 인한 오류율 및 시간 초과 급증을 목격했습니다.

조사 결과, 요청이 추론 엔진의 핵심 프로세스에 도달하지도 못하고 있었습니다. 이미지 요청을 처리하는 것은 텍스트 전용 요청보다 더 많은 리소스를 소모하는데, 이는 GPU에서 실행되는 추가 비전 인코더뿐만 아니라 CPU 집약적인 이미지 처리 때문이기도 합니다. 특정 모델의 경우 이미지 처리가 매우 느려서 이벤트 루프를 완전히 차단했습니다.

차단 작업을 별도의 스레드와 프로세스로 옮겨도 문제가 해결되지 않았습니다. 높은 이미지 부하에서는 여전히 요청이 쌓였습니다. 그래서 우리는 Python 프로세스를 프로파일링하여 몇 가지를 발견했습니다.

  • 이미지에 대한 모든 CPU 작업 중에서 이미지 처리(크기 조정 및 정규화)는 base64 디코딩과 같은 다른 작업보다 10배 느립니다.
  • 일부 Hugging Face 모델은 기본적으로 PIL 기반 이미지 프로세서를 사용하지만, 다른 모델은 더 빠른 Torchvision 기반 프로세서를 사용합니다.
  • 컨테이너화된 환경에서 OMP_NUM_THREADS (Torch가 CPU 작업에 사용하는 OpenMP 스레드 수를 제어함)는 호스트 머신의 vCPU 수로 기본 설정됩니다. 다중 테넌트 설정에서는 이는 좋지 않은 기본값입니다. 호스트에는 192개의 vCPU가 있을 수 있지만, 컨테이너는 12개만 액세스할 수 있습니다. 그 결과 사용 가능한 코어보다 훨씬 많은 스레드가 실행됩니다. 이는 컨테이너의 제한을 넘어 CPU 사용량을 증가시키고 스로틀링을 유발합니다.

Torchvision 기반 이미지 프로세서로 전환하고 OMP_NUM_THREADS를 올바르게 구성함으로써, 우리는 훨씬 더 높은 QPS를 유지하고 GPU를 완전히 활용했습니다. 수정 사항이 적용된 후, 동일한 복제본과 부하에서 초당 완료되는 요청 수가 3배 이상 증가했습니다. CPU 스로틀링이 사라졌고, 서버는 훨씬 더 건강한 상태로 작동했습니다.

그림 5: 이미지 처리 병목 현상을 최적화한 후 서버당 RPS
그림 5: 이미지 처리 병목 현상을 최적화한 후 서버당 RPS

결론

대규모로 LLM을 안정적으로 제공하려면 추론 스택의 모든 계층에서 작업이 필요합니다. 우리는 LLM 워크로드에 맞춰 설계된 오토스케일링 및 로드 밸런싱 인프라와 엔진 또는 워크로드에 관계없이 안정적으로 유지되는 런타임 메커니즘을 다루었습니다. 이 외에도 빠른 컨테이너 시작, GPU 플릿 전반에 걸친 안전한 롤아웃, 클라우드 및 지역 전반에 걸친 GPU 용량 관리 등 더 많은 이야기가 있습니다. 이러한 종류의 문제에 관심이 있다면, 저희는 채용 중입니다!

(이 글은 AI의 도움을 받아 번역되었습니다. 원문이 궁금하시다면 여기를 클릭해 주세요)

최신 게시물을 이메일로 받아보세요

블로그를 구독하고 최신 게시물을 이메일로 받아보세요.