AWS CloudFormationをフル活用してAmazon RDS for MySQLからAmazon Auroraへ移行する

f:id:vasilyjp:20180809145750j:plain こんにちは。新事業創造部インフラチームの内山(@k4ri474)です。

弊社が運営するIQONというサービスでは、長らくMySQLのバージョン5.6.27を利用していました。これは2018年9月にEOLを迎えるため、RDSの方針として強制アップグレードがアナウンスされています。
MySQLを継続する選択肢もありましたが、Auroraの運用知見が溜まっていたということもあり、これをキッカケにMySQLからのAurora移行を実施しました。

新事業創造部ではほぼ全てのAWSリソースをCloudFormationを使って宣言しているため、例に漏れずAuroraもテンプレートへ落とし込むことにしました。
ただ、CloudFormationだけで完結しない作業があったので、今回はCloudFormationでの宣言とコンソールからの手作業を織り交ぜるという対応を取っています。

執筆時点の公式ドキュメントではカバーできていない需要を満たすべく、CloudFormationを使ったAmazon Auroraへの移行の手順を皆さんに共有したいと思います。

利用したサービス

AWS CloudFormation

AWS CloudFormationはAWSのほぼ全てのリソースをテキストベースで管理し構築できるサービスです。1

構築にはYAMLもしくはJSON形式で記述されたテンプレートというテキストファイルを利用します。設計のタイミングで、どのリソースをどういったプロパティで作成するかをコントロールできます。
CloudFormationのテンプレートを使い回すことで、数クリックでサービス環境一式を複製できます。

また、ネットワークの詳細をテキストで表現するため、設計のノウハウをチーム内で共有できたり、他チームに提供する資料としてそのまま使えるというメリットもありますね。

Amazon Aurora

Amazon AuroraはAWSのAmazon RDSというサービスで利用できる、MySQL・PostgreSQLと互換性のあるリレーショナルデータベースです。

特徴を何点か抜粋してみました。

  • パッチの自動適用
  • 最大16TBまでオートスケールされるストレージ
  • リードレプリカによる読み取りスループットの容易なスケール
  • 読み込みエンドポイントによるリードレプリカの抽象化

僕のおすすめポイントは、読み込みエンドポイントがRDS版のロードバランサ感覚で使え、裏側のインスタンスの台数をクライアント側で管理する必要がなくなったことです。2

Auroraへの移行

Auroraへの移行は、大雑把に言うとMySQLをAuroraで複製し、アプリケーションの接続DBをAuroraへ変更することで実現できます。

複製を作る過程でAuroraクラスタの呼称が変わるのでここで補足しておきます。
MySQLから非同期的にデータをコピー(レプリケーション)してきている段階のAuroraクラスタをAuroraリードレプリカといい、レプリケーションを止めて独立した状態のAuroraクラスタをスタンドアロンAuroraクラスタと呼びます。

今回はスタンドアロンAuroraクラスタをどうやってCloudFormationで作成するかにフォーカスして説明を行います。

AWS公式ドキュメントで示されている手法で移行を行うと、以下のような手順になります。 f:id:vasilyjp:20180809144455p:plain 可能な限りCloudFormationで宣言したい部署の方針に沿って、この手順をテンプレートで最大限表現しようと思います。
CloudFormationでは次の作業ができることを期待し、検証を開始しました。 f:id:vasilyjp:20180809144503p:plain Auroraリードレプリカの作成とスタンドアロンAuroraクラスタへの昇格がCloudFormationのテンプレートで表現できれば、手作業を介さずに移行できます。

ただ、現実はこうでした。 f:id:vasilyjp:20180809144521p:plain 結論として、リードレプリカの昇格はCloudFormationで実装できませんでした。
CloudFormationでは、Auroraクラスタ作成においてAuroraリードレプリカの作成クラスタのスナップショットからクラスタを復元することしかできません。

そのため、Auroraリードレプリカを昇格する作業は手作業となってしまいます。
ただ、普通に手作業で実施するとテンプレートの記述と実際のリソースに相違が発生してしまいます。 f:id:vasilyjp:20180809144424p:plain これを解消するため、以下の手順で移行を実施しました。

  1. Auroraリードレプリカの作成をテンプレートで宣言
  2. コンソールにてAuroraリードレプリカを昇格
  3. コンソールにてスタンドアロンAuroraクラスタのスナップショットを取得
  4. 手順1のテンプレートを再利用して手順3のスナップショットから新しいスタンドアロンAuroraクラスタを作成

この手順を踏むと、テンプレートの記述と実際のリソースが一致します。
実際に移行を行うにあたって、まずはスケジュールを説明します。

スケジュール

今回の作業ではメンテナンス前に実施する作業と、メンテナンス中に実施する作業が存在します。

  • メンテナンス前:Auroraリードレプリカの作成(手順1)
  • メンテナンス中:Auroraリードレプリカの昇格以降、全ての手順(手順2・3・4)

Auroraリードレプリカの昇格を実施するとレプリケーションが切れ、MySQL Masterとの差分が発生します。そのため、トランザクションが無くなってから手順2以降を実施する必要があります。

それでは、本題の各手順の内容を説明していきます。

1. Auroraリードレプリカの作成

f:id:vasilyjp:20180809144654p:plain RDS for MySQLからの移行に当たって、まずはMySQLからレプリケーションを受けるAuroraリードレプリカを作成する必要があります。
この作業はCloudFormationで宣言することが可能で、テンプレートで表すと以下のようになります。

# Auroraクラスタを作成
MyRDSDBCluster:
  Type: "AWS::RDS::DBCluster"
  Properties:
    # (各種必須プロパティを省略)
    Engine: 'aurora'
    EngineVersion: '5.6.10a'
    ReplicationSourceIdentifier: 'arn:aws:rds:<AZ>:<AccountId>:db:<SourceInstanceName>'
    
# Auroraインスタンス1台目を作成
MyRDSDBInstanceApplicationFirst:
  Type: "AWS::RDS::DBInstance"
  Properties:
    # (各種必須プロパティを省略)
    DBClusterIdentifier: !Ref MyRDSDBCluster
    Engine: 'aurora'
    
# Auroraインスタンス2台目を作成
MyRDSDBInstanceApplicationSecond:
  Type: "AWS::RDS::DBInstance"
  Properties:
    # (各種必須プロパティを省略)
    DBClusterIdentifier: !Ref MyRDSDBCluster
    Engine: 'aurora'

ポイントは以下の2点です。

  1. クラスタ側でReplicationSourceIdentifierというプロパティを記述し、レプリケーション元となるMySQLインスタンスのARNを指定する
  2. インスタンス側ではDBClusterIdentifierというプロパティを記述し、所属するクラスタを指定する

これでMySQL Masterからレプリケーションを受けているAuroraクラスタができます。

2. Auroraリードレプリカの昇格

f:id:vasilyjp:20180809144644p:plain 次に実施するのはAuroraリードレプリカをスタンドアロンのAuroraクラスタに昇格させる作業です。

この手順をなんとかCloudFormationで実装しようとしてテンプレートをぽちぽち弄ってみたのですが、実現出来ませんでした。3
よって昇格はAurora リードレプリカの昇格(AWS マネジメントコンソール)に記載された手順で実施しました。

先述の通り、この時点でテンプレートではAuroraリードレプリカ、実際はスタンドアロンAuroraクラスタが構築されており、差異があります。
これを解消するため、3・4の手順を踏んでテンプレートを実態に即したものへ修正します。

3. スナップショットの取得

f:id:vasilyjp:20180809144634p:plain 最終的なスナップショットからの復元の前準備として必要なのがこの手順3です。
この作業もテンプレートでは表現できないため、DB スナップショットの作成(AWS マネジメントコンソール)に記述された手順で実施しました。

4. スナップショットからのクラスタ復元

f:id:vasilyjp:20180809144623p:plain 最後に、テンプレートを使って手順3で取得したスナップショットからAuroraクラスタとAuroraインスタンスを復元します。

この手順は、手順1で作成したテンプレートを1行変更するだけで実現できます。
テンプレートに加える修正を以下にdiffで示します。

<     ReplicationSourceIdentifier: 'arn:aws:rds:<AZ>:<AccountId>:db:<SourceInstanceName>'
---
>     # ReplicationSourceIdentifier: 'arn:aws:rds:<AZ>:<AccountId>:db:<SourceInstanceName>'
>     SnapshotIdentifier: arn:aws:rds:<AZ>:<AccountId>:cluster-snapshot:<SnapShotName>

ReplicationSourceIdentifierの代わりにSnapshotIdentifierが有効になり、スナップショットを元にクラスタを作成するという宣言に代わります。

このテンプレートを反映すると、レプリカとして宣言していたAuroraクラスタとインスタンスが削除され、スナップショットから復元されるクラスタ・インスタンスで置換されます。
古いクラスタ・インスタンスを削除する手間が省けました。
DBClusterIdentifierで指定したAuroraクラスタが別のものになることで、インスタンスも再構築されています。

以上でCloudFormationを使って、MySQLのAuroraレプリカが昇格したスタンドアロンAuroraクラスタを構築できました。
後はアプリケーションの接続DBをこのスタンドアロンAuroraクラスタへ変更することで、無事移行が完了となりますね。

まとめ

今回の4つの手順を経て作成されたテンプレートは、サービスインした実際のAWSリソースを正しく表現したものとなりました。

メンテナンス中にAuroraクラスタを置換する必要があり、しかも手作業も発生するという困った状況でしたが、今回の方針で進める大きな決め手となったのは事前に検証とリハーサルを繰り返して作業時間を正確に見積もれたことです。本番での不確定要素を最小限に出来たことで踏み切れました。

テンプレートで表現できたため、今後はメンテナンスウィンドウやセキュリティグループなどの変更をコードベースでレビューを挟んで実施できますね。

さいごに

スタートトゥデイテクノロジーズでは、一緒にサービスを作り上げてくれるエンジニアを大募集中です。 ご興味のある方は、以下のリンクからぜひご応募ください!

https://www.starttoday-tech.com/recruit/


  1. サービスに追加された新機能がCloudFormationで実装されるまでにはそれなりにラグがありますが、そことはうまく付き合っていきます。

  2. コネクションを受けているリードレプリカをクラスターからいきなり抜くとRDSはよしなに再接続処理をやってくれるんだろうか?という疑問はあります。スケールアウトは気軽にできますが・・

  3. AuroraクラスタのReplicationSourceIdentifierを外してレプリケーション設定を削除することが有効だと思ったのですが、クラスタが削除されて新規にまっさらなクラスタが作成されるだけでした。