Route53のフェイルオーバールーティングポリシーでエラーページを表示する
AWS SAAの出題範囲の1つでもある、高弾力性に関連する内容です。Route 53のフェイルオーバールーティングポリシーを選択して、障害発生時にエラーページを表示させることによって、システムの弾力性を高めることができます。
Route53では様々なルーティングポリシーを選択することが可能ですが、その1つがフェイルオーバールーティングポリシーです。
フェイルオーバーのルーティングにより、リソースが正常な場合にリソースにトラフィックをルーティングできます。また、最初のリソースが正常でない場合は別のリソースにルーティングできます。
フェイルオーバールーティング
今回はALB配下のEC2インスタンスでシステムを構築してプライマリWebサイトを構築します。プライマリ側に異常が発生した場合は、S3バケット内に配置したエラーページを表示するように設定します。
構築する環境

プライマリーWebサイト側の構成
2つのプライベートサブネットにEC2インスタンスを1つずつ配置します。インスタンスは最新のAmazon Linux 2ベースとします。
インスタンスの初期設定として、Apacheのインストール・起動の設定を行い、Webサーバとして動作させます。
EC2インスタンスの前面にELBを配置します。ELBはALBタイプとします。
エラーページ側の構成
次にエラーページですが、S3バケットで用意します。
バケットの静的Webサイトホストティング機能を有効化し、エラーページ用のHTMLファイルをバケットに配置します。
Route53の構成
最後にRoute53ですが、先述の通り、アクティブ・パッシブタイプのフェイルオーバールーティングポリシーを設定します。プライマリリソースにALBを指定し、セカンダリリソースにS3バケットを指定します。
フェイルオーバーの挙動確認ですが、以下の手順で実施します。
- プライマリリソースが正常時の動作を確認する。
- 2台のEC2インスタンスを停止させ、プライマリリソースに障害が発生した状況を再現する。
- セカンダリリソースにフェイルオーバーして、エラーページが表示されることを確認する。
- 2台のEC2インスタンスを起動させ、プライマリリソースが障害から復旧した状況を再現する。
- 再度プライマリリソースにトラフィックがルーティングされることを確認する。
環境構築用のCloudFormationテンプレートファイル
上記の構成をCloudFormationで構築します。
以下のURLにCloudFormationテンプレートを配置します。
なお今回の構成で使用するドメインは、Route53で取得したものを使用することを前提としています。そのためドメインのホストゾーン(HostedZone)に関する設定は行いません。AWS以外で取得したドメインを使用する場合は、上記のテンプレートにHostedZoneリソースの定義を追加で実施してください。
テンプレートファイルのポイント解説
今回の環境を構成するための、各テンプレートファイルのポイントを取り上げます。
プライベートサブネット内のインスタンスをALBにアタッチする
まずプライマリリソースを、ALBと2台のEC2インスタンスで構築します。
ポイントは2点あります。
1点目はインスタンスはプライベートサブネットに配置されているという点です。
ALBに紐付けることができるサブネットはパブリックサブネットです。ですから空のパブリックサブネットをAZごとに用意し、それを通じて各プライベートサブネット内のインスタンスにアクセスするという構成が必要です。
詳細は以下のページをご確認ください。
2点目はApacheをインストールするために、プライベートサブネット内のインスタンスでyumを実行する必要があるという点です。
通常、yumを実行する場合、インターネットに抜ける経路が必要となります。ただしAmazon Linuxの場合、S3バケット上にホスティングされるyumリポジトリにアクセスすることで、yumを実行することが可能です。インターネットにアクセスするルートがないプライベートサブネットにおいても、S3用エンドポイントを設置することで、セキュアにS3バケットにアクセスすることが可能です。
詳細については、以下のページをご確認ください。
今回はユーザーデータでyumによるApacheのインストールおよび起動設定、そしてindex.htmlにインスタンスIDを書き込んで作成します。
エラーページを設置するS3バケット名はドメイン名と一致させる
saa-01-001-s3.yamlでS3に関するリソースを定義します。
S3バケットにエラーページを設置し、公開するための設定を行います。
Resources:
S3Bucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Ref DomainName
AccessControl: Private
WebsiteConfiguration:
IndexDocument: index.html
Code language: YAML (yaml)
S3の静的Webサイトホスティング機能を使用して、エラーページを表示させます。本機能の詳細については、以下のページをご確認ください。
静的Webホスティング機能を使用してエラーページを表示させる上でポイントとなる点は、バケットの名前です。
バケットは、ドメインまたはサブドメインと同じ名前にする必要があります。例えば、サブドメイン acme.example.com を使用している場合、バケットの名前は acme.example.com にする必要があります。
Amazon S3 バケットでホストされているウェブサイトへのトラフィックのルーティング
バケット名はBucketNameプロパティで指定しますが、本プロパティにプライマリWebサイトで使用するドメイン名と同一の文字列を指定する必要があります。今回は組み込み関数Fn::Refを使用して、ドメイン名を参照します。
ヘルスチェックでプライマリリソースの障害の有無を確認する
saa-01-001-route53.yamlでRoute53に関するリソースを定義します。
まずRoute53で、プライマリリソースの障害の発生状況を確認できるように設定します。
Resources:
DnsHealthCheck:
Type: AWS::Route53::HealthCheck
Properties:
HealthCheckConfig:
Port: !Ref HTTPPort
Type: HTTP
ResourcePath: /
FullyQualifiedDomainName: !Ref ALBDnsName
RequestInterval: !Ref RequestInterval
FailureThreshold: !Ref FailureThreshold
Code language: YAML (yaml)
HealthCheckConfigプロパティで、ヘルスチェックする対象や方法、障害発生とみなす条件を指定します。
- FullyQualifiedDomainNameプロパティ:ヘルスチェックの対象をALBのルートページに指定する。
- Portプロパティ、Typeプロパティ:ヘルスチェックはHTTP(80/tcp)で行う。
- RequestIntervalプロパティ:ヘルスチェックは30秒に1回実施する。
- FailureThresholdプロパティ:ヘルスチェックの結果、HTTPステータスコードが3回連続で「200」で無かった場合、障害が発生したと見なす。
Route53のアクティブ・パッシブタイプのフェイルオーバー設定
最後にRoute53のレコード情報を定義します。
Resources:
AwstutNetDnsRecordGroup:
Type: AWS::Route53::RecordSetGroup
Properties:
HostedZoneName: !Sub "${DomainName}."
RecordSets:
# ALB
- Name: !Ref DomainName
Failover: PRIMARY
HealthCheckId: !Ref DnsHealthCheck
SetIdentifier: primary
Type: A
AliasTarget:
DNSName: !Ref ALBDnsName
EvaluateTargetHealth: true
HostedZoneId: !Ref ALBHostedZoneId
# S3
- Name: !Ref DomainName
Failover: SECONDARY
SetIdentifier: secondary
Type: A
AliasTarget:
DNSName: !Ref S3DnsName
HostedZoneId: !Ref S3HostedZoneId
Code language: YAML (yaml)
HostedZoneNameプロパティで、Route53に登録するドメイン名を指定します。先述のS3バケット名と同一のドメイン名を指定します。
RecordSetsプロパティで、HostedZoneNameプロパティで指定したドメイン名に紐付けるリソースを指定します。1つ目の要素がALBで、2つ目がS3バケットです。フェイルオーバーを設定する上で、特にポイントとなる設定は、以下の3点です。
- Failoverプロパティは、ALBはプライマリリソースなので「PRIMARY」、S3はセカンダリリソースなので「SECONDARY」を指定する。
- ALB側のHealthCheckIdプロパティに、先述のヘルスチェックリソースを指定する。
- ALB側のAliasTargetプロパティ内のEvaluateTargetHealthプロパティに「true」を指定する。
上記の設定を行うことで、ALBに障害が発生した場合に、自動的にフェイルオーバーし、S3側にトラフィックがルーティングされるようになります。
なおRecordSetGroup内のHostedZoneIdプロパティですが、設定するべき値が定められています。
まずALB側の値ですが、Elastic Load Balancing エンドポイントとクォータを参照すると、ap-northeast-1リージョンでは「Z14GRHDCWA56QT」を指定する必要があることがわかります。
次にS3バケット側の値ですが、Amazon Simple Storage Service エンドポイントとクォータによりますと、ap-northeast-1リージョンでは「Z2M4EHUR26P7ZW」を指定する必要があることがわかります。
環境構築
CloudFormationを使用して、本環境を構築し、実際の挙動を確認します。
CloudFormationスタックを作成し、スタック内のリソースを確認する
CloudFormationスタックを作成します。
スタック作成および各スタックの確認方法については、以下のページをご確認ください。
各スタックのリソースを確認した結果、今回作成された主要なリソースのIDや名前は以下の通りです。
- ドメイン名:awstut.net
- EC2インスタンスのインスタンスID:i-032febbe1b5fd6a9e, i-00520c950f3fbcd97
- ALB名:saa-01-001-ALB
- S3バケット名:awstut.net
- Route53ヘルスチェックID:d0d474a5-aaba-4cf3-b5c4-dc3075f56b49
Route53の設定状況を確認します。

テンプレートファイルで指定した通りに、プライマリにALB、セカンダリにS3バケットが登録されていることがわかります。
S3バケットにエラーページを設置する
S3バケットにエラーページを設置します。
$ echo "<html><head></head><body>Hello World form S3 Bucket.</body></html>" > ./index.html
$ aws s3 cp index.html s3://awstut.net
upload: ./index.html to s3://awstut.net/index.html
Code language: Bash (bash)
echoコマンドでHTMLファイルを作成後、AWS CLIでバケットにコピーします。

ALBに障害が発生した際は、フェイルオーバーして、このHTMLファイルが表示されることになります。
挙動確認:正常時
準備が整いましたので、構築したWebアプリにアクセスして挙動を確認します。
$ curl http://awstut.net
instance-id: i-00520c950f3fbcd97
$ curl http://awstut.net
instance-id: i-032febbe1b5fd6a9e
Code language: Bash (bash)
2台のEC2インスタンスのIDが交互に返ってきました。これはALBによって、2台のインスタンスにラウンドロビンでトラフィックがルーティングされているためです。
このことより、現状では、ALBおよび配下のEC2インスタンスは、ヘルスチェックに合格し、プライマリリソースとして正常に動作していることがわかります。
Route53ヘルスチェック状況確認:正常時
一応、正常時のヘルスチェックの状態も確認します。

Statusが「Healthy」とあります。
このことから、ヘルスチェックに成功していることがわかります。
EC2インスタンスを停止させて障害発生時を再現する
正常にプライマリリソースが動作していることがわかりました。
今度は2台のインスタンスを停止させて、意図的に障害が発生した状況を作ります。
$ aws ec2 stop-instances \
--instance-ids i-032febbe1b5fd6a9e i-00520c950f3fbcd97
Code language: Bash (bash)

両インスタンスのInstance stateの値が「Stopped」とありますので、停止しました。
Route53ヘルスチェック状況確認:障害発生時
インスタンスが停止した後、改めてヘルスチェックの状況を確認します。

Statusの値が「Unthealthy」となりました。ヘルスチェックに失敗しているということです。
これはインスタンスを停止したことによって、ALBに登録されている正常なターゲットがなくなったためです。
挙動確認:障害発生時
ヘルスチェックに失敗していることが確認できましたので、改めてWebアプリにアクセスします。
$ curl http://awstut.net
<html><head></head><body>Hello World form S3 Bucket.</body></html>
Code language: Bash (bash)
S3バケットに設置したHTMLファイルが返ってきました。
このことから、ALBへのヘルスチェックに失敗したことによって、Route53がトラフィックのルーティング先を、プライマリリソースであるALBから、セカンダリリソースであるS3にフェイルオーバーしたことがわかります。
インスタンスを起動させて障害復旧を再現する
障害発生時に自動的にフェイルオーバーすることはわかりました。続いて障害復旧時の挙動を確認します。
障害復旧を再現するために、先ほど停止させたインスタンスを再度起動させます。
$ aws ec2 start-instances \
--instance-ids i-032febbe1b5fd6a9e i-00520c950f3fbcd97
Code language: Bash (bash)

Instance Statusの値が「Running」となりました。これでインスタンスは現在起動中ということを意味しています。
挙動確認:障害復旧時
インスタンスが起動したところで、改めてWebアプリにアクセスし、挙動を確認します。
$ curl http://awstut.net
instance-id: i-032febbe1b5fd6a9e
$ curl http://awstut.net
instance-id: i-00520c950f3fbcd97
Code language: Bash (bash)
再びALB側にトラフィックがルーティングされていることがわかります。これはインスタンスが起動することによって、インスタンスがALBのターゲットとして再認識され、Route53からALBに対するヘルスチェックに成功したためです。

このようにRoute53のアクティブ・パッシブタイプのフェイルオーバールーティングポリシーにおいては、プライマリリソースが障害から復旧した場合、自動的にフェイルオーバー状態は解消され、再度プライマリ側にトラフィックがルーティングされるようになります。
まとめ
今回はRoute53のアクティブ・パッシブタイプのフェイルオーバーのハンズオンを通じて、障害発生時および障害復旧時のルーティングの挙動を確認しました。