RDS MySQL을 RDS Aurora로 이전하기
원티드는 서비스 초창기부터 2018년까지 데이터베이스를 AWS의 RDS MySQL을 사용하고 있었습니다.
2018년 싱가폴, 홍콩, 대만등의 서비스 지역 확대와 국내 사용자의 증가로 인한 트래픽 증가로 인하여 서비스 속도와 안정성에 문제가 생기기 시작했습니다. 서비스 안정성 문제의 가장 주된 원인은 트래픽 증가에 의한 데이터베이스의 부하였습니다.
간단하게는 RDS instance를 Scale up을하면 문제를 해결할 수 있었지만, Multi-AZ 설정이 되어 있다 하더라도 Scale up을 하면 일시적인 서비스 다운을 피할수 없고, 이러한 방법이 근본적인 해결책은 아니라고 생각됩니다.
그래서 고심 끝에, 다음과 같은 이유로 RDS Aurora를 서비스 환경에 도입하기로 결정을 하게 되었습니다.
RDS MySQL대비 뛰어난 성능
- (최대 50% API 응답 속도 개선)
유연한 확장성
- Auto scaling 지원
- Fail over를 이용한 Down time 없는 유연한 Scale up / down
멀티 마스터 지원
- 글로벌 서비스를 준비하면서 멀티 리전을 사용했을 때 필요하다고 판단.
- 예정 -> 2019.08 정식 출시
실제로 RDS MySQL에서 RDS Aurora로 이전하면서,
- 마이그레이션시 고려할 점
- 마이그레이션 방법 (과정)
- Aurora failover에 대한 Application에서의 대응방법
에 대해서 정리해봅니다.
마이그레이션시 고려할 점
Aurora는 MySQL 및 PostgreSQL 호환 관계형 데이터베이스이기 때문에 Application에서의 별도의 DB query의 수정없이 사용할 수 있습니다. DB query의 수정 없이 사용할 수 있는 점은 맞지만, 반드시 고려해야 하는 점이 한가지 있습니다. RDS MySQL과 RDS Aurora의 HA 구조가 다르고, Aurora에서 Failover 발생시 application 에서의 적절한 대응이 필요합니다.
Mysql의 경우 2대의 인스턴스가 Master-Slave 구조의 HA 구성이라면, Aurora의 경우는 Cluster 구조로 Writer-Reader 구조입니다. 이러한 Cluster 구조이기 때문에 DB endpoint도 인스턴스에 할당된 endpoint가 아닌 cluster에 할당되어 있는 cluster endpoint
와 reader endpoint
를 사용해야 합니다.
Aurora의 Cluster endpoint, Reader endpoint의 구성은 아래의 그림과 같습니다.
위의 그림을 보면, Cluster endpoint가 Master 인스턴스를 바라보고 있고, Reader endpoint가 다수의 read replica 인스턴스를 바라보고 있는 모습입니다. Aurora의 경우 Auto scaling을 제공하는데, 이것은 reader 인스턴스에 대해서 Auto scaling 설정이 가능합니다.
Aurora의 endpoint 주소는 AWS management console의 cluster 메뉴에서 확인이 가능합니다.
만약에 Cluster endpoint가 아닌 인스턴스에 할당되어 있는 endpoint를 Database의 주소로 사용한다면, failover 발생 시, 아래와 같은 에러를 접할 수 있습니다.
1290 The MySQL server is running with the –read-only option so it cannot execute this statement
Failover 발생시 application에서 가장 고려해야 하는 점은 cluster endpoint가 바라보고 있는 실제 인스턴스가 변경되기 때문에 DB Connection을 새로 맺어 주어야 합니다. Failover 이후에 계속 동일한 DB connection을 사용한다면, 해당 connection의 인스턴스는 reader로 failover 되었기 때문에 DB write 요청에서 에러가 발생하게 됩니다.
MySQL -> Aurora 마이그레이션 과정
MySQL에서 Aurora로 마이그레이션하는 방법은 여러가지가 안내되어 있습니다.
저희가 마이그레이션한 방법은 위의 내용과는 조금 다릅니다.
- Aurora replication 생성 (RDS MySQL에서 Aurora로 Replica 생성 기능을 제공합니다.)
- 서비스 중단
- Aurora replication promote
- 서비스 Database endpoint(Aurora cluster endpoint) 변경
- 서비스 동작 확인
- 서비스 재시작
위의 과정 중에 주의해야 할 점은, Aurora replication 생성 시,
- replication을 생성 할 때, Master DB에서 backup 작업이 발생하는데, 이 때 CPU 자원을 많이 사용하기 때문에 가능하다면 사용자가 적은 시간에 하는걸 추천합니다.
- Cross region replica가 설정되어 있는 경우 Aurora replica 생성이 불가능 하기 때문에 설정되어 있다면 삭제하고 진행해야 합니다.
Failover에 대한 대응
위에서도 잠깐 언급을 했지만, Aurora의 경우 cluster 구조이고 failover가 발생하면 writer 역할을 하던 인스턴스가 reader 인스턴스가 되기때문에 DB connection 관리를 제대로 하지 못하면, failover가 발생 했을 때 DB write 요청이 에러가 발생해서 서비스에 치명적인 영향을 줄 수 있습니다.
저희는 python의 sqlalchemy를 사용하고 있고 저희가 시도했던 방법들은 다음과 같습니다.
- SQLALCHEMY_POOL_RECYCLE 설정을 짧게 주어서 DB connection을 짧은 주기로 하게 하는 방법
- flask app context가 삭제될 때 특정 exception이 발생하는 경우, sqlalchemy pool connection을 다시 맺도록 하는 방법
- sqlalchemy session을 checkout 할 때, DB instance의 상태가
read only
인지 확인하고read only
인 경우DisconnectionError
exception을 raise 하여 sqlalchemy에서 다시 connection pool을 맺도록 하는 방법
결론적으로 저희가 적용한 방법은 3번째 방법입니다.
첫번째 방법의 경우, DB connection을 너무 자주 맺다보니, DB connection에 대한 오버헤드 때문에 성능적인 부분에서 손해를 보는 문제가 있었습니다.
두번째 방법의 경우, 문서상에 나와 있는 방법이 아니고 저희가 sqlalchemy code를 보고 시도했던 방법인데, 동작상의 문제는 없었지만 좋은 방법같지는 않아서 적용하지 않았습니다.
저희가 적용한 세번째 방법은 아래와 같습니다.
http://docs.sqlalchemy.org/en/rel_1_1/core/pooling.html#disconnect-handling-pessimistic 페이지의 내용을 참고했습니다.
마치며…
Aurora는 MySQL query에 대해서 호환이 되기 때문에 코드상의 별도의 수정없이 도입이 가능합니다. 하지만 Aurora DB를 도입하기 전에 반드시 다음과 같은 부분에 대해서 준비가 필요하다고 생각됩니다.
- Aurora cluster 구조의 이해
- Aurora failover에 대한 application 상에서의 대응방법
Aurora를 실제로 서비스에 적용하고 다음과 같은 부분에서 만족하고 사용하고 있습니다.
- 성능적인 부분에서의 개선
- 유연한 scale up 또는 scale down
- 추가로 제공될 다양한 기능(?)
다음번에는 Aurora Auto scaling을 적용한 내용에 대해서 써보겠습니다.