Y_Yamashitaのブログ

勉強したことのアウトプット・メモ中心。たまに日記とか。

Route53のルーティング方式毎のクエリ応答切り替わりを確認する - 加重+フェイルオーバールーティング編

はじめに

前回のブログから随分と時間が経ってしまいました。 その間に、AWS Certified Developer - Associateに合格したり、転職のための準備などをやっていました。
転職のことについては、気が向いたらその内ブログにまとめようかと思います。

さて、今更ではありますが、以前Route53のシンプルルーティングの挙動を確認したので、今回はその続きとして、加重+フェイルオーバールーティングの挙動を確認したいと思います。

今回の構成

今回は、下図のように「プライマリリソース2台、セカンダリリソース2台」の構成にし、リソースのダウン状態に応じてアクセス結果がどうなるか見ていきたいと思います。
実務ではこのような構成は採用しないと思いますが、あくまで動作検証目的なのでご容赦ください。

f:id:YuY_83:20211107213547p:plain

今回はプライベートホステッドゾーンを使用し、VPC内の踏み台サーバからWEBサーバにアクセスすることにします。
NATゲートウェイは、プライベートサブネットのEC2にパッケージをインストールするために使用します。

環境構築の方法

今回は、VPC内の環境はCloudFormationで構築し、Route53の設定は手動で行います。
CloudFormationのテンプレートは以下のものを使用します。
Route53_Failover_Routing.yml

環境構築

VPC内構築(CloudFormation)

まずは上述のテンプレートを使ってVPC内の環境を構築します。

[cloudshell-user@ip-10-0-175-26 ~]$ ls -l
total 3000
-rw------- 1 cloudshell-user cloudshell-user 3489792 Sep 25 08:20 core.3704
-rw-rw-r-- 1 cloudshell-user cloudshell-user   18077 Nov  6 16:31 Route53_Failover_Routing.yml
[cloudshell-user@ip-10-0-175-26 ~]$ 
[cloudshell-user@ip-10-0-175-26 ~]$ 
[cloudshell-user@ip-10-0-175-26 ~]$ 
[cloudshell-user@ip-10-0-175-26 ~]$ aws cloudformation validate-template --template-body file:///home/cloudshell-user/Route53_Failover_Routing.yml
{
    "Parameters": [
        {
            "ParameterKey": "KeyName",
            "NoEcho": false,
            "Description": "Name of an existing EC2 KeyPair to enable SSH access to the instances"
        },
        {
            "ParameterKey": "EnvironmentType",
            "DefaultValue": "test",
            "NoEcho": false,
            "Description": "The environment type"
        },
        {
            "ParameterKey": "ClientIP",
            "DefaultValue": "0.0.0.0/0",
            "NoEcho": false,
            "Description": "Client IP Address"
        }
    ],
    "Description": "EC2 WEB Server Deploy for Route53 Failover Routing"
}
[cloudshell-user@ip-10-0-175-26 ~]$ 
[cloudshell-user@ip-10-0-175-26 ~]$ 
[cloudshell-user@ip-10-0-175-26 ~]$ 
[cloudshell-user@ip-10-0-175-26 ~]$ aws cloudformation create-stack \
>  --stack-name EC2deploy-for-Route53Failover \
>  --template-body file:///home/cloudshell-user/Route53_Failover_Routing.yml \
>  --parameters \
>  ParameterKey=KeyName,ParameterValue="xxx" \
>  ParameterKey=ClientIP,ParameterValue="114.xxx.xxx.xxx/32" \
>  ParameterKey=EnvironmentType,ParameterValue="test"
{
    "StackId": "arn:aws:cloudformation:ap-northeast-1:xxxxxxxxxxxx:stack/EC2deploy-for-Route53Failover/888ef8d0-3f1f-11ec-8a07-069142138e3f"
}
[cloudshell-user@ip-10-0-175-26 ~]$ 
[cloudshell-user@ip-10-0-175-26 ~]$
[cloudshell-user@ip-10-0-175-26 ~]$ aws cloudformation describe-stacks --stack-name EC2deploy-for-Route53Failover
{
    "Stacks": [
        {
            "StackId": "arn:aws:cloudformation:ap-northeast-1:xxxxxxxxxxxx:stack/EC2deploy-for-Route53Failover/888ef8d0-3f1f-11ec-8a07-069142138e3f",
            "StackName": "EC2deploy-for-Route53Failover",
            "Description": "EC2 WEB Server Deploy for Route53 Failover Routing",
            "Parameters": [
                {
                    "ParameterKey": "KeyName",
                    "ParameterValue": "xxx"
                },
                {
                    "ParameterKey": "EnvironmentType",
                    "ParameterValue": "test"
                },
                {
                    "ParameterKey": "ClientIP",
                    "ParameterValue": "114.xxx.xxx.xxx/32"
                }
            ],
            "CreationTime": "2021-11-06T16:35:21.031000+00:00",
            "RollbackConfiguration": {},
            "StackStatus": "CREATE_COMPLETE",
            "DisableRollback": false,
            "NotificationARNs": [],
            "Outputs": [
                {
                    "OutputKey": "PublicIPaddress",
                    "OutputValue": "18.xxx.xxx.xxx",
                    "Description": "PublicIpaddress for Bastion"
                },
                {
                    "OutputKey": "InstanceSecurityGroup1",
                    "OutputValue": "sg-xxxxxxxxxxxxxxxxx",
                    "Description": "InstanceSecurityGroup1",
                    "ExportName": "EC2deploy-for-Route53Failover-InstanceSecurityGroup1"
                },
                {
                    "OutputKey": "PublicSubnet1",
                    "OutputValue": "subnet-xxxxxxxxxxxxxxxxx",
                    "Description": "PublicSubnet1",
                    "ExportName": "EC2deploy-for-Route53Failover-PublicSubnet1"
                }
            ],
            "Tags": [],
            "EnableTerminationProtection": false,
            "DriftInformation": {
                "StackDriftStatus": "NOT_CHECKED"
            }
        }
    ]
}
[cloudshell-user@ip-10-0-175-26 ~]$ 
[cloudshell-user@ip-10-0-175-26 ~]$ 



Route53設定

次はRoute53の設定です。加重+フェイルオーバールーティングの場合、先にヘルスチェックの作成をする必要があります。
ただし、今回WEBサーバにはパブリックIPを割り当てていないので、IPアドレスによるヘルスチェックは出来ません。詳細は以下のリンクを参照してください。

docs.aws.amazon.com


そのため、今回はCloudWatchアラームを使用してヘルスチェックを行います。

CloudWatchアラームの作成

というわけで、まずCloudWatchアラームを作成します。

f:id:YuY_83:20211107222911p:plain
EC2インスタンスの「StatusCheckFailed_Instance」メトリクスを使用します。

f:id:YuY_83:20211107223047p:plain
インスタンス毎にアラームを作成します。

Route53ヘルスチェックの作成

続いて、Route53ヘルスチェックを作成します。

f:id:YuY_83:20211107223357p:plain
まずはヘルスチェック作成ボタンを押します。


f:id:YuY_83:20211107223429p:plain
続いて、諸々の情報を入力します。なお、今回はアラームが不足状態の時は異常扱いとしました。理由は、疑似障害として対象インスタンスを手動で停止した所、CloudWatchアラームが「NG」ではなく「データ不足」になってしまったためです。


f:id:YuY_83:20211107223820p:plain
Route53ヘルスチェックも、インスタンス毎に作成します。

DNSレコードの追加

続いて、DNSレコードを追加していきます。フェイルオーバーのプライマリリソース、セカンダリリソースが複数ある場合、エイリアスレコードを併用する必要があります。詳細は以下のリンクを参照してください。

docs.aws.amazon.com



まずはインスタンスのAレコードを加重ルーティングで登録します。最初はEC2Aです。

f:id:YuY_83:20211107224811p:plain
レコード名は「failover-primary」としています。先ほど作成したヘルスチェックと紐づけます。TTLは図では300秒になっていますが、後で10秒に変更しました。(TTLが長いと、短時間のアクセスでは想定通り加重されない場合がありました。)EC2Bも同様に設定します。


f:id:YuY_83:20211107224937p:plain
EC2C、EC2Dはレコード名を「failover-secondary」としました。


続いて、名前解決したいホスト名「www.example.com」のエイリアスレコードを、フェイルオーバールーティングで作成します。

f:id:YuY_83:20211107225121p:plain
「failover-primary」と紐づけ、プライマリリソースとします。


f:id:YuY_83:20211107225503p:plain
もう一つ「www.example.com」のレコードを作成します。こちらは「failover-secondary」と紐づけ、セカンダリリソースとします。


f:id:YuY_83:20211107225557p:plain
最終的にレコードは上図のようになりました。

アクセス確認

今回のアクセス確認方法

前述通り、今回は踏み台EC2からアクセス確認します。簡便なスクリプトを使い、10秒おきに「www.example.com/index.html」宛てに100回curlして、応答したインスタンスの数をカウントします。(※各WEBサーバにはCloudFormaitonでindex.htmlを配置しています。index.htmlのBodyに自身の名前が記述されているので、それを抜き出します。)

踏み台EC2で使用するスクリプトの中身は以下です。これもCloudFormationで事前に配置しています。

#!/bin/bash

for ((i=1; i<=100; i++))
do
  curl -s http://www.example.com/index.html | grep -o "EC2." >> ./curlresult
  sleep 10
done

cat ./curlresult | sort | uniq -c

echo "curl done!!"



アクセス確認結果

インスタンスの正常時

インスタンスの正常時は、プライマリリソースの2台で加重ルーティングされました。

[ec2-user@ip-10-100-0-78 ~]$ 
[ec2-user@ip-10-100-0-78 ~]$ ./curlrepeat.sh
     70 EC2A
     30 EC2B
curl done!!
[ec2-user@ip-10-100-0-78 ~]$ 
[ec2-user@ip-10-100-0-78 ~]$ 



f:id:YuY_83:20211107213547p:plain

EC2Aの異常時

EC2Aに異常がある場合、もう1台のプライマリリソースであるEC2Bに全アクセスが集中しました。

[ec2-user@ip-10-100-0-78 ~]$ ./curlrepeat.sh
    100 EC2B
curl done!!
[ec2-user@ip-10-100-0-78 ~]$ 



f:id:YuY_83:20211107232225p:plain

EC2A、EC2Bの異常時

プライマリリソースの全台に異常がある場合、セカンダリリソースの2台で加重ルーティングされました。

[ec2-user@ip-10-100-0-78 ~]$ ./curlrepeat.sh
     17 EC2C
     83 EC2D
curl done!!
[ec2-user@ip-10-100-0-78 ~]$ 



f:id:YuY_83:20211107233209p:plain

EC2A、EC2B、EC2Cの異常時

プライマリリソースの全台+セカンダリリソースの1台に異常がある場合、セカンダリリソースの残り1台に全アクセスが集中しました。

[ec2-user@ip-10-100-0-78 ~]$ ./curlrepeat.sh
    100 EC2D
curl done!!
[ec2-user@ip-10-100-0-78 ~]$



f:id:YuY_83:20211107233541p:plain

結論

というわけで、以下のような結果となりました。

  • プライマリリソースが全異常にならない限り、プライマリリソースの正常なレコードで加重ルーティングされる。
  • プライマリリソースが全異常になっている間は、セカンダリリソースの正常なレコードで加重ルーティングされる。

ログの記載は省略しますが、EC2Bのみ異常の場合や、EC2A、EC2B、EC2Dに異常がある場合も、上記ルールに則った結果となりました。

なお、今回はプライマリ、セカンダリ両方にヘルスチェックを設定していましたが、ヘルスチェックを設定していない場合は挙動が変わるようです。詳細は以下リンクを参照ください。

docs.aws.amazon.com

Route53ヘルスチェックについての補足

もしヘルスチェックをIPで行う場合、Route53ヘルスチェッカーからのアクセスをセキュリティグループ等で許可する必要があります。Route53ヘルスチェッカーはVPC外に存在しているためです。詳細は以下のリンクを参照してください。

docs.aws.amazon.com



また、Route53ヘルスチェッカーのIPアドレスはリージョン毎に異なります。アドレス範囲については以下のリンクを参照してください。

docs.aws.amazon.com

おわりに

というわけで、前回のシンプルルーティングから2ヶ月も経ってしまいましたが、ようやく加重+フェイルオーバールーティングの設定と動作確認が出来ました。

今回もCloudFormationの設定で色々躓いたうえ、複数リソースでのフェイルオーバールーティング設定でも苦戦したため、予想以上に時間を使ってしまいました。
ただ、おかげでCloudFormationやRoute53に少しずつ慣れてきたので、今後も試行錯誤しながら色々試していきたいと思います。