Tech

새로운 루다를 지탱하는 모델 서빙 아키텍처 — 2편: ArgoCD와 모델 서빙

A/B 테스트까지 가능한 안정적인 모델 서빙 인프라 구조 설계 방법

정채홍 김만수 | 2023년 02월 20일 | #Engineering #Machine_Learning

지난 블로그 글에서 빠르게 변화하는 모델 아키텍처를 위해 백엔드 서버와 모델 추론 서버를 분리시키고, A/B 테스트를 위해 모델의 추론을 추상화하여 간단한 설정을 수정하는 것만으로 실험을 진행할 수 있는 구조에 대해 소개했어요. 이번 글에서는 1) 각 서버의 관심사를 분리하기 위해 어떻게 모델 추론 구조를 추상화했는지, 2) 모델 A/B 테스트를 손쉽게 배포 & 수행할 수 있는 구조를 어떻게 구현했는지 기술적으로 조금 더 자세히 이야기해보고자 해요.

모델 추론을 유연하게 - 모델 파이프라인 서버

너티(Nutty) 플랫폼에서 사용자와 채팅하는 이루다 서비스 백엔드와 이루다의 답변을 생성하는 모델 추론 서버는 완전히 분리되어 있어요. 서비스 백엔드가 다음 답변을 생성하기 위해서는 오로지 모델 추론 서버에 답변 생성 요청만 하면 되죠. 정확히는 A/B 테스트를 위해 A/B Proxy 서버에 요청을 보내고, A/B Proxy 서버가 실험군에 맞는 모델 추론 서버로 요청하게 되죠. 그럼 이 모델 추론 서버는 어떻게 만들어야 할까요? 아키텍처가 빠르게 변화할때 이 구조에 맞춰서 새롭게 추론 서버를 작성해야 할까요?

핑퐁팀에서는 모델 추론 구조를 일종의 파이프라인으로 정의했어요. 답변 추론을 하기 위해 미리 정의된 파이프라인을 따라 여러 모델들 사이로 데이터가 오가면서 최종 답변 하나를 정하는 작업이 된 것이죠. 이 작업을 해주는 서버를 구조화하여 모델 파이프라인 서버가 개발되었어요. 각 파이프라인은 최종 답변을 생성하는 각 추론 작업을 의미하고, 비슷한 구조를 갖는 것들은 설정을 일부 공유하거나 파이프라인을 상속하면서 구조가 매우 단순화 되었어요.

모델 파이프라인 서버는 실제로 모델 추론에 깊게 관여하지는 않아요. 모델 추론은 실제로 모델 서버를 통해 이루어지게 되는데, 모델 서버는 각 모델의 성격에 맞추어 별도로 배포돼요. 모델 파이프라인 서버는 모델 서버 또는 Faiss와 같이 추론에 필요한 서버들과 통신하는 서비스를 통해 서버의 입출력 결과를 받아오게 됩니다. 예를 들어, Retrieval 방식과 Generation 방식은 각각 아래 그림처럼 추론 과정을 거치게 돼요.

모델 파이프라인 서버를 통해 모델 아키텍처가 변경된다면 새로운 파이프라인 코드를 작성하고, A/B 테스트 진행 시 모델 서버들과 모델 파이프라인 서버만 배포하면 실험 준비는 끝나게 돼요. 모델 서버를 만들고 서빙하는 방법은 다음 블로그 글에서 더 자세히 다루어보도록 할게요.

모델 인프라를 위한 환경 구성

추론 구조를 추상화 한 모델 파이프라인 서버와 모델 추론을 위한 모델 서버가 준비 되었으면, 이제 이 서버들을 배포해야겠죠? 😀

핑퐁팀에서는 루다를 배포할때 서비스 백엔드를 포함해 모델 인프라도 쿠버네티스(Kubernetes) 인프라 환경에 배포하고 있어요. 자세하게는 헬름 차트(Helm Chart)로 배포 컴포넌트를 구성하고, ArgoCD를 통해 쿠버네티스 클러스터에 배포하는 전략을 사용하고 있죠. 먼저 헬름 차트로 배포 컴포넌트를 어떻게 구성하는지 다뤄볼게요.

배포 컴포넌트 준비

Components 항목은 각 서버들을 구동시키기 위한 정보를 담은 차트들의 모음이에요. 여기서 서버 구동에 필요한 설정들을 정의해요. 서버 단위의 테스트(예를 들어 부하 테스트)도 가능하도록 독립적으로 구성되어있어요. 하단에서 기술할 다른 항목들이 없어도 배포할 수 있죠. 여기에 모델 서버와 모델 파이프라인 서버의 차트가 작성되어 있습니다.

Infrastructures 항목은 특정 아키텍처를 위한 추론에 필요한 서버들을 조합한 차트를 의미해요. 모델 서버들과 모델 파이프라인 서버 모두를 담아서 이 차트를 배포한 것만으로도 모델 인프라를 구성할 수 있어요. 이때 각 환경에 따라 기능들을 활성화 혹은 비활성화 하는 설정들이 환경에 따라 values.yaml 파일로 구분되어 있다는 점이 특징이에요. 예를 들어 테스트 환경에서는 Auto Scaling이 필요하지 않으니 이를 꺼두는 기능을 넣는 것이죠. 다만 이 항목은 클러스터 마다 사용하는 기능이나 설정이 조금씩 달라 이를 조정하려고 사용하는 것이기 때문에, 절대로 컴포넌트의 환경 설정들을 변경하면 안돼요. 그렇게 되면 테스트 환경과 실제 배포 환경과의 괴리가 생기기 때문에 안정성을 보장할 수 없게 되죠.

Applications 항목은 후술할 ArgoCD의 App of Apps 패턴이 적용된 어플리케이션 차트에요. Infrastructures 항목까지는 모델 인프라를 위한 헬름 차트의 정의였다면, Applications 항목은 ArgoCD에 실제로 배포될 어플리케이션이죠. 여기서 A/B 테스트에 따른 모델 인프라를 선택하여 values.yaml 파일을 작성하고, 이 차트만 배포하는 것만으로 여러 환경에서 A/B 테스트까지 한번에 할 수 있게 되어있어요. ApplicationSet을 사용하여 여러 환경에 배포할 수도 있지만, 비용 절감 측면에서 비정기적으로 테스트 환경의 어플리케이션들을 내리기 때문에 사용하지 않았어요.

배포 컴포넌트를 구성하기 위해 여러 단계의 차트가 있어 복잡해보일 수 있지만, 실제로 주의 깊게 작성해야 하는 부분은 Components 항목 뿐이에요. 다른 항목들은 모델 아키텍처 구성에 필요한 컴포넌트를 활성화 시키거나 A/B 테스트를 위해 정의된 모델 인프라를 선택하는 부분이기 때문이죠. A/B/n 테스트 역시 쉽게 확장이 가능하며, 실험군 사이에서 사용되는 컴포넌트들을 대부분 공유하기 때문에 컴포넌트의 재사용성 또한 높다고 볼 수 있어요.

안정적인 배포와 운영을 위한 전략

이제 모델 인프라를 뚝딱 배포할 수 있는 기반은 마련되어 있어요. 하지만 걱정할 부분이 없는 것은 아니에요. 안정적으로 모델 인프라 배포와 A/B 테스트를 위해 핑퐁팀에서 취하는 몇가지 전략을 소개드려볼게요.

1. 테스트 환경

실제 배포 환경에서 정상적으로 동작하는지 확인하기 위해서는 엄밀히 따지면 실제로 배포를 해보는 수밖에 없겠죠. 이를 위해 많은 회사들이 취하는 전략이 Development/Stage/Production 환경을 분리하여 테스트를 진행하는 것이에요. 서버를 띄워서 유닛 테스트하는 것부터 실제 유저가 사용하는 방식처럼 End-to-End 테스트를 진행할 수 있어야 하죠. 핑퐁팀에서도 이 전략을 따라 모델 인프라 자체를 여러 클러스터에 차례로 배포하여 기능을 테스트하고 있어요. 단일 서버를 띄우는 것이 아닌 인프라를 배포해야하고 테스트 해야하기 때문에 이 과정이 조금 복잡해질 수밖에 없는데, 이를 모두 지원할 수 있도록 구성한 것이 앞서 설명한 컴포넌트 구성 방식이에요. 클러스터마다 차트 하나만 배포해두면 A/B 실험군이 반영된 모델 인프라가 구성되니 빠르게 배포할 수 있겠죠.

2. Graceful Shutdown과 무중단 배포

새로운 모델 아키텍처나 A/B 테스트를 위해 안정적인 업데이트가 필요해요. 단일 서버만 업데이트 한다고 했을때는 서버 자체가 Graceful Shutdown만 지원하면 롤링 업데이트를 통해 서비스 중단 없이 안정적으로 배포할 수 있죠. 하지만 루다와 같이 모델 인프라 자체를 안정적으로 업데이트하고 Graceful Shutdown을 할 수 있도록 하려면 조금 다른 방식으로 이를 지원해야 했어요.

다시 한번 모델 인프라의 구조를 상기시켜보면, 루다의 모델 인프라는 단일 서버가 아닌 계층적인 구조를 지닌 서버의 집합으로 이루어져 있어요. 이러한 구조에서 Graceful Shutdown이 문제없이 진행되려면 상위 서버부터 순차적으로 Graceful Shutdown이 진행되어야 해요. 즉, 루다의 모델 인프라는 모델 파이프라인 서버가 먼저 Graceful Shutdown이 가능하도록 해야 해요.

이렇게 되면 모델 파이프라인 서버를 먼저 닫으려고 시도하면 모델 인프라 상의 모든 서버들은 더 이상 트래픽을 받지 못하게 되고, 닫히기 직전에 처리하던 트랜잭션들은 모델 파이프라인 서버가 닫히기 전에 모두 처리하기 때문에 처리하지 못하는 트랜잭션은 없게 돼요. 따라서 모델 인프라는 모델 파이프라인 서버를 먼저 닫히게끔 한 후에 다른 모델 서버를 닫게 되면 모델 인프라가 전체적으로 Graceful Shutdown을 지원할 수 있게 되죠. 이는 ArgoCD의 SyncWave를 사용해서 조정할 수 있어요.

다음으로 무중단 배포를 위해 블루/그린 배포 전략을 취하고 있어요. A/B 테스트의 대상이 피쳐나 서버가 아닌 인프라이기 때문에 모델 인프라를 새로 띄워야만 하죠. 모델 서버나 모델 파이프라인 서버들을 롤링 업데이트하는 방식은 변경된 추론 구조가 서로 호환되지 않는 컴포넌트가 있다면 장애가 발생하기 때문에 너무 위험했어요. 따라서 무중단 배포를 하기 위해 각 서버들의 버그를 수정한 버전이라면 각각 롤링 업데이트로 배포하고, A/B 테스트와 같이 추론 구조가 변경된 것이라면 모델 인프라를 블루/그린 배포 전략으로 배포하고 있어요. 모델 인프라가 모두 띄워졌다면 트래픽의 경로를 바꾸어주기 위해 “A/B Proxy 서버”의 설정을 변경하는 것으로 A/B 테스트를 위한 배포를 마무리 합니다.

3. A/B 테스트 군에 따른 트래픽 분산과 확장성

핑퐁팀에서 모델 서버들을 보다 안정적으로 유지하기 위해 파드가 사용하는 리소스 외에 초당 요청 수(RPS) 또한 스케일링의 지표로 삼고 있어요. 모델 서버는 일반적인 REST API 서버 대비 요청당 리소스 사용량이 높기 때문에 초당 트랜잭션 처리량이 높지 않고, 리소스 사용량이 불규칙해요. 그래서 이전에 핑퐁팀에서 소개드린 Custom Metric(ex. RPS)으로 HPA 설정하기로 모델 서버를 RPS에 기반하여 스케일링 되도록 하고 있죠. 각 서버들은 각자 안정적인 지연시간(Latency) 이하로 응답을 보내도록 제약을 두어 최대 버틸 수 있는 RPS를 측정해요. 그런 다음 각 모델 인프라로 향하는 트래픽과 A/B 테스트 군의 비율, 각 서버의 최대 RPS를 고려하여 늘려야하는 서버의 파드 수를 계산하죠. 이렇게 계산된 파드 수에 따라 HPA가 알아서 파드의 개수를 조절하여 각 서버가 실제로 처리하는 트래픽 양에 따라 확장 가능하도록 설계되어 있습니다. 이로써 더 적은 A/B 테스트 군은 더 적은 서버만 띄울 수 있기 때문에 비용도 절감됩니다.

4. 모니터링

모델 인프라를 모두 배포하고 나면 실제로 트래픽이 잘 흐르는지, 장애가 발생하지 않는지 모니터링 시스템을 통해 확인하고 있어요. 이를 통해 실험군 비율에 따라 트래픽이 잘 나뉘어 흐르는지, 각 서버들이 예상한 최대 RPS에 맞게 스케일링 되는지 등을 확인해요. 더 자세한 모니터링 방식은 블로그 글의 마지막 시리즈에서 다뤄보도록 할게요.

모델 인프라 배포 (w.r.t. App of Apps)

App of Apps 패턴은 ArgoCD에서 클러스터 부트스래핑을 위한 전략이에요. 새로운 클러스터를 생성하고 클러스터 환경을 일일이 맞춰주지 않고 단 한 번의 배포만으로 클러스터에 올라갈 환경과 어플리케이션들을 모두 배포할 수 있는 전략이죠. 핑퐁팀에서는 이 패턴을 모델 인프라를 배포하기 위한 방법으로 사용하고 있어요. 각각의 어플리케이션들은 A/B 테스트 군에 해당하는 모델 인프라로 구성하고, 위에서 언급한 안정적인 배포를 위해 컴포넌트 사이의 배포 및 셧다운 순서를 정의해요. 단 한 번의 배포만으로 모델 인프라 뿐만 아니라 A/B 테스트 환경까지도 배포가 되는 것이죠. 뿐만 아니라 모델 인프라 자체는 ArgoCD의 각 어플리케이션으로 배포되다보니 자연스레 소멸과 생성이 빈번하지만, 이 패턴으로 적용한 하나의 어플리케이션은 실험과 관계 없이 한 개만 존재하다 보니 장애가 발생했을때 롤백이 쉽다는 장점과 배포 이력을 확인할 수 있다는 장점이 있어요.

커스터마이징

App of Apps로 배포하게 되면 순정 ArgoCD에서는 어플리케이션의 배포 순서를 정의할 수 없어요. 그래서 공식 문서에서 설명한 방법대로 어플리케이션의 상태를 배포하는 리소스들의 상태를 보고 결정될 수 있도록 수정하였죠. 이외에도 일부 특수한 상황들에서 Out of Sync가 발생하거나 Degraded 상태가 발생하는데, ArgoCD는 먼저 배포되어야 하는 리소스들이 정상적으로 배포가 되어야지만 다음 순서의 리소스도 배포하기 때문에 이를 적절히 처리하기 위해 커스텀 Health Check도 적용하고 있답니다.

마치며

이 글에서는 모델 아키텍처가 빠르게 변화하는 상황 속에서 A/B 테스트까지 가능하면서 안정적인 인프라 구조를 어떻게 설계했는지에 대한 과정을 소개했어요. 이외에도 해당 본문 내에 담고 싶었던 내용들이 여럿 있어요. 기회가 되면, 아래 내용들 또한 블로그로 공유하려고 해요!

핑퐁팀은 항상 기능 구현 뿐만 아니라, 서비스 지속성/안정성을 위한 최적의 솔루션을 찾도록 노력하고 있고 이런 문제에 대해 고민할 수 있는 기회가 열려 있습니다. 저희와 함께 고민하며 이런 문제들을 풀어보고 싶으신 분들은 채용 공고를 참조해주세요!

스캐터랩이 직접 전해주는
AI에 관한 소식을 받아보세요

능력있는 현업 개발자, 기획자, 디자이너가
지금 스캐터랩에서 하고 있는 일, 세상에 벌어지고 있는 흥미로운 일들을 알려드립니다.