스크립트
00:00:00- 안녕하세요, 발표에 참석해 주셔서 감사합니다.
00:00:14저는 루이고, 제 동료 체인루입니다.
00:00:17오늘 저희가 다룰 주제는
00:00:20저희 둘 다 OpenAI의 추론 팀에서 일하고 있는데요,
00:00:23오늘 이야기해 볼 주제는
00:00:24프로덕션 환경에서의 LLM 추론 라우팅입니다.
00:00:27구체적으로는 저희 시스템이 어떻게 진화했는지,
00:00:29엔진 신호 기반 피드백 루프 라우팅에서
00:00:32더 명확하고 예측 가능한 정책으로
00:00:35어떻게 발전했는지 이야기하려 합니다.
00:00:37여전히 엔진 신호의 정보를 활용하긴 하지만
00:00:39그 신호를 활용하는 방식 자체가 달라진 셈이죠.
00:00:43오늘 아젠다를 말씀드리면,
00:00:44우선 추론 로드 밸런서가
00:00:46무엇인지 소개하며 시작할 것입니다.
00:00:48어떤 역할을 하고 어떻게 진화해 왔는지 말이죠.
00:00:51이어서 체인루가 새로운 제어 평면과
00:00:53데이터 평면 기반 아키텍처를 설명하고,
00:00:56각각의 역할이 무엇인지 알아본 뒤
00:00:59글로벌 네트워크 오버헤드를 줄인
00:01:01구체적인 사례 연구를 살펴보겠습니다.
00:01:05마지막에는 제가 다시 돌아와서
00:01:07프로덕션 수준의 부하 속에서도 시스템을 안정적으로
00:01:10유지해 주는 보호 메커니즘을 설명하겠습니다.
00:01:13우선, 추론 로드 밸런서란 무엇일까요?
00:01:17그리고 어디에 위치할까요?
00:01:20이것은 저희가 다루는 시스템의 매우 개략적인 다이어그램입니다.
00:01:25왼쪽 편에는 프론트엔드 클러스터들이 있습니다.
00:01:29아 죄송합니다, GPU가 아니라 CPU 클러스터들로,
00:01:33저희 시스템의 게이트웨이 역할을 합니다.
00:01:36사용자 요청을 받아 추론 엔진이 처리할 수 있는
00:01:42추론 요청의 형태로 준비하죠.
00:01:44그리고 오른쪽 편에는 엔진 클러스터들이 있습니다.
00:01:47보통 GPU 클러스터이며, 각각 여러 추론 엔진을 호스팅합니다.
00:01:53그래서 엔진 클러스터라는 이름이 붙은 것이죠.
00:01:56잘 아시겠지만 요즘 GPU는 인기도 많고 매우 비쌉니다.
00:02:02그 중간에 위치한 것이 바로 IRB, 즉 추론 로드 밸런서입니다.
00:02:07실제로는 프론트엔드 클러스터에서 동작하지만, 추론 스택으로 이어지는 다리 역할도 합니다.
00:02:14핵심 역할은 두 가지입니다. 엔진 선택과 요청 근사화죠.
00:02:19이번 발표에서는 엔진 선택 부분에 집중해 보겠습니다.
00:02:23어떤 면에서 IRB는 매우 전통적인 로드 밸런서와 유사합니다.
00:02:28요청은 보통 하나의 모델을 타깃으로 하고, 모델 뒤에는 여러 엔진이 받쳐주고 있으니까요.
00:02:35이 엔진들은 서로 다른 지역의 클러스터나 대륙 건너편에 존재할 수도 있습니다.
00:02:40지역적 성능 저하나 클러스터 장애에 대해 훌륭한 복원력을 제공하기 때문이죠.
00:02:46하지만 추론 스택 혹은 추론만의 특수성으로 인해 수많은 세부사항이 발생합니다.
00:02:53실시간으로 보고되는 여러 신호를 고려해야 하기 때문인데요,
00:02:59잘 알려진 첫 토큰 생성 시간(TTFT), 출력 토큰 간격 시간,
00:03:05즉 토큰 처리량이나 토큰 간격 시간(TBT) 외에도,
00:03:08기타 부하 상태 및 사용률 신호 등이 있습니다.
00:03:11또한 매우 중요한 개념인 KV 캐시라는 것도 잘 알려져 있죠.
00:03:16예를 들어 대화에 유용한 컨텍스트가 이미 특정 엔진에 캐시되어 있다면,
00:03:22동일한 대화의 후속 요청을 다시 동일한 엔진으로 보내는 것이
00:03:27재계산을 방지하고 효율성을 높이며 지연 시간을 줄여줍니다.
00:03:32따라서 성능, 신뢰성, 데이터 인접성, 캐시 인지의 조합이 이 문제를 흥미롭게 만드는 요소입니다.
00:03:39그렇다면 저희가 이 문제에 어떻게 접근했는지 초기 시절을 살펴보겠습니다.
00:03:45솔직히 말씀드리면 이 분야에서 초기라고 하면 실제 지나간 시간보다 훨씬 아득하게 느껴지네요.
00:03:51당시의 라우팅 프로세스는 필터링부터 시작했습니다. 모든 요청을 모든 엔진이 처리할 수 있는 것은 아니었으니까요.
00:04:01성능상의 제약이나 컴퓨팅 자원, 데이터 거주성 문제로 인한 지리적 제한이 원인이었죠.
00:04:08그리고 남아있는 엔진들 중에서, IRB는 가중치가 적용된 일관된 해싱을 사용해 특정 사용자의 요청을 처리할 최선의 대상 엔진을 선택했습니다.
00:04:19그러면 그 가중치는 어디서 오는지 중요한 질문이 생깁니다.
00:04:24가중치는 주기적인 피드백 루프에 의해 생성되었습니다.
00:04:27앞서 언급했듯 추론 엔진은 관심 대상이 되는 온갖 종류의 신호를 보고합니다.
00:04:33그리고 컨트롤러는 주기적으로 이 신호들을 평탄화하여 성능 점수를 계산합니다.
00:04:40이 성능 점수를 전체 플릿의 평균과 비교하게 되는 것이죠.
00:04:45그런 뒤 가중치가 조정됩니다. 기본적으로 성능이 더 좋아지면
00:04:52가중치가 올라가고, 플릿 평균보다 나쁘면 가중치가 내려가는 방식입니다.
00:04:57이렇게 생성된 가중치는 라우팅에 영향을 주며, 기본적으로 하나의 제어 루프를 이룹니다.
00:05:05개념적으로는 PID 제어기와 매우 유사합니다.
00:05:10참고로 이 PID 제어기는 리눅스 프로세스를 강제 종료(kill)하는 데 쓰이지는 않고요.
00:05:13대신 시스템을 원하는 상태로 지속적으로 유도하는 고전적인 제어 이론 기술입니다.
00:05:20저희는 이 중요한 개념 중 비례(Proportional) 제어 부분을 가져와 시스템에 적용한 것이죠.
00:05:27그래서 이 방식은 매우 훌륭한 특성들을 많이 가지고 있습니다.
00:05:33예를 들어 우리가 신경 쓰는 유용한 신호들을 단일 라우팅 결정으로 결합할 수 있었습니다.
00:05:40그리고 관찰된 성능에 맞춰 적응하기 때문에, 앞서 말씀드렸듯 많은 제약 조건이 존재하더라도 말이죠.
00:05:45그러한 제약으로 인해 일부 엔진은 다른 엔진보다 더 다양한 요청을 처리할 수 있어서 더 바빠질 수 있습니다.
00:05:53하지만 바쁜 상태의 신호가 다음 루프에 반영되면, 제약이 적은 요청들이 이러한 유형의 엔진으로 더 많이 이동하게 됩니다.
00:06:03결과적으로 알아서 균형을 찾아가는 것이죠.
00:06:05어느 정도는 수동 개입을 많이 할 필요가 없다는 의미입니다.
00:06:11그냥 잘 동작하니까요.
00:06:12하지만 그런 적응성에는 큰 트레이드오프가 따릅니다.
00:06:16너무 많은 신호를 조합한다는 바로 그 이유 때문에 특정 라우팅 결정을 이해하기가 매우 까다롭고,
00:06:22특정 엔진이 왜 예상보다 더 높은 가중치를 받았는지 원인을 파악하기 어렵습니다.
00:06:28그리고 특정 측면을 미세 조정하려 할 때마다 다른 부분에 영향을 주지 않기가 거의 불가능했습니다.
00:06:36또한 부하가 항상 고르게 분산되는 것은 아닌데, 때로는 하나의 모델이 서로 다른 GPU 규모의 엔진에서 제공되고
00:06:48그 엔진들이 각기 다른 특성을 가지고 있어서 문제가 훨씬 더 까다로워집니다.
00:06:53게다가 피드백 루프가 때로는 심한 진동을 일으킵니다. 트래픽을 특정 엔진에서 다른 곳으로 돌리면
00:07:03해당 엔진의 부하가 식게 되고, 이 신호가 컨트롤러에 전달됩니다.
00:07:08그러면 컨트롤러는 “아, 이 엔진이 트래픽을 훨씬 더 받겠구나”라고 판단하죠.
00:07:12그러고는 트래픽이 몇몇 엔진 사이를 오가며 요동치게 되고, KV 캐시 활용을 방해하게 됩니다.
00:07:20이러한 모든 한계로 인해 저희는 아키텍처를 재검토하고 이 문제를 해결할 새로운 방법이 있는지 고민하게 되었습니다.
00:07:29이제 체인루에게 마이크를 넘겨 저희가 시도해 본 새 아키텍처를 자세히 들어보겠습니다.
00:07:38네, 감사합니다 루.
00:07:40저는 로드 밸런서의 아키텍처와, 라우팅 알고리즘을 통해 전체 오버헤드를 어떻게 줄였는지 말씀드리겠습니다.
00:07:50로드 밸런서는 한 가지 질문에 답합니다.
00:07:53CPU 클러스터에서 들어오는 각 요청을 어떤 엔진이 처리해야 할까요?
00:07:59가장 단순한 기준은 엔진에 요청을 균등하게 보내는 라운드 로빈일 것입니다.
00:08:06하지만 조금만 더 생각해 보면 이건 적절하지 않습니다.
00:08:09엔진은 동질적이지 않고, 하드웨어 성능과 용량이 다를 수 있으며
00:08:15상태도 다르고 CPU 클러스터와의 거리도 제각각이기 때문이죠.
00:08:19또한 라운드 로빈은 캐시 지역성을 깨뜨릴 수 있습니다.
00:08:24동일한 엔진 캐시를 재사용할 수 있는 연관된 요청들이 서로 다른 엔진으로 보내질 수 있으니까요.
00:08:30더 나은 해결책은 각 CPU 클러스터가 자체 로컬 관점에서 가장 좋은 엔진을 선택하는 것일 수 있습니다.
00:08:39하지만 그것만으로도 부족합니다.
00:08:41극단적인 경우를 생각해 보세요. 여러 CPU 클러스터가 트래픽을 독립적으로 동일한 엔진에 보낸다면,
00:08:48해당 엔진은 과부하가 걸리고 다른 엔진들은 충분히 활용되지 못할 수 있습니다.
00:08:53따라서 우리에게 필요한 것은 전역적으로 최적화된 솔루션입니다.
00:08:57모든 CPU 클러스터와 GPU 엔진을 아우르는 전역 뷰를 가진 제어 평면이 필요하며,
00:09:02전역적으로 최적화된 라우팅 결과값을 계산할 수 있어야 합니다.
00:09:06그리고 데이터 평면은 제어 평면에서 가져온 답을 바탕으로 라우팅 결정을 빠르게 내릴 수 있죠.
00:09:18이제 제어 평면과 데이터 평면 내부를 살펴보겠습니다.
00:09:22데이터 평면에는 각 요청에 맞게 엔진을 선택하는 엔진 셀렉터가 있습니다.
00:09:28이 셀렉터는 후보 엔진들과 각 후보 엔진의 라우팅 가중치가 포함된 로컬 라우팅 상태를 읽습니다.
00:09:37이 두 가지 정보는 모두 백그라운드에서 비동기적으로 갱신됩니다.
00:09:41따라서 각 요청에 대한 라우팅 결정을 내리기 전에 제어 평면에 물어볼 필요가 없습니다.
00:09:48또한 데이터 평면은 준비된 복제본 수, 엔진 상태 등과 같은 실시간 엔진 신호를 수집하여
00:09:56빠른 로컬 가드레일 역할을 하도록 합니다.
00:10:00제어 평면에서는 데이터 로더가 실시간 엔진 신호와 네트워크 오버헤드를 결합합니다.
00:10:06그리고 용량, TTFT, TBOT에 대한 오프라인 회귀 분석을 통해 옵티마이저가 이 데이터들을
00:10:14라우팅 가중치로 변환합니다.
00:10:16그러면 제어 평면은 각 데이터 평면이 가져갈 수 있도록 라우팅 가중치를 게시합니다.
00:10:21이러한 방식을 통해 어떤 요청도 데이터 평면에서 대기할 필요가 없습니다.
00:10:26제어 플레인은 지속적으로 다음의 전역 최적화된 라우팅 가중치 스냅샷을 계산하고,
00:10:32데이터 플레인은 로컬에 이미 적용된 최신 스냅샷을 기반으로 라우팅 결정을 내립니다.
00:10:42요약하자면, 시스템에는 세 가지 중요한 경로가 있습니다.
00:10:46첫 번째 경로는 추론 요청 경로입니다.
00:10:49요청이 CPU 클러스터에 도달하면, CPU 클러스터 내부의 데이터 플레인이
00:10:55로컬 라우팅 상태를 기반으로 해당 요청을 처리할 엔진을 선택하고, 선택된 엔진으로 요청을 전달합니다.
00:11:03두 번째 경로는 엔진 신호 경로입니다.
00:11:06시스템은 TTFT, TPOT, 준비된 복제본 수,
00:11:15엔진 상태 등 실시간 엔진 신호를 지속적으로 수집합니다.
00:11:17두 플레인 모두 이러한 실시간 엔진 신호가 필요합니다.
00:11:20제어 플레인은 전역 최적화된 라우팅 가중치를 계산하기 위해 신호가 필요하고, 데이터 플레인은
00:11:26빠른 로컬 가드레일을 운영하기 위해 신호가 필요합니다.
00:11:29그리고 세 번째 경로는 라우팅 가중치 경로입니다.
00:11:32제어 플레인이 라우팅 가중치를 계산하여 게시하면, 데이터 플레인은
00:11:38업데이트를 가져와 로컬 캐시에 저장합니다.
00:11:41따라서 첫 번째 경로만 동기식이지만, CPU 클러스터의 데이터 플레인 내부에서만 이루어지므로 매우 빠릅니다.
00:11:49나머지 두 루프는 비동기식 루프이며, 향후의 라우팅 결정을 개선하기 위한 것입니다.
00:11:59아키텍처 부분은 이 정도입니다만, 아직 질문 하나가 남아 있습니다.
00:12:04이러한 라우팅 가중치는 어떻게 계산할까요?
00:12:10그 질문에 답하기 전에 다른 질문부터 먼저 살펴보겠습니다.
00:12:15그냥 가장 가까운 엔진으로 요청을 보내면 안 될까요?
00:12:20트래픽 수요와 GPU 용량이 지리적으로 균형을 이루지 않기 때문입니다.
00:12:27예를 들어 리전 1에서는 CPU 클러스터 A가 90 RPS를 보내고, 인근 엔진 A는 100 RPS를 처리할 수 있습니다.
00:12:36따라서 이 경우에는 가장 가까운 곳으로만 전달해도 괜찮습니다.
00:12:40반면 리전 2에서는 CPU 클러스터 B가 120 RPS를 보내는데, 인근 엔진 B는 100 RPS만 처리할 수 있습니다.
00:12:49이 경우 모든 것을 로컬에서 처리하려고 고집하면, 초과된 20 RPS는 과부하 상태인
00:12:57엔진 B에서 대기해야 합니다. 한편 리전 3에서는 80 RPS 용량의 엔진 C에서 40 RPS만 사용하고 있습니다.
00:13:05여전히 40 RPS의 여유가 남아 있죠.
00:13:08따라서 클러스터 B의 초과 20 RPS를 엔진 C로 보내면 네트워크 거리는 늘어나지만,
00:13:16엔진 측에서 발생할 수 있는 훨씬 더 큰 대기 시간을 방지할 수 있습니다.
00:13:22따라서 이런 경우에는 더 먼 엔진을 이용하는 것이 엔드투엔드로 더 빠를 수 있습니다.
00:13:27이것이 바로 최단 거리 전송보다 더 나은 라우팅 방식이 필요한 이유입니다.
00:13:34이제 옵티마이저의 블랙박스를 열어보겠습니다.
00:13:39옵티마이저는 네 가지 유형의 입력을 받습니다.
00:13:44각 CPU 클러스터의 요청, 각 엔진까지의 네트워크 지연 시간,
00:13:50사용 가능한 엔진 용량 및 상태, 그리고 TTFT/TPOT 지연 시간 프로필입니다.
00:13:57이를 통해 부하가 증가함에 따라 엔진 측 지연 시간이 어떻게 변하는지 알 수 있습니다.
00:14:04그리고 이러한 입력을 받아 옵티마이저는 출력값인 라우팅 가중치로 변환합니다.
00:14:10라우팅 가중치는 각 CPU 클러스터의 트래픽 중 얼마만큼을 각 GPU 엔진으로 보낼지 나타냅니다.
00:14:19최적화 목표는 명확합니다.
00:14:22모든 라우팅 트래픽 전체의 예상 엔드투엔드 지연 시간을 최소화하는 것입니다.
00:14:28중요한 점은 엔드투엔드 지연 시간에 네트워크 거리와 엔진 측 지연 시간이 모두 포함된다는 것입니다.
00:14:36즉, 인근 엔진이 트래픽을 처리할 여유가 있을 때는 매력적인 선택지이지만,
00:14:43인근 엔진이 모두 포화에 가까워지면 먼 엔진으로 보내는 것이 더 나을 수 있습니다.
00:14:50또한 옵티마이저는 몇 가지 엄격한 제약 조건을 준수해야 합니다.
00:14:56첫째, 모든 트래픽 수요를 라우팅해야 합니다.
00:14:59둘째, 모든 엔진이 유효 용량 범위 내에서 유지되도록 해야 합니다.
00:15:05셋째, 라우팅 가중치를 0 이상으로 유지해야 합니다.
00:15:09이를 통해 제어 플레인은 옵티마이저로부터 라우팅 가중치를 받아 게시합니다.
00:15:17그리고 데이터 플레인은 이를 가져와 전역 최적화된 라우팅 결정을 내리는 데 사용합니다.
00:15:24제 발표 내용은 여기까지입니다.
00:15:26이어서 루 님이 시스템의 보호 메커니즘에 대해 설명해 드리겠습니다.
00:15:33감사합니다, 천루 님.
00:15:34AI 엔지니어로서 우리 모두 알고 있듯이, 프로덕션 환경이 이상적으로만 동작하는 경우는 많지 않습니다.
00:15:45클러스터에 장애가 발생하거나, GPU나 개별 노드의 성능이 저하되거나, 네트워크에 온갖 알 수 없는 문제가 생길 수 있죠.
00:15:55그렇다면 과부하 상태에서도 프로덕션 시스템을 최대한 정상적으로 유지하려면 어떻게 해야 할까요?
00:16:02우리가 사용하는 첫 번째 장치는 페널티입니다.
00:16:05기본적으로 특정 엔진에 이상이 감지되면, 이를 감지하여 해당 엔진으로 향하는 라우팅 가중치를 줄입니다.
00:16:13그렇게 하면 일시적인 문제일 경우 엔진 스스로 회복할 기회를 줄 수 있고,
00:16:22작업자가 개입하여 교체하거나 결함이 있는 하드웨어를 수리할 수도 있습니다.
00:16:28두 번째는 재시도 메커니즘으로, 문제를 완화하는 데 흔히 쓰이는 기술입니다.
00:16:34하지만 때에 따라서는 상황을 오히려 악화시킬 수도 있습니다.
00:16:39시스템이 마비 직전이거나 사용률이 매우 높은 경우가 그렇습니다.
00:16:45재시도는 더 많은 부하를 만들어냅니다.
00:16:47그리고 이 늘어난 부하는 더 많은 실패와 재시도를 불러오며 악명 높은 재시도 폭풍을 일으키죠.
00:16:52그래서 저희는 상한선이나 버짓을 구현하여 재시도를 허용 가능한 수준으로 제한했습니다.
00:17:00또한 이는 동적으로 작동해야 하는데, 시스템이 원활하거나 정상적인 상태일 때는
00:17:07부하가 높을 때보다 훨씬 더 많은 재시도를 감내할 수 있기 때문입니다.
00:17:12마지막으로 부하 차단(Load Shedding)이 있는데, 이는 프로덕션 용량이
00:17:20증가하는 추론 요청량을 감당할 수 없을 때 사용하는 최선의 수단입니다.
00:17:24전체 시스템을 다운시키는 대신,
00:17:27트래픽의 일부를 선제적으로 차단하여 시스템이 완만하게 성능을 낮추며 유지되도록 합니다.
00:17:35이것으로 오늘 발표를 마치겠습니다.
00:17:38참석해 주셔서 감사합니다.
00:17:40저희 둘 다 오늘 오후에 부스 쪽에 있을 예정입니다.
00:17:45추가 질문이 있으시면 언제든 오셔서 이야기 나누어 주시기 바랍니다.
00:17:50감사합니다.
00:17:51감사합니다.
커뮤니티 글
아직 글이 없습니다. 이 영상에 대한 첫 번째 글을 작성해 보세요!
이 영상에 대해 글쓰기