Amazon Aurora カスタムエンドポイントの検証と考察

#  1. はじめに
* [2018年11月12日のリリース](https://aws.amazon.com/jp/about-aws/whats-new/2018/11/amazon-aurora-simplifies-workload-management-with-custom-endpoints/)時から気になっていた **Amazon Aurora カスタムエンドポイント** をようやく試すきっかけができたので、いろいろ検証してみた結果と考察の備忘録。
* 定時バッチの重い参照系クエリによる長時間DB負荷を、カスタムエンドポイントを使えばサービス用DBから分離できるのか、というのが今回の検証の目的。


# 2. 使うべきかどうかの最重要ポイント
* [リリース内容](https://aws.amazon.com/jp/about-aws/whats-new/2018/11/amazon-aurora-simplifies-workload-management-with-custom-endpoints/)はいい感じに書いてあるけど、実際は「目的」「コスト」「スケールアウト/イン」「フェイルオーバー」といった要素を総合的に考慮して、利用すべきかどうかを検討する必要がある。

* 特に注意すべきポイント

    >編集アクションによる変更の進行中は、カスタムエンドポイントへの接続やカスタムエンドポイントの使用はできません。エンドポイントのステータスが [使用可能] に戻り、再度接続できるようになるまでに数分かかることがあります。
(引用元: https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/Aurora.Overview.Endpoints.html#aurora-endpoint-editing)

    これの挙動を確認したところ、<font color="red">カスタムエンドポイントの設定変更進行中は、そのカスタムエンドポイントを名前解決してもメンバーのインスタンス情報が返ってこなくなる状態が数分間あった。</font>

    つまり、カスタムエンドポイントに新しくレプリカを追加する、あるい、カスタムエンドポイントからレプリカを削除するなどで、カスタムエンドポイント設定に何からの編集を行うと、変更進行中の数分間、そのカスタムエンドポイントのメンバーインスタンスにDB接続ができなくなる。そのカスタムエンドポイントが集計や分析などのバッチ用途ならまだ許容できるが、サービス用の読み込み利用であれば致命的だ。

* サービスへ影響を与えることなくカスタムエンドポイントのメンバーを変更する方法はあるが、手順が増えるため対応スピードは落ちる。
 * レプリカを追加する場合の手順
     * 「クラスター内に新たに作成されたインスタンスをクラスターエンドポイントに自動的に追加するかどうかを設定」を無効
     * レプリカ作成
     * 新しいメンバー構成を設定したカスタムエンドポイントを新規に作成
     * プログラム側でエンドポイントを入れ替える
<p />
 * レプリカを削除する場合の手順
     * 新しいメンバー構成を設定したカスタムエンドポイントを新規に作成
     * プログラム側でエンドポイントを入れ替える
     * レプリカ削除

# 3. バッチ用 replicaインスタンス 導入検証
* いくつかのAuroraクラスター構成を想定して、実際に設定を行い挙動を確認する。
* カスタムエンドポイントの作成や設定変更は terraform を使う。
 * AWSマネジメントコンソールからカスタムエンドポイントを作成した場合は、カスタムエンドポイントのタイプが必ず「ANY」になってしまう。
 * terraform は、カスタムエンドポイントのタイプを「READER」に設定することができる。


## 3.1. 検証１

**元の構成**

* １つの primary インスタンス「**test-nishimura-01**」で「サービス用書き込み」と「サービス用読み込み」を行う構成。
* コスト重視で冗長性はない。
* 図

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/d9e03633-9a5b-f065-6145-b848ced9d39e.png)

**バッチ用 replica インスタンスを追加した構成**

* バッチ用 replica インスタンス「**test-nishimura-10**」を作成する。
 * フェイルオーバー優先順位を primary インスタンスと同じにする（ここでは "1" にしている)。
<p />
* 「バッチ用読み取り」のカスタムエンドポイント「**batch-reader**」を作成する。
 * タイプは「READER」
 * スタティックメンバーは「test-nishimura-01」と「test-nishimura-10」
<p />
* <font color="red">カスタムエンドポイントのタイプについて</font>
 * スタティックメンバーに primary の「test-nishimura-01」が設定されているが、タイプが「READER」なのでカスタムエンドポイントの名前解決で返されるメンバーは 「test-nishimura-10」だけになる。
 * タイプが「READER」ではなく「ANY」の場合は、でカスタムエンドポイントの名前解決でprimaryインスタンスの「test-nishimura-01」も引けるようになる。
<p />
*  「サービス用読み取り」の接続設定を reader endpoint から cluster endpoint へ変更する。
<p />
* 図

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/a2e3c88f-2d1d-0271-56a9-ea8602cb3391.png)

**primary インスタンス「test-nishimura-01」をフェイルオーバーする**

* 「test-nishimura-01」と「test-nishimura-10」のフェイルオーバー優先順位が同じ "1" なので、replica だった 「test-nishimura-10」 が昇格して primary になり、「test-nishimura-01」が replica となった。
<p />
* cluster endpoint のインスタンスが 「test-nishimura-01」から「test-nishimura-10」へ変わった。
<p />
* custom endpoint「batch-reader」のインスタンスが 「test-nishimura-10」から「test-nishimura-01」へ変わった。
 * <font color="red">カスタムエンドポイント「batch-reader」の指すメンバーが変更されたが、これはカスタムエンドポイントの設定を編集したわけではなく、既存設定通りの動きなので、この変更によりカスタムエンドポインが一時的に名前解決ができなくなるわけではなく、即座に切り替わる。</font>
<p />
* 「test-nishimura-01」と「test-nishimura-10」インスタンスの役割が入れ替わった。
<p />
* 図

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/285c5838-ea90-2703-c22f-affcde103d94.png)

**考察**

* フェイルオーバー後も、サービス用とバッチ用でインスタンスを分離することができる。
* <font color="red">フェイルオーバーによりインスタンスの役割が入れ替わった</font>ので、サービス用とバッチ用のインスタンスクラスは同じにする必要がある。
 * 追加した replica インスタンスは、バッチ用に加えてサービス用のフェイルオーバーとしても機能するので、クラスターの冗長性を確保できるようになった。


## 3.1. 検証２

**元の構成**

* １つの primary インスタンス「**test-nishimura-01**」で「サービス用書き込み」を、１つの replica インスタンス「**test-nishimura-02**」で「サービス用読み込み」を行う構成。
<p />
* フェイルオーバー優先順位を同じにする（ここでは "1" にしている)。
<p />
* 図

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/c0f64b7b-264c-efd8-8a84-99bb22430c68.png)

**バッチ用 replica インスタンスを追加した構成**

* バッチ用 replica インスタンス「**test-nishimura-10**」を作成する。
 * フェイルオーバー優先順位をサービス用インスタンス群より低い数値にする（ここでは "10" にしている)。
<p />
* 「バッチ用読み取り」のカスタムエンドポイント「**batch-reader**」を作成する。
 * タイプは「READER」
 * スタティックメンバーは「test-nishimura-10」
<p />
* 「サービス用読み取り」のカスタムエンドポイント「**serv-reader**」を作成する。
 * タイプは「READER」
 * スタティックメンバーは「test-nishimura-01」と「test-nishimura-02」
<p />
*  「サービス用読み取り」の接続設定を reader endpoint から costum endpoint 「**serv-reader**」へ変更する。
<p />
* 図

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/d96bc14f-842a-a449-6108-c426fb9ab067.png)

**primary インスタンス「test-nishimura-01」をフェイルオーバーする**

* 「test-nishimura-01」と「test-nishimura-02」のフェイルオーバー優先順位が同じ "1" なので、replica だった 「test-nishimura-02」 が昇格して primary になり、「test-nishimura-01」が replica となった。
<p />
* cluster endpoint のインスタンスが 「test-nishimura-01」から「test-nishimura-02」へ変わった。
<p />
* custom endpoint「serv-reader」のインスタンスが 「test-nishimura-02」から「test-nishimura-01」へ変わった。
<p />
* バッチ用のカスタムインスタンスや replica インスタンスには特に影響はない。
* 図

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/f1acba2d-48c5-f765-08ca-a87a1f5b3bde.png)

**考察**

* フェイルオーバー後も、サービス用インスタンス と バッチ用インスタンス を完全に分離できる。
<p />
* <font color="red">フェイルオーバー後も、インスタンスの役割は入れ替わらない</font> (test-nishimura-10 はバッチ用読み取りのまま)。
<p />
* バッチ用 replica インスタンスのクラスをサービス用インスタンスと同じにしなくても良いので、バッチ処理の完了を時間かけてもよいのであれば、低いスペックのクラスでもよい。
<p />
* バッチの時間帯だけバッチ用 replica インスタンスを作成して、バッチ処理が完了したら replica インスタンスを削除してもよい。
 * ただし、都度カスタムエンドポイントも作成・削除する必要がある。カスタムエンドポイントの設定だけ残しておいて、スタティックメンバーで設定してある同じ識別子で replica インスタンスを作成しても、カスタムエンドポイントは新しいインスタンスを見つけられないため。



## 3.3. 検証３

**元の構成**

* １つの primary インスタンス「**test-nishimura-01**」で「サービス用書き込み」を、２つの replica インスタンス「**test-nishimura-02**」と「**test-nishimura-03**」で「サービス用読み込み」を行う構成。
<p />
* フェイルオーバー優先順位を同じにする（ここでは "1" にしている)。
<p />
* 図
 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/8363b587-0d5f-708b-cb18-f1cd68c7ef64.png)

**バッチ用 replica インスタンスを追加した構成**

* 設定ポイントは、検証２ と同じ
* 図

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/37b07964-1a79-d95e-8bf9-6f5535773748.png)

**primary インスタンス「test-nishimura-01」をフェイルオーバーする**

* 挙動は 検証2 と同じ
* 図

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/561a2345-8c93-afff-332e-13308283b9d7.png)

**考察**

* 検証2 と同じ


# 4. カスタムエンドポイントにメンバーを追加する検証

**元の構成**

* １つの primary インスタンス「test-nishimura-01」で「サービス用書き込み」を、<br />１つの replica インスタンス「test-nishimura-02」で「サービス用読み込み」を、<br />１つの replica インスタンス「test-nishimura-10」でバッチ用読み込みを行う構成<br /> (検証２ のバッチ用 replica インスタンスを追加した構成と同じ)。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/b6fe6874-28d5-3407-55bb-2fa50a5592f9.png)

**元の構成に サービス用読み取り replica を追加する**

* まずは、サービス用読み取り replica「test-nishimura-03」を新規作成する (terraformで)。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/a30e739a-fcc7-9703-a962-9b8a051aeb8c.png)
 
* 新しい replica 「test-nishimura-03」は、reader endpoint に自動的に追加されるが、custom endpoint「serv-reader」には追加されない

* カスタムエンドポイント「serv-reader」の static_members 設定に 「test-nishimura-03」を追加する (terraformで)。

```terraform:カスタムエンドポイントserv-readerの設定部分を抜粋
resource "aws_rds_cluster_endpoint" "serv-reader" {
  cluster_endpoint_identifier = "serv-reader"
  cluster_identifier          = "test-nishimura"
  custom_endpoint_type        = "READER"
  #static_members              = ["test-nishimura-01", "test-nishimura-02"]
  static_members              = ["test-nishimura-01", "test-nishimura-02", "test-nishimura-03"]
}
```

* この時、各エンドポイントに対して１秒間隔で名前引きするスクリプトを動かしておく。

```sh:１秒間隔でカスタムエンドポイント「serv-reader」の名前引きするスクリプトの例
#!/bin/bash
LANG=C
for i in {1..2000}
do
  date
  dig +short serv-reader.cluster-custom-※※※※※※※※※※※※.ap-northeast-1.rds.amazonaws.com
  sleep 1
  echo
done
```

* 結果
 * 11:03:24 ころに terraform plan を実施 (カスタムエンドポイント変更を開始)
<p />
 * cluster endpoint, reader endpoint, batch-reader endoint の名前引きは途切れることなく返ってきた
<p />
 * カスタムエンドポイント「serv-reader」 の名前引きは、<font color="red">11:05:03 くらいから途切れ、11:06:22 くらいから徐々に返ってきて、設定変更が完了した 11:07:00 以降は途切れることなく返ってきた</font>。

```text:１秒間隔でカスタムエンドポイント「serv-reader」の名前引きするスクリプトの実行結果を部分抜粋
$ sh dig-serv-reader-ep.sh
Sun Nov  8 11:03:24 JST 2020
test-nishimura-02.※※※※※※※※※※※※.ap-northeast-1.rds.amazonaws.com.
172.31.11.88

Sun Nov  8 11:03:25 JST 2020
test-nishimura-02.※※※※※※※※※※※※.ap-northeast-1.rds.amazonaws.com.
172.31.11.88

〜snip〜

Sun Nov  8 11:05:02 JST 2020
test-nishimura-02.※※※※※※※※※※※※.ap-northeast-1.rds.amazonaws.com.
172.31.11.88

Sun Nov  8 11:05:03 JST 2020

Sun Nov  8 11:05:04 JST 2020

〜snip〜

Sun Nov  8 11:06:21 JST 2020

Sun Nov  8 11:06:22 JST 2020
test-nishimura-02.※※※※※※※※※※※※.ap-northeast-1.rds.amazonaws.com.
172.31.11.88

Sun Nov  8 11:06:23 JST 2020

Sun Nov  8 11:06:24 JST 2020
test-nishimura-03.※※※※※※※※※※※※.ap-northeast-1.rds.amazonaws.com.
172.31.12.227

Sun Nov  8 11:06:25 JST 2020

Sun Nov  8 11:06:26 JST 2020

〜snip〜

Sun Nov  8 11:06:58 JST 2020

Sun Nov  8 11:06:59 JST 2020

Sun Nov  8 11:07:00 JST 2020
test-nishimura-02.※※※※※※※※※※※※.ap-northeast-1.rds.amazonaws.com.
172.31.11.88

Sun Nov  8 11:07:01 JST 2020
test-nishimura-02.※※※※※※※※※※※※.ap-northeast-1.rds.amazonaws.com.
172.31.11.88

Sun Nov  8 11:07:02 JST 2020
test-nishimura-03.※※※※※※※※※※※※.ap-northeast-1.rds.amazonaws.com.
172.31.12.227

Sun Nov  8 11:07:04 JST 2020
test-nishimura-03.※※※※※※※※※※※※.ap-northeast-1.rds.amazonaws.com.
172.31.12.227
```

**変更後の構成**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/76ff693f-31b8-c9b7-e543-b1d359ff8ef5.png)

**考察**

* やはり、カスタムエンドポイントの設定変更中は、数分間そのカスタムエンドポイントは使用できなくなる。
<p />
* これが許容範囲かどうかはそのサービスの特性や求められる信頼性によるので、それらを事前に把握・確認した上で、サービス用読み取りにカスタムエンドポイントを使うかどうかを判断する必要がある。
<p />
* 例えば、24/365リクエストや負荷が一定数のサービスであれば、サーバ増減はインスタンスの障害時くらいしか対応することはないので、カスタムエンドポイントは有効な手法となる。
<p />
* 例えば、平常時の何倍もリクエストが増える定時イベントを開催しているサービスにおいて、イベント前に事前にレプリカを増やしてそれらをメンバーにしたカスタムエンドポイントを新規に作成してイベントが始まる直前にプログラム側で接続先エンドポイントを切り替える方法を採用した場合、想定以上にリクエストが来てレプリカが足りなくなった時は、同じ手順では迅速な対応ができなくなる。
<p />
* 例えば、リクエストや負荷のタイミングは不特定で急にレプリカをスケールアウトしなければならないようなサービスの場合、Amazon Aurora Auto Scaling を使用して現在稼働中のレプリカインスタンスに関するスケーリングポリシー設定して増減させるのが一般的だけど、カスタムエンドポイントには auto scaling で起動された レプリカ を自動的にメンバーにすることができない。


# 5. 個人的着地点

**その１**
１つの primary インスタンスで「サービス用書き込み」と「サービス用読み込み」を行っている構成に、バッチ用 replica インスタンスを追加したい場合

* もともとカスタムエンドポイントの利用を検討したきっかけは、検証１ のケースが発生したから。
* このケースの場合は、検証1 の「バッチ用 replica インスタンスを追加した構成」で良いと思う。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/a2e3c88f-2d1d-0271-56a9-ea8602cb3391.png)

**その２**
サービス用レプリカを頻繁に即座にスケールイン・アウトしたい場合

* サービス用読み取りのカスタムエンドポイントは作成せずに、reader endpoint を使う。
* バッチ用読み取りのカスタムエンドポイントを作成する。
 * そのインスタンスのクラスを他のサービス用インスタンスよりも高めにする（サービス用読み取りクエリも受けるため)。
 * そのインスタンスのフェイルオーバー優先順を他のサービス用インスタンスよりも低めにする(primaryのフェイルオーバーによって昇格させないため)。
* 必要に応じて、 Amazon Aurora Auto Scaling でレプリカをスケールアウト/インする設定をいれる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/62a7c71d-2f3b-7094-f583-484ae9437a57.png)
# 6. その他気になったこと

**AWSマネジメントコンソール**

* AWSマネジメントコンソールからカスタムエンドポイントを作成した場合は、カスタムエンドポイントのタイプが必ず「ANY」になってしまう。タイプを指定できる設定項目が存在しない。何でだろう・・
* 「今後追加されるインスタンスをこのクラスターにアタッチする」の利用目的がわからない・・
* カスタムエンポイントを作成するときは、タイプを「READER」にしたいとので、現時点でAWSマネジメントコンソールからカスタムエンドポイントを作成することはないな・・

![20201108-01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/9a979c93-402e-5890-30fb-35d81471da82.png)

**terraform**

* [aws_rds_cluster_endpoin](thttps://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/rds_cluster_endpoint)リソースでカスタムエンドポイントの作成・削除・編集ができる。
* AWSマネジメントコンソール設定できる「今後追加されるインスタンスをこのクラスターにアタッチする」の設定は、terraform ではできない。

# 7. 今後の期待

* カスタムエンドポイント設定変更中も、クラスターやリーダーエンドポイント同様に、名前解決が途切れることなく切り変わってほしい。