【信頼性】AWS NLBのクロスゾーン負荷分散 有効 vs 無効 の動作確認

# 1. はじめに
- 仕事でNLBのクロスゾーン負荷分散の調査をする機会があり、超今更ながら「有効と無効どう違うの？」をスッキリ理解できるようにするため、実機動作検証を行うことにした。

# 2. NLBクロスゾーン負荷分散とは（自分の理解）
- 2018年にリリースされた機能。基本的な概念や使い分けについては、DevelopersIO記事「[Network Load Balancer（NLB）でクロスゾーン負荷分散が可能になりました](https://dev.classmethod.jp/articles/cross-zone-load-balancing-for-nlb/)」を参照。
- NLBはターゲット(リクエストの転送先となるインスタンスなど)をAZ-a, AZ-c の両方に持っている場合、NLB自身もAz-a, Az-c 両方のIPアドレスを持つ。
    - クロスゾーン負荷分散が無効(デフォルト)の場合、自身のAZ-a側のIPアドレスで受けたリクエストは、Az-a側のターゲットにのみ転送する。
    - クロスゾーン負荷分散が有効の場合、自身のAz-a側のIPアドレスで受けたリクエストは、Az-a側、Az-c側どちらかのターゲットに転送する。

# 3. やったこと
- NLB、インスタンス2台(Az-a, Az-cにnginxインスタンスそれぞれ1台ずつ)を作成し、インスタンス2台をNLBのターゲットとして登録する。
- クロスゾーン負荷分散が有効／無効の時、通常時にリクエストがどのように振り分けられるのか確認する。
- クロスゾーン負荷分散が有効／無効の時、片側のインスタンスの障害発生時にリクエストがどのように振り分けられるのか確認する。

# 4. 構成図

![crosszone04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189302/34e61589-d149-3acc-40dd-84fe19fbed54.png)

# 5. 手順
## 5.1 環境準備
上記構成図の環境を作成する。

### VPC
- 4つのサブネット(Az-a, Az-cにパブリックとプライベートを1つずつ)を持つVPCを作成する。
### WEBサーバ
- Az-a, Az-c のプライベートサブネットに1台ずつインスタンスを起動し、nginxをインストールする。
- nginxのindex.htmlの中身をそれぞれ"az-a"/"az-c"とし、NLB経由でWEBサーバにアクセスした際に、どちら側のサーバにアクセスしたのか分かるようにする。

```index.html
<!DOCTYPE html>
<html>
<body>
az-a
</body>
</html>
```

### NLB
- 2台のnginxインスタンスを登録したターゲットグループを作成する。

![crosszone01a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189302/922ef157-7b76-f7d7-180e-3ba595b9cb0b.png)

- リスナーとしてhttp、ターゲットとして上記のターゲットグループを持つInternal NLBを作成する。
![crosszone02a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189302/b371ef99-d2a4-5e9d-73d6-8e559e3a7c37.png)


- NLBの設定はクロスゾーン有効とする。
![crosszone03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189302/a42d81d6-5448-dfbc-20f2-c6760f044337.png)

### httpクライアント
- Az-aのパブリックサブネットにインスタンスを1台起動し、httpクライアント(curlを利用)とする。
- curlでNLBの名前を指定して、NLB経由でnginxへアクセスできることを確認する。

```
[ec2-user@ip-10-0-0-156 ~]$ curl -sS mksamba-nlb-crosszone-xxxxxxxxxxxxxxx.elb.us-west-1.amazonaws.com | grep az
az-a
```


## 5.2 クロスゾーン設定有効時の通常動作

![crosszone04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189302/34e61589-d149-3acc-40dd-84fe19fbed54.png)

- 今回の検証では、httpクライアントから、NLBの名前を指定してアクセスする。その際、httpクライアントとなるインスタンス内での名前解決に従い、NLBのaz-a側のIP、az-c側のIPにランダムにアクセスすることになる。(以下はNLBの名前解決の結果)
 
```
[ec2-user@ip-10-0-0-156 ~]$ dig mksamba-nlb-crosszone-xxxxxxxxxxxxxxxx.elb.us-west-1.amazonaws.com

; <<>> DiG 9.16.38-RH <<>> mksamba-nlb-crosszone-xxxxxxxxxxxxxxxx.elb.us-west-1.amazonaws.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 39083
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;mksamba-nlb-crosszone-xxxxxxxxxxxxxxxx.elb.us-west-1.amazonaws.com. IN A

;; ANSWER SECTION:
mksamba-nlb-crosszone-xxxxxxxxxxxxxxxx.elb.us-west-1.amazonaws.com. 37 IN A 10.0.11.20
mksamba-nlb-crosszone-xxxxxxxxxxxxxxxx.elb.us-west-1.amazonaws.com. 37 IN A 10.0.10.135

;; Query time: 0 msec
;; SERVER: 10.0.0.2#53(10.0.0.2)
;; WHEN: Fri Jun 23 05:05:18 UTC 2023
;; MSG SIZE  rcvd: 127
```

- クロスゾーン有効の場合、上図の通り、以下の4つの経路の可能性がある。
    - httpクライアント -> NLBのAz-a側IP(10.0.10.135) -> Az-a側のnginx
    - httpクライアント -> NLBのAz-a側IP(10.0.10.135) -> Az-c側のnginx
    - httpクライアント -> NLBのAz-c側IP(10.0.11.20) -> Az-a側のnginx
    - httpクライアント -> NLBのAz-c側IP(10.0.11.20) -> Az-c側のnginx

- 結果として、Az-a側/Az-c側のnginxにランダムに振り分けられる。

```
[ec2-user@ip-10-0-0-156 ~]$ curl -sS mksamba-nlb-crosszone-xxxxxxxxxxxxxxxx.elb.us-west-1.amazonaws.com | grep az
az-c
[ec2-user@ip-10-0-0-156 ~]$ curl -sS mksamba-nlb-crosszone-xxxxxxxxxxxxxxxx.elb.us-west-1.amazonaws.com | grep az
az-a
[ec2-user@ip-10-0-0-156 ~]$ curl -sS mksamba-nlb-crosszone-xxxxxxxxxxxxxxxx.elb.us-west-1.amazonaws.com | grep az
az-c
[ec2-user@ip-10-0-0-156 ~]$ curl -sS mksamba-nlb-crosszone-xxxxxxxxxxxxxxxx.elb.us-west-1.amazonaws.com | grep az
az-c
[ec2-user@ip-10-0-0-156 ~]$ curl -sS mksamba-nlb-crosszone-xxxxxxxxxxxxxxxx.elb.us-west-1.amazonaws.com | grep az
az-a
[ec2-user@ip-10-0-0-156 ~]$ curl -sS mksamba-nlb-crosszone-xxxxxxxxxxxxxxxx.elb.us-west-1.amazonaws.com | grep az
az-a
```

## 5.3 クロスゾーン設定有効時の障害時動作

![crosszone05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189302/73a3fdce-2085-4024-a1fe-d0a414abef03.png)

- Az-a側のnginxインスタンスに障害発生した場合、ターゲットグループに設定されたヘルスチェック(間隔10秒、3回応答NGであればunhealthyとみなす)に従い、NLBからの切り離しが行われる。そのため、障害発生⇒ヘルスチェック⇒切り離しが完了するまでの最大40秒の間、NLBのAz-a側にアクセスした場合はアクセス不可となる。
- 今回は、Az-a側nginxインスタンスのセキュリティグループを変更し、NLBからのhttpを遮断することで障害を模擬する。05:22:00 にNLBからAz-a側nginxへのアクセス遮断後、dateコマンドとcurlコマンドを手動で約1秒ごとに実行する(curlコマンドのタイムアウトが長く、スクリプト内での連続実行ができないため)。
- 結果としては想定通り、遮断から約40秒間は、半数のリクエスト(NLBのAz-a側にアクセスしたもの)が失敗するが、約40秒後には、全てのリクエスト(NLBのAz-a側にアクセスした場合も含む)がAz-c側に片寄せされ成功するようになる。

```
[ec2-user@ip-10-0-0-156 ~]$ ./curl-script-manual.sh
Thu Jun 15 05:22:05 UTC 2023
^C
[ec2-user@ip-10-0-0-156 ~]$ ./curl-script-manual.sh
Thu Jun 15 05:22:12 UTC 2023
az-c
[ec2-user@ip-10-0-0-156 ~]$ ./curl-script-manual.sh
Thu Jun 15 05:22:13 UTC 2023
^C
[ec2-user@ip-10-0-0-156 ~]$ ./curl-script-manual.sh
Thu Jun 15 05:22:16 UTC 2023
az-c
[ec2-user@ip-10-0-0-156 ~]$ ./curl-script-manual.sh
Thu Jun 15 05:22:18 UTC 2023
^C
[ec2-user@ip-10-0-0-156 ~]$ ./curl-script-manual.sh
Thu Jun 15 05:22:23 UTC 2023
az-c
[ec2-user@ip-10-0-0-156 ~]$ ./curl-script-manual.sh
Thu Jun 15 05:22:24 UTC 2023
^C
[ec2-user@ip-10-0-0-156 ~]$ ./curl-script-manual.sh
Thu Jun 15 05:22:31 UTC 2023
az-c
[ec2-user@ip-10-0-0-156 ~]$ ./curl-script-manual.sh
Thu Jun 15 05:22:32 UTC 2023
^C
[ec2-user@ip-10-0-0-156 ~]$ ./curl-script-manual.sh
Thu Jun 15 05:22:39 UTC 2023
az-c
[ec2-user@ip-10-0-0-156 ~]$ ./curl-script-manual.sh
Thu Jun 15 05:22:40 UTC 2023
^C
[ec2-user@ip-10-0-0-156 ~]$ ./curl-script-manual.sh
Thu Jun 15 05:22:47 UTC 2023
az-c
[ec2-user@ip-10-0-0-156 ~]$ ./curl-script-manual.sh
Thu Jun 15 05:22:48 UTC 2023
az-c
```

## 5.4 クロスゾーン設定無効時の通常動作

![crosszone06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189302/a1535867-ab7f-5bdf-81df-203d6f8a3fb3.png)

- クロスゾーン無効の場合、上図の通り、以下の2つの経路の可能性がある。
    - httpクライアント -> NLBのAz-a側IP(10.0.10.135) -> Az-a側のnginx
    - httpクライアント -> NLBのAz-c側IP(10.0.11.20) -> Az-c側のnginx

- 今回はhttpクライアントは、NLBに名前でアクセスしており、結果として、Az-a側/Az-c側のnginxにランダムに振り分けられる。(curl結果は省略)

## 5.5 クロスゾーン設定無効時の障害時動作

![crosszone07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189302/bf2948e2-93dd-c969-6130-07e368664d9e.png)

- クロスゾーン設定無効時、nginxインスタンスに障害が発生し、該当のAz内にhealthyなターゲットが一つもなくなってしまった場合、NLBの該当Az側のレコードが消失する。
- 今回の構成の場合、Az-a側のnginxへのアクセスを遮断すると、最大40秒でのヘルスチェック完了後、NLBのレコード更新が行われる(Az-a側のレコードがなくなる)。レコードのTTLは60秒のため、最大60秒後にhttpクライアント側でのレコード更新が完了する。障害発生後トータルで最大100秒間、httpクライアントでは半数のリクエスト(NLBのAz-a側にアクセスした場合)が失敗する。

- 04:17:00 にAz-a側nginxへのアクセスを遮断する。httpクライアントとなるインスタンスでのdigの1秒間隔の実行結果は以下の通り。障害発生時から約100秒後(ヘルスチェック40秒＋レコードのTTL60秒)に、Az-a側のレコードが消失している。

```
Thu Jun 15 04:18:46 UTC 2023
mksamba-nlb-crosszone-xxxxxxxxxxxxxxxx.elb.us-west-1.amazonaws.com. 2 IN A 10.0.11.20
mksamba-nlb-crosszone-xxxxxxxxxxxxxxxx.elb.us-west-1.amazonaws.com. 2 IN A 10.0.10.135
Thu Jun 15 04:18:47 UTC 2023
mksamba-nlb-crosszone-xxxxxxxxxxxxxxxx.elb.us-west-1.amazonaws.com. 1 IN A 10.0.10.135
mksamba-nlb-crosszone-xxxxxxxxxxxxxxxx.elb.us-west-1.amazonaws.com. 1 IN A 10.0.11.20
Thu Jun 15 04:18:48 UTC 2023
mksamba-nlb-crosszone-xxxxxxxxxxxxxxxx.elb.us-west-1.amazonaws.com. 0 IN A 10.0.11.20
mksamba-nlb-crosszone-xxxxxxxxxxxxxxxx.elb.us-west-1.amazonaws.com. 0 IN A 10.0.10.135
Thu Jun 15 04:18:49 UTC 2023
mksamba-nlb-crosszone-xxxxxxxxxxxxxxxx.elb.us-west-1.amazonaws.com. 60 IN A 10.0.11.20
Thu Jun 15 04:18:50 UTC 2023
mksamba-nlb-crosszone-xxxxxxxxxxxxxxxx.elb.us-west-1.amazonaws.com. 59 IN A 10.0.11.20
Thu Jun 15 04:18:51 UTC 2023
mksamba-nlb-crosszone-xxxxxxxxxxxxxxxx.elb.us-west-1.amazonaws.com. 58 IN A 10.0.11.20
```

- 結果として、約100秒間は半数のhttpリクエストが失敗し、100秒後以降は全てのhttpリクエストがAz-c側に片寄せされ成功するようになった。(curlの結果は省略)

# 6. 所感

- アクセス経路のパターンの確認、障害発生時の完全片寄せまでの所要時間の差異といった、基本的な内容を理解することができた。