Tech Blog 목록으로 Insights

ETA 오토스케일링 적용기 (1) — 워커를 언제 늘려야 할까?

ETA는 테스트 시나리오를 정해진 시간에 자동 실행할 수 있는 스케줄 기능을 제공한다.

평소에는 별 문제가 없는 기능이지만 분기별 테스트처럼 특정 시간에 작업이 한꺼번에 몰리는 경우가 있다. 이때 워커가 부족하면 실행을 기다리는 시나리오가 계속 쌓인다.

그렇다고 이벤트가 많이 쌓일 때마다 사람이 직접 Pod 수를 늘리고, 끝나면 다시 줄이는 게 최선일까?

자동화 솔루션을 운영하면서 정작 워커 증설은 수동으로 해야 한다는 것은 용납할 수 없다.

일단 HPA를 쓰면 되는 거 아닌가?

Kubernetes에는 HPA(Horizontal Pod Autoscaler)가 있다. CPU나 메모리 사용률 등을 기준으로 Pod 수를 자동 조절하는 기능이다.

처음에는 ETA 워커에도 HPA를 적용하면 되겠다고 생각했다. 하지만 워커의 실행 구조를 생각해 보니 문제가 있었다.

ETA의 워커 Pod 하나는 한 번에 시나리오 하나만 실행한다. 그리고 Pod 안에서는 브라우저, 워커 프로세스, Playwright MCP 서버가 함께 동작한다. 하나의 시나리오를 실행하는 데에도 꽤 많은 리소스가 필요하다.

즉, 워커가 시나리오를 실행 중이라면 워커 pod에 할당받은 CPU와 메모리를 전부 사용한다.
고로, 리소스 사용량을 기준으로 파드 수를 늘리게 되면 시나리오가 고작 하나 실행중인 경우에도 오토스케일링이 될 수 있는 것이다.(극단적인 예시..)

결국 리소스 사용률보다 중요한 건 실행을 기다리는 시나리오가 얼마나 있는지였다.

그러면 대기열을 보면 되잖아?

현재 ETA의 스케줄 실행 흐름은 비교적 단순하다.

[WAS]
  사용자가 Cron 스케줄 등록
            ↓
  실행 시점에 Redis Queue에 Job 등록
            ↓
[Redis Queue]
  실행 대기 중인 Job
            ↓
[Worker Pod]
  Job을 가져와 시나리오 실행

여기서 문득 생각이 들었다.

이미 Redis에 실행 대기 중인 작업이 쌓이고 있는데, 이 대기열 길이를 기준으로 워커를 늘리면 되는 것 아닐까?

예를 들어 워커가 모두 바쁘고 대기 중인 Job이 10개라면, 그만큼 워커를 추가로 띄워서 처리하도록 만드는 것이다. 물론 서버 자원이 무한한 것은 아니니 최대 Pod 수는 제한해야 한다.

문제는 일반적인 CPU·메모리 지표 대신 Redis Queue의 대기열을 어떻게 스케일링 기준으로 삼느냐였다.

역시 이런 고민을 나만 한 건 아니었다

조금 찾아보니 KEDA(Kubernetes Event-driven Autoscaling)라는 스킬이 있었다.

KEDA는 CPU 사용률 같은 리소스 지표뿐 아니라 Redis, Kafka, RabbitMQ 등 외부 시스템의 이벤트나 지표를 기준으로 Kubernetes 워크로드를 확장할 수 있게 해준다.

딱 지금 필요한 방식이었다.

ETA에서는 Redis Queue에 쌓인 대기 Job 수를 기준으로 워커 Pod 수를 조절하는 방향을 검토해 보기로 했다.

다만 Redis를 쓴다고 설정 하나로 무조건 끝나는 건 아니다. 실제 Queue 구현 방식에 따라 KEDA에서 지원하는 Scaler와 대기열을 측정하는 방법이 달라질 수 있으니 먼저 확인해야 한다.

그런데 늘리는 것보다 줄이는 게 더 걱정이다

Pod를 늘리는 건 비교적 명확하다. 대기 중인 Job이 많아지면 워커를 추가하면 된다.

하지만 작업이 끝나고 대기열이 0이 됐다고 해서 모든 워커를 바로 줄여도 될까?

그건 아니다. Queue는 비어 있어도 이미 Job을 가져간 워커에서는 시나리오가 실행 중일 수 있다. 이때 Pod가 종료되면 브라우저와 MCP 서버도 함께 내려가고, 실행 중이던 테스트가 중단될 수 있다.

따라서 단순히 Queue 길이만 보는 것에서 끝내지 않고, 실행 중인 Job을 보호할 수 있는 종료 처리와 스케일인 정책도 함께 설계해야 한다.

이번에 해보려는 것

우선 목표는 명확하다.

대기 중인 시나리오가 늘어나면 워커 Pod가 자동으로 증가하고, 작업이 줄어들면 실행 중인 시나리오를 안전하게 마친 뒤 Pod가 감소하도록 만드는 것.

이를 위해 Redis Queue의 실제 구조를 확인하고, KEDA ScaledObject 설정과 최대 Pod 수, 스케일링 기준, 종료 정책을 차례대로 검증해 보려고 한다.

다음 편에서는 KEDA를 설치하고 ETA의 Redis Queue와 연결해 볼 예정이다.

P.S.
처음 Kubernetes로 전환했을 때가 생각난다.
당시에는 내가 이걸 직접 구축한다는 것 자체가 너무 재미있어서 금요일부터 일요일까지 주말 내내 거의 밤새 작업했던 기억이 있다.
처음 해보는 작업이다 보니 '이게 진짜 되나?' 싶기도 했고, 반신반의하면서 하나씩 붙여 나갔는데 어느 순간 실제로 서비스가 돌아가는 걸 보니 꽤 신기했다.
그때 구성했던 CI/CD 파이프라인과 GitHub Actions 기반 배포 워크플로우가 지금까지 큰 오류 없이 잘 돌아가고 있다는 것도 여전히 신기하다.
사실 대단한 인프라를 구축한 것도 아니고, 누군가에게는 별것 아닌 작업일 수도 있겠지만, 직접 고민하고 하나씩 구축해서 그런지 유독 인프라 쪽에 애정이 가는 것 같다.