How to: RDS MySQL 에서 Aurora MySQL로 안전하게 이전하기

데이터는 정말로 중요하다. 날아간 서버는 금방 다시 띄우면 그만이지만 망가진 데이터 베이스는 두고두고 모두를 괴롭힌다. 데이터 베이스가 불안정 하면 아무리 서버 사양이 좋아도 서비스는 불안정 해진다. HBSmith를 시작하고 나서 다양한 회사의 다양한 장애 상황을 들여다 볼 기회가 있었다. 어플리케이션 서버의 장애는 코드 수정으로 비교적 짧은 시간내에 고쳐볼수 있었다. 하지만 데이터 베이스의 불안정성은 바로 해결되기 어려우며 오랜시간 구성원들을 괴롭히고 있었다.

RDBMS가 클라우드 시대에도 불안정한 근본 이유는 Scale in/out이 힘든 구조이기 때문이다. RDBMS의 핵심인 ACID 원칙은 OS에서 지원하는 Semaphore, Lock등의 개념을 통해 단일 머신 내에서 비교적 쉽게(?) 실현 가능했다. 하지만 네트워크로 연결된 수많은 node로 구성된 HA(High Availability)환경 내에서는 구현이 복잡해진다. A 머신에서 걸어놓은 Transaction과 Lock을 B 머신에서도 똑같이 복제할수 있는가? 그리고 여러 머신과 네트워크 장애에도 불구하고 안정적으로 유지하며 Race condition 또는 Dead lock 이슈를 해결할수 있는가? (이런 문제들을 Split Brain 문제라고 한다.) 같은 온갖 복잡한 문제들이 존재한다.

그래서 NoSQL 같은 기술들은 ACID의 일부를 포기함으로서 이러한 문제들을 해결할수 있다고 이야기 해왔다. 그러나 ACID가 가져다 주는 이점을 포기했기 때문에 동시에 RDBMS를 완전히 대체하는 것은 불가능해졌다. (용도 자체가 달라져 버렸기 때문에)

클라우드 기술이 대중화 되면서 이런 RDBMS의 근본적인 한계가 이상적인 HA를 달성하는데 방해로 지적되어왔다. 오랜 시간동안 컴퓨터 업계에선 CPU의 속도는 무어의 법칙처럼 빨라졌는데 스토리지가 거북이 걸음같은 속도에 머물기 때문에 컴퓨터가 제 성능을 못낸다고 이야기 되어왔다. 마찬가지로 모든 컴퓨팅 자원이 가상화되고 HA화 되었는데 RDBMS는 완벽하게 HA 되지 못했기 때문에 계속 SPOF(Single Point of Failure)가 Architecture 속에 아킬레스 건처럼 존재해 왔다.

AWS는 이 문제를 클라우드적인 해법으로 풀어보고자 노력했고 그 결과로 AWS Aurora를 출시하였다. Aurora는 완전히 새로운 DBMS가 아니라, 기존 RDBMS의 하부 구조를 AWS에 맞게 개조하여 ACID는 유지하면서 자동화된 장애복구와 HA가 가능하도록 만든 RDBMS이다. MySQL과 완전히 호환되며 AWS에서 RDS 기반의 Workload를 돌리는 분이라면 Aurora로의 전환을 고려해 보시는걸 추천드린다.


우리 고객사중 하나인 Sixshop은 수년이상 AWS 상에서 서비스를 운용해 왔고 RDS MySQL을 이용하여 전자상거래를 제공해 왔다.

Sixshop은 WIX 처럼 누구나 쉽게 6분만에 예쁜 쇼핑몰을 만들수 있는 서비스를 제공한다. 관심있는 분은 https://sixshop.com 에 방문해 보시길 추천드립니다.

하지만 고객이 점차 늘어나면서 기존 RDS MySQL을 이용하여 안정적인 서비스를 제공하는것이 점점 어려워졌고 외부의 공격과 해킹에 대비해 DBMS의 보안을 좀더 강화할 필요가 생겼다. 따라서 AWS Aurora로의 Migration이 필요하게 되었고 이를 위해 몇가지 전제조건을 제시했다.

  1. 최대한 Downtime이 적었으면 한다.
  2. DBMS가 네트워크 레벨 부터 외부와 완전히 격리 되었으면 한다.
  3. 데이터는 완벽하게 이전되어야 한다.

이 전제조건을 만족하기 위해 조사한 몇가지 방법중 우리는 최종적으로 Aurora Read Replica를 사용하는 방법을 택했다. 선정 이유는 다음과 같다.

  1. 원본 DB를 Snapshot을 뜨고 이를 바닥에서 Aurora로 다시 띄우는 것보다 짧은 시간에 이전이 가능하다. = Downtime의 최소화

나머지 조건들은 전통적인 Snapshot 복제 방법으로도 달성할수 있었다.

이전 계획은 어떻게 세우면 되나?

AWS 문서에는 당연하겠지만 이 마이그레이션 과정에서 고려해야할 문제나 계획에 대해선 자세히 언급되어 있지 않다. 따라서 운영 DB를 처음 마이그레이션 하는 사람은 여러가지 실수를 범할 가능성이 크게 존재한다. 이 실수 때문에 복구가 불가능한 상황에 처할수도 있다. 이런 문제를 피하려면 모든 과정에 대해서 분명한 계획을 세워야 하고 계획을 테스트 베드에서 여러차례 실험해 보아야 한다. 이 계획이 없는 상태에서 migration 시도를 하려다가 실패하거나 장애를 겪은 고객들을 몇번 목격했기 때문에 강조하고 싶다.

우선 AS-IS 분석이 필요하다.

  1. 현재 운영중인 DBMS(이하 Source DB)의 사양과 설정을 정확히 알아야 한다. 다음과 같은 내용들이 문서에 정리되어야 한다.
  • Source DB의 Engine과 Version (예: mysql 5.6.5a, Inno DB x.x)
  • Source DB의 Network 설정 (어떤 VPC에 있으며 어떤 SG 설정이 되어 있는지)
  • Source DB에 접근하는 요소 (WAS 뿐만 아니라 cron 같은 요소들 모두)
  • Source DB의 데이터 크기 (Snapshot을 뜨는데 걸리는 시간을 결정함)
  • Source DB의 Parameter 설정 (매우 중요한 요소로서 DB의 시간대와 인코딩을 결정한다. 이 정보가 제대로 이전이 안되면 데이터 자체가 틀어져버린다)

DB Parameter의 경우 RDS MySQL에선 한벌이었던 Parameter가 Aurora에서는 Cluster용과 Instance용의 2벌로 나뉘어져 있다. 만약 Source DB의 Parameter를 튜닝하였다면 Aurora의 Cluster와 Instance Parameter중에 어떤걸 튜닝해야 Source DB와 똑같이 맞출수 있는지 확인해야 한다. 비교하는 방법은 RDS Web Console에서 비교하고 싶은 Parameter를 선택하고 Compare 를 클릭하면 된다.

비교하고 싶은 Parameter를 선택하고 우측 상단의 Compare를 클릭한다.
  • Source DB내의 Table, Schema 정보 (어떤 Table이 있고 각 Table이 몇개의 Row를 지니고 있는지 확인할 필요가 있다)

2. 현재 운영중인 서비스의 특성을 알아야 한다. 다음과 같은 내용들이 역시 문서에 정리되어 있어야 한다.

  • 서비스가 가장 한가한 시간이 언제인가 (Sixshop의 경우 새벽 3~4시 대가 가장 트래픽이 적은 시간이었다. 이시간대에 migration을 진행해야 Downtime 발생에 대한 고객 불만을 최소화 할수있다.)
  • 서비스 점검/장애 공지를 언제 어떻게 누구를 통해 내보낼수 있는가? (고객에게 미리 공지가 나가야 하며, 문제가 터졌을때 복구가 될 때까지 운영팀에서 방어를 해줘야 한다.)
  • 장애 발생시 보통 어떻게 고객에게 보상하는가? (예기치 못한 문제로 인해 이전/복구 과정이 길어지거나 장애로 인한 고객 손실을 어떻게 보상해서 불만을 줄일 것인가를 고민해야 한다.)
  • 서비스의 현재 Architecture는 어떻게 되어 있는가? (특히 시스템의 네트워크 구성과 WAS와의 연결이 어떻게 설정되어 있는지 자세히 확인해야 한다.)

충분히 현 상황에 대한 파악이 되어 있다면 To-Be 계획을 세울 차례다.

  1. Source DB와 동급 성능의 Tier와 Engine이 AWS Aurora에 있는지 확인한다. 
    (너무 오래된 Engine 버전을 Source DB에서 쓸 경우 Aurora에서 지원하지 못할 가능성이 있다. 이럴경우엔 DB Data를 직접 Dump 떠서 변환 후 이전하는 방법밖에 없다.)
  2. 기존 Architecture Diagram이 있다면 migration 후의 최종 Architecture Diagram을 그려본다.
  3. 기존 Architecture에서 최종 Architecture로의 모든 이전 Step을 Diagram 및 표로 묘사해 둔다. Sixshop의 경우는 다음과 같다. (여기선 일부만 모사했다) 여기서 중요한건, migration이 실패해도 잽싸게 옛날 상태로 rollback할수 있게 계획을 세워야 한다는 것이다.

To-Be 계획에서 3번째인 각 작업 Step이 어떻게 이루어지는지는 아래 예시를 참고 바란다. 실제로 진행한 과정이므로 참고가 될 것이다.

Migration 과정은 어떻게 이루어지나?

STEP 0: AS-IS (Migration 시작 전 상태)
 
이번 작업은 default VPC에 설정된 RDS MySQL 서버를 RDS 전용 VPC에 Aurora MySQL 서버로 migration 하는 작업이다. 따라서 미리 RDS 전용의 VPC를 생성하고 WAS가 위치한 VPC와 VPC Peering Connection을 연결해 두었다. (기존 WAS 서버와 VPC를 다르게 둔 이유는 Security 때문이며 이 VPC는 WAS가 위치한 VPC에서 MySQL용 TCP 3306 포트로 접근하는 것 외엔 모든 접근이 차단된 격리된 VPC 이다.)

STEP 1: Aurora Read Replica 생성, 실시간 복제 시작
 
Aurora Read Replica를 이용한 migration 방법은 기존 MySQL master의 slave로 Aurora Read Replica Instance를 띄워 master의 내용이 slave에 replication이 완료되면 이 slave를 master로 승격(promote)시키고 여기로 WAS의 연결을 옮기는 방법이다. 따라서 Source DB의 slave로 띄울 Aurora Read Replica instance를 마련해야 한다. 방법은 RDS Web Console 상에서 “Create Aurora Read Replica”를 선택하고

https://aws.amazon.com/blogs/aws/new-create-an-amazon-aurora-read-replica-from-a-mysql-db-instance/

어떤 instance를 띄울 것인지 To-Be 계획을 참고하여 설정한다. sixshop의 경우엔 DB Instance Identifier를 기존 Source DB와 다르게 설정하고, Network는 앞에서 설명한 전용 VPC와 subnet으로 설정한 후 나머지는 Source DB와 동일하게 맞춰두었다.

https://aws.amazon.com/blogs/aws/new-create-an-amazon-aurora-read-replica-from-a-mysql-db-instance/

이렇게 Replica를 생성하면 RDS에서 자동으로 다음 작업을 수행한다.

1) Source DB의 가장 최신 Snapshot을 생성한다.
2) 1)에서 생성한 Snapshop을 이용하여 Aurora Read Replica를 생성한다. (Cluster와 Instance 모두)
3) Source DB에서 Read Replica로 Replication을 수행한다 = Source DB와 Replica 사이의 데이터 차이를 확인하여 Replica로 복제를 수행한다.

클러스터 설정에서 preparing-data-migration 모드로 들어가면 작업이 시작된 것이다.

STEP 2: Replica Lag로 복제가 완료되었는지 확인
 
Replication이 완전히 진행되었는지 여부는 Replication Lag으로 확인할수 있다. 보통 MySQL에 root 계정으로 직접 접속하여 Query로 확인해볼수 있지만 RDS에서 제공하는 기본 root 계정은 이 Query를 수행할수 있는 권한이 막혀있기 때문에 CloudWatch를 통해서 확인해야만 한다. Aurora Read Replica의 CloudWatch Dashboard에서 Aurora Binload Replica Lag 값의 변화를 확인한다.

https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Aurora.Monitoring.html

우측 하단의 그래프를 참고하라

이 값이 0에 도달한지 좀 됐다면 Source DB의 내용이 Aurora Read Replica와 거의 일치한다고 보면 된다.

STEP 3: Replica Lag이 0에 거의 도달면 기존 WAS의 트래픽을 차단
 
기존 WAS는 여전히 Source DB에 연결되어 있기 때문에 새로운 내용이 계속 Source DB에 기록되고 slave인 Aurora Read Replica로 지속적으로 복제된다. 이런 상황에선 Source DB와 Read Replica간의 복제가 굉장히 빠르게 이루어진다고 하더라도 미세한 데이터 차이가 계속 발생하게 된다. (Source DB의 Table과 복제된 Table의 Row Count를 해보면, Row 수가 계속 미세하게 차이가 난다)이 상황에서 WAS의 연결을 Read Replica로 옮겨버리면 옮기는 과정에서 걸린 시간 사이에 발생한 데이터 차이를 직접 찾아서 Read Replica에 수동으로 넣어줘야 한다. 그렇지 않으면 migration 후에 데이터가 누락되거나 정합성이 깨지는 일이 벌어진다.

이런일을 방지하려면 WAS나 다른 요소가 Source DB에 Write를 하는 작업(INSERT, UPDATE, ALTER, DELETE 등)을 모두 멈추도록 해야한다. 따라서 sixshop의 경우 공사중 페이지를 띄우고 WAS를 모두 중지 시켜야 했다. 그리고 Source DB를 Reboot 시켜 모든 Connection이 끊어지도록 하여 Write 작업이 중단되도록 만들었다. (프로시저를 돌리고 있다면 미리 모두 중단시켜야 할 것이다.) 이후 양쪽 DB의 모든 Table (DBMS에서 관리하는 Table은 제외)에 대해서 Row Count를 확인하여 양쪽 DB의 Row Count가 일치하는지 확인하였다. 유저 Table에 대한 Row Count를 뽑는 SQL을 만들어주는 쿼리는 다음을 이용 하였다.

SELECT CONCAT( 
    'SELECT "',
    table_name,
    '" AS table_name, COUNT(*) AS exact_row_count FROM `',
    table_schema,
    '`.`',
    table_name,
    '` UNION '
)
FROM INFORMATION_SCHEMA.TABLES
WHERE table_schema = '<my table schema>';

Source DB에 연결된 Connection이 0이고 Write 작업이 일어나지 않으며, 양쪽 DB에서 각각 수행한 Row Count가 동일하다면 이제 다음 Step으로 넘어갈 준비가 된 것이다.

특정 설정만 on 하면 시스템 전체가 Source DB에 대해서 Write 작업을 멈추고 Read만 하는, Read-Only 모드가 가능하도록 설계가 되어 있다면 WAS를 굳이 중단시키고 공사중 페이지를 띄우는 일은 없었을 것이다. 하지만 대부분의 고객 시스템들이 이런 고려가 되어있지 않기 때문에 얼마간의 Downtime이 발생할 수밖에 없다. 물론 세상엔 더 좋은 방법이 있어 “완벽한 무중단 이전"이 가능할수도 있다. 하지만 이 상황과 당신의 비슷하다면, 얼마간의 “중단”은 각오해야 하며 개발하시는 서비스의 운영팀이 이를 인지하고 정책적으로 대비할수 있도록 미리 알려줘서 준비해야 한다.

주의사항
이 과정에서 Aurora Read Replica는 놀랍게도 Read-Only 상태가 아니기 때문에 Write 쿼리를 실행할수 있다! 만일 실수로라도 Read Replica에서 Write 쿼리를 수행해 버리면 양쪽 DB의 복제 정합성을 보장하는 Binary Log가 틀어져버려 Read Replica를 완전히 부수고 Step 1부터 모든 작업을 다시 시작해야만 한다! 
STEP 4로 넘어가기 전에 실수로라도 Write 쿼리를 수행하지 않도록 주의하자!

STEP 4: Aurora를 Master로 승격 (Promote)

https://aws.amazon.com/blogs/aws/new-create-an-amazon-aurora-read-replica-from-a-mysql-db-instance/

복제가 완료되었기 때문에 이제 Aurora와 Source DB의 연결을 끊을 때가 왔다. Promote Read Replica를 클릭하면 수초 이내로 Aurora 클러스터를 replica 모드에서 master 모드로 바꾸어준다.

Cluster 설정이 이렇게 바뀌게 되면 Promotion이 완료된 것이다.

STEP 5: Aurora Connection 변경 후 트래픽 다시 연결
이제 마지막 단계이다. WAS의 설정에서 DB Endpoint 부분을 Aurora의 Cluster Endpoint로 바꾸고 WAS를 재시작 한다. 그리고 서비스의 모든 기능들이 정상 동작하는지 확인한다. 문제가 없다면 이제 공사중 페이지로 차단했던 사용자 트래픽을 다시 WAS로 연결하고 아무일이 없던것 처럼 서비스를 시작하면 된다. 이때 주의할 점은 이전후에 Source DB를 바로 부수지 말라는 것이다. 왜냐면 DB 이전때문에 문제가 발생했을때 참고할 원본 자료가 있는것이 안전하기 때문이다. 일주일 정도 운영한 후에 문제가 없다면 Source DB는 최종 스냅샷만 만들고 부숴서 비용을 아끼도록 하자.

이제 테스트와 실행만이 남았다.

실제 migration 을 진행하기 전에, To-Be 계획을 세웠던 그때로 돌아가 운영환경이 아닌 테스트 환경에서 위의 모든 과정을 실험해보길 추천한다. AS-IS와 최대한 똑같은 환경을 구축하고 To-Be로 이전하는 테스트를 최대 3번 정도 진행하면서 각 과정을 기록해 본다. 과정에서 어떤 일들이 발생하고, 얼마만큼의 시간이 걸리는지 미리 확인해보기 위해서이다.

경험적으론, 과정중에 가장 시간이 많이 걸리는 과정은 STEP 1에서 Source DB의 스냅샷을 뜨고 여기서 Read Replica를 띄운후 복제를 시작하는데 까지 걸리는 시간이었다. DB에 저장된 데이터의 크기가 클수록, 띄우려는 Instance의 사이즈가 클수록 오래 걸린다.

여기까지 진행했다면, 운영팀 및 경영진과 협의하여 언제 얼마만큼의 시간을 가지고 이전할 것인지 정할수가 있게된다. 이제부턴 migration에 참여할 인력들과 그 시간에 한자리에 모여 고객에게 공지를 보낸 후에 한 Step씩 진행하면 된다.


HBSmithAWS Technology Partner로서 AWS에 대한 전문성을 바탕으로 고객님의 IT 서비스가 24/365 잘 동작하는지 모니터링해 주는 “서버 관제 전문 서비스" HBSmith를 제공하고 있습니다. 무료로 가입하시고 AWS 관련 소식도 받아보세요!