Amazon Auroraフェイルオーバー中のダウンタイムを調べてみた
Amazon Auroraデータベースは可用性に優れており、その高速なフェイルオーバーは売りの一つです。
Amazon Auroraの可用性に関するドキュメントによると、レプリカが1つ以上あると、フェイルオーバーによりレプリカがプライマリインスタンスに昇格し、60秒未満でフェイルオーバーが完了します。
DB クラスターに 1 つ以上の Aurora レプリカがある場合は、障害発生中に 1 つの Aurora レプリカがプライマリインスタンスに昇格されます。障害イベントによって短い中断が発生し、その間例外によって読み取りと書き込みオペレーションが失敗します。ただし、一般的なサービスの復元時間は 120 秒未満であり、多くの場合 60 秒未満で復元されます。DB クラスターの可用性を高めるために、複数のアベイラビリティーゾーン内で少なくとも 1 つ以上の Aurora レプリカを作成することをお勧めします。
では、フェイルオーバーが概ね60秒で完了するとして(FAQでは30秒の記載)、フェイルオーバー中に読み/書きオペレーションがどのように失敗するのか気になったので、調べてみました。
フェイルオーバーまとめ
プライマリ・レプリカがそれぞれ1台の場合の
- フェイルオーバーの流れ
- 発生するイベント
- ダウンタイム
を一枚にまとめたのが次の図です。
順に解説します。
フェイルオーバーの流れ
単純化のために、2AZにプライマリ・レプリカが1インスタンスずつあるシングルスターのAmazon Aurora MySQLクラスターを用意します。
このAuroraクラスタでフェイルオーバーが発生すると、おおよそ、以下の流れでフェイルオーバーが行われます。
- 手動のフェイルオーバー命令
- 旧レプリカをプライマリに昇格(再起動を伴う)
- 旧プライマリをリーダーに降格(再起動を伴う)
- 読み書き可能なクラスターエンドポイントを新プライマリに向ける # 以降は読み書き可能
- 読み取りのみ可能なリーダーエンドポイントを新レプリカに向ける
#4 の完了をもって、クラスター、及び、リーダーエンドポイントが利用可能になるため、フェイルオーバー完了とみなせます。
イベントとの関連
Amazon Aurora はクラスター・インスタンスなどの単位でイベントを発火します。
フェイルオーバーに伴うクラスター・インスタンス操作でも、このイベントが発火されます。
主要なイベントとしては、 以下があります。
- #2 の開始時、クラスターイベント Started cross AZ failover to DB instance の発火
- #3 の開始時、旧プライマリに対して A new writer was promoted. Restarting database as a reader. の発火(新プライマリの昇格完了後に旧プライマリを降格)
- #5 の後、しばらくして Completed failover to DB instance の発火(#4 の両エンドポイントが利用可能になったタイミングではないことに注意)
イベントには発生時刻も含まれています。
読み書きオペレーションが失敗するケース
このフェイルオーバー中、以下のケースなどで読み書きのオペレーションが失敗します。
再起動中の接続性の障害
#2/#3 のインスタンス再起動中は接続障害が発生します。
具体的には
ERROR 2003 (HY000): Can't connect to MySQL server on 'xxx.rds.amazonaws.com.:3306' (111)
や
ERROR 2013 (HY000) at line 1: Lost connection to MySQL server during query
というようなエラーが発生します。
エンドポイントとインスタンスの役割の不一致
#3 〜 #4 の間では、読み書き用のクラスターエンドポイントは新レプリカインスタンスを指しています。
しかし、このインスタンスはリーダーに降格しているため(innodb_read_only=ON
)、書き込もうとすると、エラーが発生します。
具体的には
ERROR 1290 (HY000) at line 1: The MySQL server is running with the --read-only option so it cannot execute this statement
というようなエラーが発生します。
このエラーを避けたい場合は、次のナレッジベースを参照ください。
Aurora DB クラスターがフェイルオーバーした後の読み取り専用エラーのトラブルシューティング
一方で、#3 の後、読み取り用のリーダーエンドポイントは新プライマリインスタンスを指しています。
読み書き可能ですが(innodb_read_only=OFF
)、参照クエリのみ処理します。
#4 〜 #5 の間では、この新プライマリインスタンスが
- 読み書き用のクラスターエンドポイント
- 読み取り専用のリーダーエンドポイント
を兼任し、一時的に負荷が高まります。
次のドキュメントにも、この挙動についての記載があります。
フェイルオーバーの際に、Aurora レプリカが新しいプライマリ DB インスタンスに昇格されると、リーダーエンドポイントは接続を DB クラスターの新しいプライマリ DB インスタンスに短時間だけ振り向ける場合があります。
フェイルオーバー中のダウンタイムを計測してみた
最後に参考情報として、フェイルオーバー中のダウンタイムを2回計測したため共有します。
環境
- DBエンジン : 5.7.mysql_aurora.2.10.2
- インスタンスタイプ : db.t3.small
- 2AZを利用し、プライマリ、レプリカを異なるAZにデプロイ
- 手動でフェイルオーバーを発生
計測結果
イベント | 1回目 | 2回目 |
---|---|---|
旧レプリカの再起動 | 5.5 | 5.5 |
旧プライマリの再起動 | 4.5 | 4.0 |
旧プライマリの書き込み失敗 | 4.5 | 4.5 |
クラスターエンドポイントの更新 | 14.0 | 14.5 |
リーダーエンドポイントの更新 | 13.5 | 10.0 |
イベントのフェイルオーバー期間 | 32.4 | 26.7 |
※ 単位は秒
イベントと対応する期間は次の図の通りです。
繰り返しとなりますが、クラスターエンドポイントが新しいプライマリを向くように更新されると、クラスター・リーダーエンドポイントともに利用可能になります。 サービスへの影響という観点では、この期間を重要視してください。 今回の計測では、上記表の通り、15秒弱でした。
また、すべての計測において、リーダーエンドポイントの更新から何秒も遅れて、イベントのフェイルオーバー終了通知が行われました。
検証用スクリプト
#!/bin/bash W=XXX.eu-central-1.rds.amazonaws.com # cluster endpoint R=YYY.eu-central-1.rds.amazonaws.com # reader endpoint while true; do date +%S WH=$(dig +short CNAME $W) RH=$(dig +short CNAME $R) echo W $WH echo R $RH # write mysql -h $WH -u admin -pYOUR-PASSWORD test -e 'insert into x values(1)' mysql -h $WH -u admin -pYOUR-PASSWORD test -e "show variables where variable_name='innodb_read_only'" # read mysql -h $RH -u admin -pYOUR-PASSWORD test -e 'select 1' mysql -h $RH -u admin -pYOUR-PASSWORD test -e "show variables where variable_name='innodb_read_only'" sleep 0.3 done
各ループにおいて、クラスター/リーダーエンドポイントをインスタンスエンドポイントで解決させ、後続処理を行っています。
グローバル変数 innodb_read_only
を用い、接続先のインスタンスが実際にプライマリ・レプリカのどちらなのか判定しています。
フェイルオーバーを高速化するには?
クライアント・ミドルウェアでフェイルオーバーを高速化することも期待できます。
クライアントサイドでは
ミドルウェアでは
- Amazon RDS Proxy
- サードパーティーの MariaDB MaxScale や ProxySQL
などの利用をご検討ください。
最後に
Amazon Auroraのフェイルオーバー中の可用性への影響を確認しました。
旧リーダー、旧プライマリの順に構成変更が走り、クラスタエンドポイントが新プライマリに向くまでは、読み書きオペレーションが一部失敗します。
フェイルオーバーで障害が起きていた時間帯をざっくりと把握したい場合は、イベントの "Started cross AZ failover ..."/"Completed failover ..." が便利です。 より精緻に、実際にオペレーションが失敗していた時間帯を調査したい場合は、アプリケーションログから、通信断や更新失敗ログを頼りに、障害時間帯を更に絞り込むことが可能です。
今回はAurora MySQLで調査しましたが、PostgreSQLでも同様の結果になると思われます。
それでは。