やるきげんきなみき

Web系インフラエンジニアのTips集

Amazon Auroraのカスタムエンドポイントをフェイルオーバーに追従させる

はじめに

こんにちは!インフラエンジニアのなみきです。

Webサーバーなどユーザーにサービスを提供するサーバーと、バッチサーバーなど裏方の処理を行うサーバーがある時、バッチ処理によるDB負荷がユーザー側へ影響しないよう、接続するDBインスタンスを分けたいシーンがあると思います。

ここで役立つのがAmazon Auroraのカスタムエンドポイントです。 以下のようにエンドポイントを使い分けるのがよくあるユースケースかと思います。

ユーザーにサービス提供するWebサーバーと裏側のバッチサーバーで異なるインスタンスを利用したい

ここで、フェイルオーバーしたときの挙動はどうなるのだろうと疑問に思いました。

リーダーだけを含むカスタムエンドポイントを作ったつもりでも、フェイルオーバーで昇格してしまったら、リーダーとライターが混ざったカスタムエンドポイントになってしまうのでは? そして、ライターからリーダーになったインスタンスには接続がなくなるのでは?

フェイルオーバー時に考えられる懸念。ライターになったinstance2にはreadが来てしまい、リーダーになったinstance1にはどこからも接続されない

理想としては、もしフェイルオーバーが起きてライターとリーダーが入れ替わったとしても、「カスタムエンドポイントはリーダーインスタンスに接続する」という点は変わらないようにしたいです。

理想の挙動。フェイルオーバー時にカスタムエンドポイントの宛先が動的に変わる

そこで、理想を満たす方法はないか、RDSのカスタムエンドポイントについて調べて検証してみました。

結論

  • カスタムエンドポイントをCLIで作成する
  • エンドポイントタイプを READER に指定する
  • ライターインスタンスも含める形でカスタムエンドポイントに登録する

このように設定すると、理想を満たす挙動にできます。

詳しくは以下より解説します。

CLIでのみ確認できる「エンドポイントタイプ」

まずはカスタムエンドポイントで何ができるのか公式ドキュメントを読んでみると、エンドポイントタイプという設定を見つけました。

https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/Aurora.Overview.Endpoints.html

AWS CLI および Amazon RDS API では: エンドポイントタイプを指定できます。そのため、エンドポイントタイプを READER または WRITER に設定すると、フェイルオーバーやプロモーション時にエンドポイントのメンバーシップが自動的に調整されます。 例えば、タイプ READER のカスタムエンドポイントに Aurora レプリカが含まれ、その後、ライターインスタンスに昇格されたとします。新しいライターインスタンスは、カスタムエンドポイントの一部ではなくなります。

コンソールでは表示されませんが、CLIでdescribeしてみると確かに CustomEndpointType という設定が存在していました。

$ aws rds describe-db-cluster-endpoints --db-cluster-endpoint-identifier myendpoint
{
    "DBClusterEndpoints": [
        {
            "DBClusterEndpointIdentifier": "myendpoint",
            "DBClusterIdentifier": "mycluster",
            "DBClusterEndpointResourceIdentifier": "cluster-endpoint-XXXXXXXXXXXX",
            "Endpoint": "myendpoint.cluster-custom-xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com",
            "Status": "available",
            "EndpointType": "CUSTOM",
            "CustomEndpointType": "ANY",
            "StaticMembers": [
                "instance1",
                "instance2"
            ],
            "ExcludedMembers": [],
            "DBClusterEndpointArn": "arn:aws:rds:ap-northeast-1:000000000000:cluster-endpoint:myendpoint"
        }
    ]
}

コンソールではエンドポイントタイプを指定できません。コンソールを使用して作成されたカスタムエンドポイントは、すべて ANY タイプです。 そのため、フェイルオーバーやプロモーションにより DB インスタンスのロールがライターとリーダーの間で変更されても、Aurora はカスタムエンドポイントのメンバーシップを変更しません。

つまり、コンソール上から「リーダーのみで構成されたカスタムエンドポイント」を作っても、そのメンバーがフェイルオーバーで昇格してしまうと「リーダーとライターが混在するエンドポイント」になってしまうということです。 確実にリーダーのみのエンドポイントとするためには、CLIでエンドポイントタイプを READER に指定すると良いと分かりました。

しかし、「フェイルオーバー時にメンバーシップが自動的に調整される」のであれば、エンドポイントタイプが READER のカスタムエンドポイントにリーダーインスタンスだけを含めておくと、フェイルオーバーしたときにアクセスできるインスタンスが1台減ってしまいます。これは理想の挙動ではありません。

リーダーインスタンスだけを含めるとフェイルオーバー時に1台無効になる

そこで、あらかじめカスタムエンドポイントにライターインスタンスも含めておくことができれば、その時々のリーダーインスタンスだけが有効になるという挙動をしてくれるのでは?と考えました。

ライターインスタンスも含めておくとフェイルオーバーに対応できるのでは?という予想図

とはいえ、エンドポイントタイプがREADERなのにライターインスタンスを追加するなんてできるのでしょうか。 期待通りの挙動をしてくれるのか、実際に検証してみました。

検証

1. クラスターを作成

以下の3つのインスタンスからなるクラスターを作成しました。

2. カスタムエンドポイントをCLIで作成

メンバーの指定方法には2通りあります。ここではCLIのオプションに従って以下のように呼びます。

挙動の違いを確認するため、両方のリストを作ってみます。

Static members

  • --db-cluster-endpoint-identifier : 作成したいエンドポイント名
  • --db-cluster-identifier : クラスター名
  • --endpoint-type : エンドポイントタイプ
    • READER を指定
  • --static-members : 含めたいインスタンス
    • readerとwriterを指定

エンドポイントタイプを READER としておきながらメンバーにライターインスタンスを含めたので、この時点でエラーにならないか不安でしたが、無事に作成されました。 作成完了までには3〜4分ほどかかりました。

$ aws rds create-db-cluster-endpoint \
  --db-cluster-endpoint-identifier reader-static \
  --db-cluster-identifier mycluster \
  --endpoint-type READER \
  --static-members reader writer
{
    "DBClusterEndpointIdentifier": "reader-static",
    "DBClusterIdentifier": "mycluster",
    "DBClusterEndpointResourceIdentifier": "cluster-endpoint-XXXXXXXXXXXX",
    "Endpoint": "reader-static.cluster-custom-xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com",
    "Status": "creating",
    "EndpointType": "CUSTOM",
    "CustomEndpointType": "READER",
    "StaticMembers": [
        "reader",
        "writer"
    ],
    "ExcludedMembers": [],
    "DBClusterEndpointArn": "arn:aws:rds:ap-northeast-1:000000000000:cluster-endpoint:reader-static"
}

コンソールから確認すると、エンドポイントメンバーとしてはreaderしか見えていません。

カスタムエンドポイントのエンドポイントメンバーにはreaderだけがある

VPC内から名前解決すると、何回やってもreaderしか返りません。

$ dig +noall +ans reader-static.cluster-custom-xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com
reader-static.cluster-custom-xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com. 1 IN CNAME reader.xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com.
reader.xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com. 5 IN A 10.xx.xx.xx

以上のことから、エンドポイントタイプに合わないインスタンスもメンバーとしては追加できるが、エンドポイントタイプと一致するメンバーにしかルーティングされないということが分かりました。 また、コンソールに表示されているのがルーティング対象のメンバーであることが分かりました。

Excluded members

さきほどのコマンドのオプションを変えます。

こちらもStatic同様ライターインスタンスを含む形になりますが、エラーにならず無事に作成されました。

$ aws rds create-db-cluster-endpoint --profile goc-stg-admin \
  --db-cluster-endpoint-identifier reader-excluded \
  --db-cluster-identifier mycluster \
  --endpoint-type READER \
  --excluded-members reader-for-batch
{
    "DBClusterEndpointIdentifier": "reader-excluded",
    "DBClusterIdentifier": "mycluster",
    "DBClusterEndpointResourceIdentifier": "cluster-endpoint-XXXXXXXXXXXX",
    "Endpoint": "reader-excluded.cluster-custom-xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com",
    "Status": "creating",
    "EndpointType": "CUSTOM",
    "CustomEndpointType": "READER",
    "StaticMembers": [],
    "ExcludedMembers": [
        "reader-for-batch"
    ],
    "DBClusterEndpointArn": "arn:aws:rds:ap-northeast-1:000000000000:cluster-endpoint:reader-excluded"
}

コンソールから確認すると、エンドポイントメンバーとしてはreaderしか見えていません。

カスタムエンドポイントのエンドポイントメンバーにはreaderだけがある

VPC内から名前解決すると、何回やってもreaderしか返りません。

$ dig +noall +ans reader-static.cluster-custom-xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com
reader-static.cluster-custom-xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com. 1 IN CNAME reader.xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com.
reader.xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com. 5 IN A 10.xx.xx.xx

こちらもStatic同様、エンドポイントタイプに合わないメンバーも含むこともできるが、エンドポイントタイプと一致するメンバーにしかルーティングされないということが分かりました。

カスタムエンドポイント作成のまとめ

  • エンドポイントタイプに合わないインスタンスを含めることもできるが、ルーティングはされない
  • コンソールに表示されているのがルーティング対象のメンバー
  • 作成時点でStaticとExcludedの挙動の違いはなかった

理想を満たすためにはエンドポイントタイプに合わないインスタンスを含む形でカスタムエンドポイントを作成できる必要がありました。 作成の時点で弾かれる可能性もあると思っていたので、特に弾かれず作成できてホッとしました。

3. リーダーを追加

ここでStaticとExcludedの挙動の違いを確認するため、クラスターに「reader2」というリーダーインスタンスを1台追加してみました。

Static members

コンソールから確認すると、メンバーは変化なくreaderのみのままです。

カスタムエンドポイントのエンドポイントメンバーにはreaderだけがある

CLIで見ても変化なし。

$ aws rds describe-db-cluster-endpoints --db-cluster-endpoint-identifier reader-static
{
    "DBClusterEndpoints": [
        {
            "DBClusterEndpointIdentifier": "reader-static",
            "DBClusterIdentifier": "mycluster",
            "DBClusterEndpointResourceIdentifier": "cluster-endpoint-XXXXXXXXXXXX",
            "Endpoint": "reader-static.cluster-custom-xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com",
            "Status": "available",
            "EndpointType": "CUSTOM",
            "CustomEndpointType": "READER",
            "StaticMembers": [
                "reader",
                "writer"
            ],
            "ExcludedMembers": [],
            "DBClusterEndpointArn": "arn:aws:rds:ap-northeast-1:000000000000:cluster-endpoint:reader-static"
        }
    ]
}

VPC内から名前解決してみると、追加前と同じくreaderしか返りません。

$ dig +noall +ans reader-static.cluster-custom-xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com
reader-static.cluster-custom-xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com. 1 IN CNAME reader.xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com.
reader.xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com. 1 IN A 10.xx.xx.xx

Excluded members

コンソールから確認すると、メンバーにreader2が追加されています。

カスタムエンドポイントのエンドポイントメンバーにはreaderだけがある

CLI上は変化なし。ExcludedMembersにreader-for-batchがいるのみです。

$ aws rds describe-db-cluster-endpoints --db-cluster-endpoint-identifier reader-excluded
{
    "DBClusterEndpoints": [
        {
            "DBClusterEndpointIdentifier": "reader-excluded",
            "DBClusterIdentifier": "mycluster",
            "DBClusterEndpointResourceIdentifier": "cluster-endpoint-XXXXXXXXXXXX",
            "Endpoint": "reader-excluded.cluster-custom-xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com",
            "Status": "available",
            "EndpointType": "CUSTOM",
            "CustomEndpointType": "READER",
            "StaticMembers": [],
            "ExcludedMembers": [
                "reader-for-batch"
            ],
            "DBClusterEndpointArn": "arn:aws:rds:ap-northeast-1:000000000000:cluster-endpoint:reader-excluded"
        }
    ]
}

VPC内から名前解決すると、readerとreader2の両方が返ってきます。

$ dig +noall +ans reader-excluded.cluster-custom-xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com
reader-excluded.cluster-custom-xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com. 1 IN CNAME reader2.xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com.
reader2.xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com. 5 IN A 10.xx.xx.xx
$ dig +noall +ans reader-excluded.cluster-custom-xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com
reader-excluded.cluster-custom-xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com. 0 IN CNAME reader.xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com.
reader.xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com. 4 IN A 10.xx.xx.xx

リーダー追加のまとめ

  • Static memberで指定すると新しいリーダーは自動では追加されない
  • Excluded memberで指定すると新しいリーダーが自動で追加される

これはCLIのドキュメントに書いてあるとおりなので、想定内です。

4. フェイルオーバーしてみる

いよいよ本題です。

readerのフェイルオーバー優先度を最も高くした上でフェイルオーバーを実行します。 writerがリーダーインスタンスになり、readerがライターインスタンスとなりました。

フェイルオーバーでwriterとreaderの役割が入れ替わった

このとき、カスタムエンドポイントの挙動はどうなっているのでしょうか。

Static members

コンソールで確認すると、ライターインスタンスに昇格したreaderがなくなり、かわりにリーダーインスタンスとなったwriterが追加されています。

カスタムエンドポイントのエンドポイントメンバーにはwriter(現リーダーインスタンス)だけがある

CLIで見ると、 StaticMembers はフェイルオーバー前と変わりません。

$ aws rds describe-db-cluster-endpoints --db-cluster-endpoint-identifier reader-static
{
    "DBClusterEndpoints": [
        {
            "DBClusterEndpointIdentifier": "reader-static",
            "DBClusterIdentifier": "mycluster",
            "DBClusterEndpointResourceIdentifier": "cluster-endpoint-XXXXXXXXXXXX",
            "Endpoint": "reader-static.cluster-custom-xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com",
            "Status": "available",
            "EndpointType": "CUSTOM",
            "CustomEndpointType": "READER",
            "StaticMembers": [
                "reader",
                "writer"
            ],
            "ExcludedMembers": [],
            "DBClusterEndpointArn": "arn:aws:rds:ap-northeast-1:000000000000:cluster-endpoint:reader-static"
        }
    ]
}

VPC内から名前解決すると、writer(現リーダーインスタンス)だけが返るようになっています。

$ dig +noall +ans reader-static.cluster-custom-xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com
reader-static.cluster-custom-xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com. 1 IN CNAME writer.xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com.
writer.xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com. 1 IN A 10.xx.xx.xx

Excluded members

コンソールで確認すると、ライターインスタンスに昇格したreaderがなくなり、かわりにリーダーインスタンスとなったwriterが追加されています。Staticと同じ挙動ですね。

カスタムエンドポイントのエンドポイントメンバーにはwriter(現リーダーインスタンス)だけがある

こちらもCLI上は変化なしです。

$ aws rds describe-db-cluster-endpoints --db-cluster-endpoint-identifier reader-excluded
{
    "DBClusterEndpoints": [
        {
            "DBClusterEndpointIdentifier": "reader-excluded",
            "DBClusterIdentifier": "mycluster",
            "DBClusterEndpointResourceIdentifier": "cluster-endpoint-XXXXXXXXXXXX",
            "Endpoint": "reader-excluded.cluster-custom-xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com",
            "Status": "available",
            "EndpointType": "CUSTOM",
            "CustomEndpointType": "READER",
            "StaticMembers": [],
            "ExcludedMembers": [
                "reader-for-batch"
            ],
            "DBClusterEndpointArn": "arn:aws:rds:ap-northeast-1:000000000000:cluster-endpoint:reader-excluded"
        }
    ]
}

VPC内から名前解決すると、writer(現リーダーインスタンス)とreader2が返ります。

$ dig +noall +ans reader-excluded.cluster-custom-xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com
reader-excluded.cluster-custom-xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com. 1 IN CNAME writer.xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com.
writer.xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com. 1 IN A 10.xx.xx.xx
$ dig +noall +ans reader-excluded.cluster-custom-xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com
reader-excluded.cluster-custom-xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com. 1 IN CNAME reader2.xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com.
reader2.xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com. 5 IN A 10.xx.xx.xx

フェイルオーバーまとめ

登録されたメンバーの中から、エンドポイントタイプに合うインスタンスにルーティングします。 そのため、ライターインスタンスをメンバーとして登録しておくと、フェイルオーバーでライターインスタンスがリーダーインスタンスになったとき、入れ替わりで有効なメンバーとなります。

期待通りの挙動をしてくれることが確認できました!

まとめ

  • エンドポイントタイプに一致しないインスタンスもメンバーとして登録できるが、ルーティングはされない
  • コンソールに表示されているのがルーティング対象のメンバー
  • エンドポイントタイプに一致しないインスタンスが、フェイルオーバーによってエンドポイントタイプに一致するようになると、ルーティングされるようになる(逆も然り)
  • StaticかExcludedかでフェイルオーバー時の挙動に違いはないため、「リーダーインスタンスを追加した際に自動でカスタムエンドポイントに追加されてほしいかどうか」で決めれば良い

エンドポイントタイプを指定することで動的にメンバーを調整してくれることはドキュメントに書いているので想定通りといえばそうなのですが、「カスタムエンドポイント作成のまとめ」でも述べた通り、そもそもエンドポイントタイプに合わないインスタンスを追加できるかが最も不安な点でした。 そのため、エンドポイントタイプに合わないインスタンスを追加できた時点でほぼ勝ちを確信していました。 実際にフェイルオーバー時に意図通りに動いてくれるのを確認できて良かったです。

とっても賢いカスタムエンドポイント、ぜひみなさんも使ってみてください。