DEV Community

GyeongSeon
GyeongSeon

Posted on

LLM 서빙, 왜 Ray 여야만 했을까?

안녕하세요! 오늘부터 새로운 시리즈를 통해 제가 거대한 언어 모델(LLM)을 효율적으로 서빙하기 위해 쿠버네티스 환경에서 Ray를 활용하고, 나아가 이 모든 과정을 자동화하는 Managed API를 구축했던 여정을 공유해보고자 합니다.

요즘 어딜 가나 LLM 이야기뿐입니다. 많은 팀이 LLM을 활용해 새로운 가치를 만들고 싶어 하지만, 멋진 아이디어를 현실로 만드는 길은 생각보다 험난합니다. 특히 프로토타입을 넘어 수많은 사용자의 요청을 실시간으로 처리해야 하는 '서빙' 단계에 이르면, 비싼 GPU 비용, 까다로운 동시성 처리, 변화무쌍한 트래픽에 대한 스케일링 등 현실적인 문제들이 발목을 잡곤 하죠.

이번 첫 번째 글에서는 제가 마주했던 이 문제들을 어떻게 정의했고, 수많은 선택지 속에서 왜 최종적으로 'Ray'라는 카드를 선택하게 되었는지, 그 고민의 과정을 솔직하게 이야기해보려 합니다.

🎯 제가 풀어야 할 숙제: 거대 모델을 제 손으로 직접 서빙하기

저의 목표는 명확했습니다. '효율적인 LLM 모델 배포 및 운영'.

하지만 '효율적'이라는 단어 안에는 여러 가지 의미가 담겨 있었습니다. 고민 끝에, 제가 구축할 LLM 서빙 인프라는 다음과 같은 요구사항을 만족해야 했습니다.

  • ⚡ 높은 동시성(High Concurrency): 수많은 사용자의 요청을 지연 없이 동시에 처리할 수 있어야 한다.
  • 🌊 탄력적 확장성(Elastic Scaling): 트래픽이 몰릴 때는 자원을 늘리고, 한가할 때는 자원을 줄여 비용을 최적화해야 한다.
  • 💰 자원 효율성(Resource Efficiency): 비싼 GPU 자원을 낭비 없이 최대한 활용해야 한다. 하나의 GPU를 여러 모델이 나눠 쓰거나, 필요에 따라 유연하게 할당할 수 있어야 한다.
  • 🧩 프레임워크 유연성(Framework Flexibility): vLLM, TGI(Text Generation Inference) 등 다양한 최신 추론 프레임워크를 쉽게 통합하고 테스트할 수 있어야 한다.
  • 🧑‍💻 개발 편의성(Developer Experience): 인프라에 대한 깊은 지식이 없는 개발자도 간단한 API 호출만으로 모델을 배포하고 관리할 수 있도록 모든 과정을 추상화해야 한다.

이 까다로운 조건들을 만족시킬 도구를 찾기 위한 기나긴 여정이 시작되었습니다.

🤔 수많은 선택지 속에서, 왜 'Ray'였을까?

단순히 쿠버네티스 Deployment와 Service를 활용하는 방법부터 KServe, BentoML 같은 전문 서빙 프레임워크까지, 정말 다양한 도구들을 검토했습니다. 각 도구는 저마다 훌륭한 장점을 가지고 있었지만, 저의 모든 요구사항을 만족시키기엔 조금씩 아쉬운 부분이 있었습니다.

그러던 중, 제 눈에 들어온 것이 바로 Ray였습니다. 처음에는 분산 처리를 위한 라이브러리 정도로만 생각했지만, 깊게 들여다볼수록 Ray는 제가 찾던 '해답'에 가장 가까운 도구였습니다.

  1. 파이썬 개발자에게 너무나 자연스러운 확장

    LLM과 머신러닝 생태계의 중심은 단연 파이썬입니다. Ray의 가장 큰 매력은 기존 파이썬 코드를 거의 그대로 유지한 채 분산 환경으로 확장할 수 있다는 점이었습니다. 복잡한 분산 시스템 개념을 배우기 전에, 익숙한 파이썬 함수나 클래스에 @ray.remote 데코레이터 하나만 붙이면 마법처럼 코드가 클러스터 환경에서 실행되는 경험은 정말 강력했습니다.

  2. 유연하고 강력한 분산 처리 모델 (Actor Model)

    LLM 서빙은 모델을 GPU 메모리에 올리고, 여러 요청을 상태를 유지하며(stateful) 처리해야 하는 대표적인 작업입니다. Ray의 액터(Actor) 모델은 이러한 stateful 워크로드를 처리하는 데 최적화되어 있습니다. 특정 모델을 특정 GPU에 할당한 액터로 만들고, 요청이 들어올 때마다 해당 액터에게 작업을 전달하는 방식으로 서빙 로직을 매우 직관적으로 구현할 수 있었습니다.

  3. 똑똑한 GPU 자원 관리

    "3번 노드의 1번 GPU가 비어있나?" 같은 질문은 더 이상 할 필요가 없습니다. Ray는 클러스터 전체의 GPU를 하나의 거대한 풀(pool)처럼 관리합니다. 우리는 그저 "GPU 0.5개가 필요한 액터를 만들어줘" 라고 요청하기만 하면, Ray 스케줄러가 알아서 가장 적합한 노드에 작업을 할당해 줍니다. 이러한 추상화 덕분에 복잡한 자원 관리 로직을 직접 구현하는 수고를 덜 수 있었습니다.

  4. LLM 서빙을 위해 태어난 듯한, Ray Serve

    Ray의 여러 라이브러리 중에서도 Ray Serve는 LLM 서빙을 위한 기능의 집약체였습니다. 들어오는 트래픽에 따라 자동으로 액터(파드) 수를 조절하는 오토스케일링, 여러 요청을 묶어 GPU 효율을 높이는 동적 배치(Dynamic Batching), 여러 모델을 조합하여 파이프라인을 만드는 기능(Deployment Graph) 등 제가 원했던 대부분의 기능이 이미 구현되어 있었습니다.

🚀 앞으로 펼쳐질 여정: KubeRay부터 Managed API까지

물론 Ray가 모든 것을 해결해주는 '은 탄환'은 아니었습니다. 오히려 Ray라는 강력한 도구를 제 손에 맞게 길들이는 과정은 또 다른 도전의 연속이었습니다.

앞으로 이어질 시리즈에서는 다음과 같은 이야기들을 하나씩 풀어가려고 합니다.

  • 2부: 쿠버네티스에 Ray 클러스터를 구축하는 가장 쉬운 방법, KubeRay 첫 삽 뜨기
  • 3부: LLM 서빙의 정석, vLLM과 Ray Serve를 활용한 고성능 추론 엔드포인트 구축기
  • 4부: 이상과 현실 사이, TGI(Infinity)와 Ray를 연동하며 겪었던 제약사항과 삽질의 기록
  • 5부: GPU 한 방울까지 짜내기, Ray 클러스터 GPU 활용 극대화를 위한 운영 팁
  • 6부: 모든 것을 하나로, LLM 서빙을 위한 Ray Managed API 설계 및 회고

이 시리즈가 저처럼 LLM을 안정적으로, 그리고 효율적으로 서빙하는 방법에 대해 고민하는 분들께 작게나마 도움이 되기를 바랍니다.

다음 글에서는 KubeRay를 활용하여 쿠버네티스 위에 Ray 클러스터를 구축하는 구체적인 방법으로 찾아뵙겠습니다. 많은 기대 부탁드립니다!

Top comments (0)