
이번 포스트에선 최근에 진행한 ECS 서비스의 오토스케일링 검증을 위한 부하 테스트 중에 발생한 DB 커넥션 풀(Connection Pool) 고갈 문제와 해결 과정을 작성해보고자 합니다.
문제는 k6를 이용하여 퍼블릭 클라우드 상의 ECS 서비스에 부하를 주는 과정에서 발생했습니다.
제 목표는 트래픽이 급증할 때 CPU 사용량이 늘어나고, 이에 따라 ECS Task가 자동으로 확장(Scale-out)되는지 확인하는 것이었습니다. 이를 위해 가상 유저(VU) 100명이 동시에 접속하는 시나리오를 실행했습니다.
그런데... 오토스케일링이 동작하기도 전에 에러 로그가 쏟아지며 테스트가 실패했습니다.

애플리케이션 로그를 확인해보니 위와 같은 TimeoutError가 발생하고 있었고, k6 결과에서도 대다수의 요청이 500 Internal Server Error로 실패 처리되고 있었습니다. . .
CPU가 올라가서 스케일링이 되어야 하는데, DB 연결 단계에서 먼저 병목이 발생하여 애플리케이션이 뻗어버린 상황이었습니다.
에러 로그(QueuePool limit of size 5 overflow 10 reached)를 분석해보니 원인은 SQLAlchemy의 Connection Pool 설정값이 트래픽 대비 너무 작았기 때문이었습니다.
FastAPI에서 SQLAlchemy를 사용할 때 기본적으로 QueuePool을 사용하는데, 당시 설정은 다음과 같았습니다.
즉, 하나의 컨테이너(Task)가 동시에 처리할 수 있는 DB 연결은 최대 15개 (5 + 10)였습니다.
하지만 부하 테스트에서는 100명의 유저를 기준으로 동시 요청을 보내고 있었으니, 15개의 풀 외에 나머지 85개의 요청은 큐에 쌓이다가 결국 TimeoutError 으로 연결이 끊어진 것입니다.
SQLAlchemy Connection Pooling Docs
문제를 해결하기 위해 컨테이너가 더 많은 DB 연결을 수용할 수 있도록 커넥션 풀 설정을 상향 조정했습니다.
Terraform 코드(modules/ecs/main.tf)에서 ECS Task Definition의 환경변수를 수정하여 DB_POOL_SIZE와 DB_MAX_OVERFLOW 값을 늘려주었습니다.

총 50개까지 동시 연결이 가능하도록 여유 있게 설정을 변경하고 배포를 진행했습니다.
이후, 더 이상 QueuePool limit 에러는 발생하지 않는 것을 확인하였습니다.
단순히 컴퓨팅 리소스 지표에만 신경쓰는것이 아니라, 애플리케이션이 부하를 정상적으로 처리할 수 있는 전제 조건(DB 커넥션 풀, 병목, 외부 의존성 등) 이 먼저 충족되어야 한다는 점을 느끼고 갑니다. . .