AWS RDS 비용 효율적으로 낮춰 사용하기 (1) : RDS Proxy 도입

Tony Lee
DOMOVERSE 도모버스 블로그
7 min readJun 28, 2021

AWS에서 워크로드를 운영중이라면, 대부분 관계형 DB 서비스로 RDS를 사용할 것입니다. EC2에 DB를 설치해서 사용하는 경우 관리도 귀찮을 뿐더러, HA 구성에 많은 애로사항을 겪게 되는데요. RDS는 EC2 대비 가격은 비싸지만 다양한 DB 엔진을 지원하고 있고, 기초적인 DBA의 역할을 알아서 수행해주는 ‘관리형’ 서비스이기에 AWS에서 필수적으로 사용하는 서비스로 자리잡았습니다. 아마존(Amazon.com) 역시 수 년간의 마이그레이션 작업을 통해 오라클 DB를 모두 Aurora RDS로 전환하였다고 하고요.

AWS의 RDS Aurora MySQL max_connection limit

규모가 작은 워크로드, 특히나 소규모 스타트업이라면 개발 환경에서는 t 계열 인스턴스(t3.small, t3.medium 등)을 많이 사용하실 것으로 생각됩니다. 저희 역시 대부분의 인스턴스가 t 계열로 구성되어 있고요. 대부분 아시다시피 t 계열 인스턴스는 시간 당 사용 가능한 CPU 크레딧이 정해져 있어, CPU 로드가 많은 작업을 돌릴 경우 컴퓨팅 파워가 부족하거나 Extra 크레딧 과금이 되어 배보다 배꼽이 더 큰 경우가 생길 수 있습니다. 또한 RDS에서 t 계열 인스턴스를 사용한다면, max_connection 갯수가 100개도 되지 않습니다.

이러한 이유로 대부분의 서비스들이 라이브 환경에서는 r 계열 인스턴스로 DB 노드를 구성하고 있습니다. 컴퓨팅 리소스가 크게 필요하지 않은 경우에도, 혹시나 발생할 수도 있는 트래픽의 유입에 대비하기 위해 선제적(?)으로 대응하는 것인데요.

문제는 r 계열 인스턴스가 t 계열 대비 2배 이상 가격이 비싸다는 겁니다. 위에 첨부된 사진은 Aurora MySQL 사용 시 발생하는 인스턴스 비용인데, db.t3.medium과 db.r5.large간 비용 차이가 거의 3배에 가깝게 발생합니다. 시간당 요금으로 봐서 크게 와닿지 않으실 수 있으니, 월 비용으로 비교도 해보겠습니다.

db.t3.medium : 월 $91.25

db.r5.large : 월 $255.5

보통 이중화를 위해 DB 인스턴스를 두 개 이상 배포하고, 서비스의 성격에 따라 DB 클러스터도 여러 개로 분리하여 사용하는 점을 감안해보면 RDS 인스턴스 타입에 따른 AWS 비용 차이는 더욱 극명하게 발생할 것입니다.

발단

작년 11월 MVP 서비스를 진행하던 도중, RDS 비용이 AWS 사용료의 절반을 넘어가기 시작했습니다. 도모버스는 매일 특정 시간대에 사용자들에게 푸시 알림을 보내는데, 알림 직후 순간적으로 유입되는 트래픽으로 인해 ECS Task 개수가 급증하고, 그에 따라 DB Connection도 늘어나는 서비스 형태를 보였습니다. 그 과정에서 too many connection 에러를 많이 마주하기도 했고요. 다행히 DB 인스턴스의 리소스 사용량은 미미한 수준이었고, 마냥 DB 인스턴스 사이즈를 늘릴 수 없었기에 RDS Proxy를 빠르게 도입하기로 결정했습니다.

RDS Proxy 도입 전/후 아키텍처

RDS Proxy를 도입하여 얻는 가장 큰 장점은 프록시 노드에서 Connection Pool을 관리한다는 것입니다. 프록시 뒷 단의 RDS 노드들의 failover나 Scale-Up / Scale-Down이 매우 자유로워져, DB 클러스터 관리가 한층 편해집니다. 또한 Connection Pool을 관리, 재사용하기 때문에, RDS 인스턴스의 max_connection 갯수보다 더 많은 연결을 RDS Proxy를 통해 맺을 수 있습니다.

RDS Proxy 설정

RDS Proxy는 AWS RDS 콘솔 내 Proxies 메뉴를 통해 접근할 수 있습니다.

  1. RDS Proxy의 식별자를 적고, 프록시에 연결할 RDB의 엔진 종류를 선택합니다. MySQL 혹은 PostgreSQL 중 선택할 수 있습니다.

2. RDS Proxy와 연결될 RDS 인스턴스 or 클러스터를 지정합니다. 1번에서 Engine compatibility를 지정했기에, 해당 엔진을 사용중인 RDS만 리스트로 표시됩니다. 또한, EC2 등에 설치된 DB는 프록시 설정이 불가합니다.

3. 커넥티비티 설정의 경우, 저는 도큐먼트를 제대로 읽지 않아 한참 헤맸던 부분인데요.

  • Secrets Manager Secret: DB에 접속하기 위한 계정 정보를 Secrets Manager에 저장한 후 불러옵니다. 어플리케이션에서 기능 별로 DB 계정이 분리되어 있다면, 모든 DB 계정을 Secrets Manager를 통해 제공해야 합니다.
    지금 생각해보면 당연한 내용인데, 처음 설정할 때 sa에 근접한 권한의 계정을 RDS Proxy에 제공하고, 어플리케이션에서 다른 DB 계정으로 로그인이 불가하여 삽질하던 기억이 있습니다.
  • IAM Role : Secrets Manager에 접근하기 위한 권한을 설정해야 합니다. 보통은 자동 생성되는 IAM으로도 Secrets는 충분히 읽어옵니다.
  • IAM Authentication: RDS에 접속 시, IAM 인증을 사용하는 경우 활성화합니다. 이 기능 역시 저희는 사용하지 않아 비활성화 처리하였습니다.
  • Subnets: RDS Proxy가 배포될 서브넷을 지정합니다. DB가 배포된 서브넷과 통신이 가능한 서브넷을 지정하면 됩니다.
  • VPC Security Group: 이 부분이 Additional connectivity configuratioin으로 숨어있는 부분인데요. (일단 왜 숨어있는지 모르겠음) 어플리케이션 서버, RDS와 모두 통신이 가능한 Security Group을 할당해주시면 됩니다.
    아마도 버그일 것 같은데, 배포가 완료된 RDS Proxy의 Security Group을 수정하려고 해도 Modify가 잘 적용이 되지 않는 문제를 비교적 최근까지 겪고 있습니다.

대략 10~15분 가량을 기다리면 예쁜 RDS Proxy Cluster가 생성됩니다.

결론

RDS Proxy의 CloudWatch 모니터링 지표입니다. 트래픽이 많은 시간대에는 Client Connections이 최대 1000개까지 치솟기도 하고요. 프록시에서 Connection Pool을 생각보다 잘 관리해주는 덕분에, 이 당시에는 db.t3.medium 인스턴스로 DB 서비스를 운영했습니다.

하지만 프록시도 처리가 가능한 최대 커넥션 수가 있습니다. 임계점을 넘어가는 순간에는 역시나 ‘Too many connections’ 에러를 내뿜었고요. 프록시 도입 후 2주 가량이 지난 후 부터 Connection Pool 에러를 겪기 시작했고, 대략 커넥션 1,000 ~ 1,500 정도에서 에러가 발생했던 것으로 기억합니다.

결국 짠돌이 인프라 담당자는 db.t3.medium 인스턴스만으로는 서비스를 안정적으로 운영할 수 없다는 결론에 도달했습니다. 그 다음 해결책으로 Lambda + CloudWatch Events를 이용해 특정 시간대에 RDS 인스턴스 사이즈를 상향하는 cron을 설정하게 되었는데요. 관련된 내용은 2편에서 다루도록 하겠습니다.

--

--