VITO CI/CD 변천사 #2 | Container Orchestration

Choi Geonu
returnzero
Published in
8 min readAug 23, 2022

--

VITO CI/CD 변천사 #1 에서 이어지는 포스팅입니다.

Scalability는 대규모 서비스에서 1순위로 고려해야 할 문제입니다. 아니, 대규모 서비스가 아니더라도 1순위로 중요한 문제일 것입니다. DAU 10명에서 100명으로 넘어갈때에도 Scalability가 발목을 잡을것이고 1,000명에서 10,000명으로 넘어갈때도 발목을 잡습니다. 비단 트래픽만의 문제만이 아닐것입니다. 서비스의 기능이 추가되고 성능이 개선되면서 컴포넌트의 교체, 추가가 빈번하게 일어나는 상황도 하나의 Scalability 문제입니다.

VITO에서도 마찬가지로 Scalability라는 큰 고민이 있었습니다. 기존의 EC2 서버 Set와 Ansible을 기반으로한 시스템에서 ECS로 그리고 최종적으로 Kubernetes로 이전하게된 결정적인 이유였습니다.

Scalability

먼저 기존 시스템에서 VITO팀이 겪었던 어려움을 정리해보겠습니다. Scalability 라는 주제는 위에서도 언급했듯이 다양하게 해석될 수 있는 주제입니다. 우리가 이야기할 Scalability가 어떤 맥락을 가진 이야기인지 먼저 이야기 해보려 합니다.

서버 스케일링

당연하게도, 기존 VITO 서비스는 Application 레벨에서 Scalable 했습니다. 즉 트래픽이 2배로 Linear하게 늘어났을 때 실행되는 컨테이너 수를 2배로 늘리면 트래픽을 버틸 수 있는 구조였지요. 물론 인프라도 또한 Scalable 했습니다. EPM (Enqueue per Minute, 이전 포스팅 참고)이 증가하면 하나의 서버 Set를 추가하고, 감소하면 Set를 제거하는등 자동화가 되어있었죠. 이론적으론 완벽했습니다.

EC2 서버 Set 를 기반으로한 스케일링

하지만 현실은 그렇게 녹록치 않았죠. 우리가 한땀한땀 만든 배포 시스템은 여러가지 문제에 봉착했습니다.

Granularity — 스케일링의 세밀함

VITO 서비스의 Sommers System은 각 컴포넌트간의 개수의 조화가 중요했습니다. 특정 컴포넌트가 뒤따라오는 컴포넌트에 비해 Throughput이 높으면 뒤따라오는 컴포넌트는 처리량을 따라가지 못해 많은 양의 메시지 처리를 지연시키게 됩니다. Queue가 delay 될때 되더라도 각 컴포넌트에 부하가 골고루 나누어져야 각 유저들이 겪을 수 있는 처리 지연시간이 균등하게 나누어질 것 입니다.

이를 위해 VITO 서비스는 앞선 포스팅에서 이야기한 Set라는 단위로 그룹 스케일링을 진행했습니다. 항상 같은 비율로 컴포넌트가 실행되면서 컴포넌트간의 조화를 유지시켰죠.

하지만 이러한 그룹 스케일링은 여러가지 문제를 야기시켰습니다. 첫번째로, 자원의 낭비였습니다. 항상 같은 비율로 스케일링을 하기 위해 스케일링 단위가 가장 큰 컴포넌트를 기준으로 스케일링을 트리거 해야했습니다.

예를 들어, 컴포넌트 A는 한대당 500 EPM을 처리할 수 있고, B는 한대당 100 EPM을 처리할 수 있다고 가정합니다. 이 두개의 컴포넌트를 묶어서 스케일링을 하려면 다음과 같은 단위로 스케일링이 일어나야 했습니다.

- 0 ~ 500 EPM - 컴포넌트 A: 1대, 컴포넌트 B: 5대
- 500 ~ 1000 EPM - 컴포넌트 A: 2대, 컴포넌트 B: 10대
- 1000 ~ 1500 EPM - 컴포넌트 A: 3대, 컴포넌트 B: 15대

위의 예시에서 볼 수 있듯이 컴포넌트 B는 스케일링의 경계선에서 불필요하게 많이 실행 되고 있었습니다. 만약 컴포넌트 B가 GPU를 사용하는 값비싼 자원이라면 이 문제는 두고볼 수 없는 문제였죠.

두번째 문제는 조화의 붕괴였습니다. Set를 구성하기 위한 컴포넌트간의 비율은 이상적인 상황에서의 비율이었습니다. 특정한 종류의 input이 많은 상황에서는 중간에 있는 컴포넌트 A가 효율이 떨어져 처리량이 낮아질 수 있습니다. 이 경우 컴포넌트 A를 더 많이 증설하여 해결할 수 있지만, 스케일링은 항상 그룹 단위로 일어나기에 컴포넌트 A만을 증설하는것은 어려운 일이었지요.

Mutability — 스케일링 정책의 수정

세밀함의 부족함에도 우리 서비스는 미려하게 잘 동작했습니다. 낭비는 조금 있었지만 트래픽의 변화를 잘 버텨냈고, 지연으로 인한 고객들의 불편함도 최소화했습니다. Sommers의 음성인식 엔진이 업데이트 되기 전까지는요.

더 나은 음성인식 성능을 자랑하는 Sommers엔진의 새로운 버전에 대한 연구가 끝나고 서비스에 투입하기 위한 과정에서 우리는 전쟁을 겪어야 했습니다. 기존 컴포넌트들을 위한 Ansible 스크립트들을 새로운 컴포넌트에 맞춰서 대폭 수정해야했고, 각 Set에서의 조화로운 컴포넌트 비율을 새롭게 찾아야 했습니다.

하나의 Set를 구성하는 EC2 인스턴스에서 우리는 각 컴포넌트를 몇대씩 실행해야하는지 계산했습니다. 정해진 vCPU 개수에서 총 12대의 컴포넌트를 띄우면 컴포넌트당 vCPU는 몇 퍼센트씩 돌아가는지 특정 컴포넌트 개수가 부족해서 더 실행하면 다른 컴포넌트들은 얼마만큼의 vCPU와 메모리를 손해보는지 등등 최적값을 찾기 위한 계산식은 어려운 최적화 문제로 변질되었습니다.

또한 어느정도 최적의 비율을 찾더라도 확신이 있을때까지 Auto Scaling을 다시 활성화시키지 못했습니다. 잘못된 기준으로 한 스케일링은 서비스 장애로 이루어지기 때문이었죠.

이러한 어려움들을 겪고 나서 VITO 팀은 기존의 낡은 서비스 구성방식을 버리고 Container Orchestration의 세계로 이사하기로 마음먹었습니다. 장비 스펙을 기반으로 서버를 실행하는 방식이 아닌 컨테이너를 기반으로한 사고를 하기로 방향성을 다시 잡은것이죠.

Container Orchestration

컨테이너를 기반으로한 시스템으로 옮기기 위해서 우리는 Amazon ECS를 출발점으로 삼았습니다. 왜 Kubernetes가 아닌 ECS를 선택했는지에 대한 이야기는 나중에 하도록 하고, ECS로 이전함에 따라 어떤 개선점이 있었는지 이야기 해보도록 하겠습니다.

컨테이너 배치 전략의 유연화

VITO의 기존 시스템에서도 각 컴포넌트는 컨테이너 기반으로 실행되었습니다. 하지만 사용하던 방식은 서버에 JVM 설치해서 Tomcat위에 WAS 컨테이너를 띄우는것과 다름없었죠. 자원의 분배, 배치 전략을 우리가 투명하게 한땀한땀 관리하고 있었습니다.

https://www.44bits.io/ko/post/container-orchestration-101-with-docker-and-aws-elastic-container-service

컨테이너의 배치 전략을 ECS로 위임하면서 우리는 기존보다 더욱 유연한 스케일링 전략을 선택할 수 있게 되었습니다. 스케일링 전략을 Set가 아닌 각 컴포넌트를 단위로한 전략으로 변경하면서 더욱 세밀한 스케일링을 할 수 있게 되었고 EC2 Instance에 대한 스케일링 전략과 컨테이너에 대한 스케일링 전략을 분리하게 되며 더 가볍게 전략을 수정할 수 있었습니다.

정책 수정의 간편화

기존에는 배포 및 스케일링 정책을 수정하려면 Ansible 스크립트와 그를 실행하는 Jenkins Groovy Script를 수정해야만 했습니다. 물론 새로운 사람이 이에 대해 학습하는 비용도 매우 컸고요. 하지만 ECS에선 표준적이고 자동화된 스케일링 정책이 있습니다. ECS 뿐만 아니라 같은 더 많은 기능을 가진 Kubernetes의 환경에서의 경험이 있는 사람이라면 쉽게 정책을 수정할 수 있도록 개선되었지요. 이제 Ansible 스크립트는 단 한줄도 남아있지 않습니다!

돌발상황에 대한 대응 강화

위에서 이야기한 내용과 별개로 AWS에서 인프라를 운영하면 각종 돌발상황을 많이 겪게됩니다. 특정 Zone의 장애상황이 발생하는 이슈, 데이터센터에 GPU 자원이 부족해 할당에 실패하는 이슈 등 예상치 못한 상황을 많이 겪을 수 있지요.

기존 Ansible과 Jenkins기반 스크립트에선 이러한 돌발상황에 대한 예외 처리를 한땀한땀 직접 해주어야 했습니다. GPU 할당에 실패하면 성공할때까지 누군가 계속 할당 시도 버튼을 눌렀어야 했지요. 하지만 ECS와 EKS는 실패한 할당에 대한 재시도 로직이 내장되어있는 등 전세계 사람들이 겪었던 돌발상황에 대한 예외처리가 이미 되어있었습니다.
또한 Multi-AZ도 별다른 노력 없이 구성할 수 있어 간단하게 도입할 수 있었습니다.

그럼에도 Kubernetes로

ECS로 이전하면서 많은 부분에 있어서 성공적인 개선을 이룩했습니다. 첫 Container Orchestration 도구로 ECS를 선택한 이유는 사실 간단했습니다. 팀에서 이전을 리드하는 담당자가 ECS 운영 경험이 훨씬 길었기 때문이었죠.

익숙하고 성공적인 시스템이었음에도 불구하고 우리는 Kubernetes로의 이전을 결심하게 되었는데, 이번 한편의 포스팅에서는 길고 긴 이야기를 모두 풀어내기는 어려울 것 같습니다.

이어지는 포스팅에서는 VITO 팀이 Kubernetes로 이전하게된 이유를 여러 방면에서 이야기하고 현재 VITO 시스템이 어떻게 이루어져 있는지 이야기 해보도록 하겠습니다.

--

--