mitsuのぶろぐ

基本的にはプログラミングの話のつもり。

AWS CDKでサーバーレスなフェイルオーバールーティングを実装してみた

serverless-failover-routing-stack

大阪リージョンもついにローカルリージョンから正式なリージョンへと昇格したので、冗長化した構成を試してみたいと思い実装しました。
やったこととしては

  • 東京リージョンと大阪リージョンにapi-gateway × lambda で簡単なAPIを用意
  • Route53で東京リージョン側をPRIMARY、大阪リージョン側をSECONDARYとしてfailoverした際に切り替えれるようにした

ソースコード

github.com

フェイルオーバーの試し方

  • CDKのスタックを流し、環境をつくる
  • packages/cdk/lib/config.ts で設定したドメインを叩き、 東京リージョン側のレスポンスが返ってくることを確認する
  • 東京リージョン側のlambdaのレスポンスを意図的に失敗するようにする
  • -> 30~60秒後に大阪リージョンからレスポンスがくるようになる

ハマったこと & 注意点

Route53の設定が Cfn のメソッドで実装する必要があった(2021/04/02時点)

High Level Construct がまだ提供されていないようだったので、 Low Level Construct で実装する必要がありました。
そのため、Route53のCloudFormationの理解を求められました。
AWS::Route53::RecordSet
AWS::Route53::HealthCheck

  • CfnRecordSet.failover の型は string だが許容される文字は PRIMARYSECONDARY のどちらかのみ
  • CfnRecordSet. AliasTarget に設定する dnsNamehostedZoneIdapi-gatewayAliasDomainNameAliasHostedZoneId を使用する
  • api-gatewayで生成されるendpoint(今回healthcheckに利用するendpoint)は https でしか叩けないため、CfnHealthCheck. healthCheckConfigtypeport にはそれぞれ HTTPS443 を指定する必要がある
  • CfnHealthCheck. healthCheckConfig. fullyQualifiedDomainName はいわゆる URI の部分を差し込む必要がある
    • CDK上の apigw.LambdaRestApi からは urlForPathapi-gatewayのendpointを取得することはできるが https://xxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/ として返ってきてしまい、 https:///prod の箇所が不要である
    • そのため this.healthCheckDomainName = `${this.api.restApiId}.execute-api.${props.env?.region}.amazonaws.com`; といったように自前で URI 部分だけstringに格納して、CfnHealthCheck. healthCheckConfig. fullyQualifiedDomainName にわたす必要がある

CfnHealthCheck. healthCheckConfig のデフォルトの設定値だと、failoverで切り替えするまでに時間がかかる

HealthCheck を行ってターゲットを切り替えようとする際に、Route53では以下のように評価を行っているとのこと。

Route 53 はヘルスチェッカーからデータを集計し、エンドポイントが正常であるかどうかを判断します。
・18% を超えるヘルスチェッカーがエンドポイントを正常であるとレポートした場合、Route 53 はそのエンドポイントを正常と見なします。
・ ヘルスチェッカーが正常であるエンドポイントが 18% 以下であるとレポートした場合、Route 53 はそのエンドポイントを異常と見なします。

Amazon Route 53 がヘルスチェックの正常性を判断する方法

そのため、エラーがおきたらすぐ切り替わるかというとそうではありません。

また、 HealthCheckConfig のデフォルト値は以下のような設定になります

  • FailureThreshold : 3
  • RequestInterval : 30
  • Regions : [us-east-1, us-west-1, us-west-2, eu-west-1, ap-southeast-1, ap-southeast-2, ap-northeast-1, sa-east-1] (現在ヘルスチェッカーとして指定できるリージョンすべてから叩かれる)

18% を超えるヘルスチェッカー がとあるため、Regionsの数が多くてFailureThresholdの条件も回数が多いと18% に至るまでに時間がかかります。
※厳密に計測はできていないですが、上記のデフォルト値でfailoverさせると切り替えまでに1分以上かかっていました

そのためエラーが発生したらすぐに切り替えたい、ということであれば上記の値を低め、少なめに設定する必要があります。
今回試した値は以下のような設定値にしており、これだと30秒から長くても1分以内には切り替わるようになりました。

  • FailureThreshold : 1
  • RequestInterval : 10
  • Regions : [ap-southeast-1, ap-southeast-2, ap-northeast-1]

このようにいろいろ考慮する必要はありましたが、ひとまずfailover routingはできたかなと思います。

Low Level Construct はしんどいな〜と思いましたが、本来 High Level Construct で書くときも同じくらい把握して書かないとだめだなと再認識しました。