AWS ELB(ALB)のヘルスチェックを調べてみる

## はじめに
AWSのApplication Load Balancer（以下ALB）は、様々なシーンで利用する機会が多いサービスです。
ALBを構築する際、「ヘルスチェック」で躓く方も多いかと思い、基本的な内容になりますがALBのヘルスチェックに着目して確認してみたいと思います。

## 事前準備

### 構成情報

以下に示す構成を用意します。
- 東京リージョンで2AZを利用。
- VPCに計3つのサブネット（パブリックサブネット2つ、プライベートサブネット1つ）を作成。
- パブリックサブネットには、ALBとNatゲートウェイ*1を構築。
- プライベートサブネットには、EC2（Windows）を1台構築。
*1：NATゲートウェイは、Systems ManagerやWindows Update等で利用する用途。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3490859/ce4400a9-e032-381a-4caf-ab4ee5709c6b.png)

EC2（Windows）は、以下の用意をしておきます。
- AMIは、Windows_Server-2022-Japanese-Full-Base-2023.11.15（ami-0ccf1fea52470bfb0）
- WindowsインスタンスなのでAdministratorのパスワードを複合化
- Systems Managerで必要なIAMロールを作成しアタッチ
- Windowsの[役割と機能の追加]より、Webサーバーとして使うIISをインストール

### （参考）AWS利用料金
どのくらいお金かかるのか、参考情報を記載します。
詳細な条件は割愛しますが、ALBとNATゲートウェイに加えEC2(Windows,t3.large,gp3 50GB)を常時稼働させた場合、176.31 USD/月が目安となります。
以下は参考にリンクを張ります。
なお、[パブリック IPv4 アドレスの新しい料金体系](https://aws.amazon.com/jp/blogs/news/new-aws-public-ipv4-address-charge-public-ip-insights/)の件がありますので、先々はさらにかかりますね。
利用しないリソースは削除するのが良いかと思います。

https://calculator.aws/#/estimate?id=89f89e8e26a61126394ae5db7094871f5895ae43

なお、本検証で実際にかかったコストは、1.2 USD程度でした。お安いですね。


### この記事を書く上で考慮した点
Windowsインスタンスで動作させるIISのログ出力設定を変更します。
IISマネージャーを起動し、ログ記録を選択すると以下のような画面が表示されます。
ログの出力先は、ディレクトリに記載のパスとなります。
ログへ出力する情報は、ログファイルのフィールドの選択ボタンを押下することで変更可能になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3490859/7deeb7f3-85bf-218d-9765-54d086a4de6b.png)

IISの初期構築状態ですと、ヘルスチェックのリクエストに含まれるヘッダー情報をすべて出力できません。
そのため、カスタムフィールドにログフィールドを追加します。
追加項目は、[AWS re:Post](https://repost.aws/ja/knowledge-center/elb-fix-failing-health-checks-alb)に記載のヘッダーと、ALBといえば定番のX-FORWARDED-FORを追加してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3490859/78ba7284-fb07-289c-098c-637ef0293469.png)


## ヘルスチェックのヘッダー情報確認
ALBのヘルスチェックが成功していることを確認した後に、IISのログを見てみます。
IISのログを以下に転載します。
ALB(10.0.0.228,10.0.1.88）から交互にEC2（10.0.8.73）にヘルスチェックが送信されていることが確認できます。
```ruby:IISのログ(ALBヘルスチェック)
#Fields: date time s-ip cs-method cs-uri-stem cs-uri-query s-port cs-username c-ip cs(User-Agent) cs(Referer) sc-status sc-substatus sc-win32-status time-taken Host Connection User-Agent Accept-Encoding X-FORWARDED-FOR
2023-11-26 03:57:41 10.0.8.73 GET / - 80 - 10.0.1.88 ELB-HealthChecker/2.0 - 200 0 0 2 10.0.8.73 close ELB-HealthChecker/2.0 gzip,+compressed -
2023-11-26 03:57:49 10.0.8.73 GET / - 80 - 10.0.0.228 ELB-HealthChecker/2.0 - 200 0 0 0 10.0.8.73 close ELB-HealthChecker/2.0 gzip,+compressed -
2023-11-26 03:58:11 10.0.8.73 GET / - 80 - 10.0.1.88 ELB-HealthChecker/2.0 - 200 0 0 1 10.0.8.73 close ELB-HealthChecker/2.0 gzip,+compressed -
2023-11-26 03:58:19 10.0.8.73 GET / - 80 - 10.0.0.228 ELB-HealthChecker/2.0 - 200 0 0 0 10.0.8.73 close ELB-HealthChecker/2.0 gzip,+compressed -
```
またヘルスチェックのヘッダー情報ですが想定通りの結果となります。
[AWS re:Post](https://repost.aws/ja/knowledge-center/elb-fix-failing-health-checks-alb)で公開されているナレッジと同じですね。
また、X-FORWARDED-FORについても予想通り空でした。

| ヘッダー        | リクエストの値         |
|:----------------|:----------------------|
| Host            | 10.0.8.73             | 
| Connection      | close                 |
| User-Agent      | ELB-HealthChecker/2.0 | 
| Accept-Encoding | gzip,+compressed      | 
| X-FORWARDED-FOR | （空）                |

次に通常のリクエスト（インターネット経由でALBへ接続）のログを転載します。
HostヘッダーはXXX-alb-YYYYYYYYY.ap-northeast-1.elb.amazonaws.com、X-FORWARDED-FORはクライアントのグローバルIPアドレス（ZZZ.ZZZ.ZZZ.ZZZ）となり、これも想定通りの結果となります。

```ruby:IISのログ(インターネット経由でALBへリクエスト)
#Fields: date time s-ip cs-method cs-uri-stem cs-uri-query s-port cs-username c-ip cs(User-Agent) cs(Referer) sc-status sc-substatus sc-win32-status time-taken Host Connection User-Agent Accept-Encoding X-FORWARDED-FOR

2023-11-26 04:27:58 10.0.8.73 GET / - 80 - 10.0.1.88 Mozilla/5.0+(Windows+NT+10.0;+Win64;+x64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/119.0.0.0+Safari/537.36 - 304 0 0 2 XXX-alb-YYYYYYYYY.ap-northeast-1.elb.amazonaws.com - Mozilla/5.0+(Windows+NT+10.0;+Win64;+x64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/119.0.0.0+Safari/537.36 gzip,+deflate ZZZ.ZZZ.ZZZ.ZZZ

```
## ALBのヘルスチェックを失敗させてみる

通常のALB経由のリクエストと、ヘルスチェックのリクエストでHostヘッダーに格納される情報が異なることがわかりました。
今回はWebサーバー側の機能でヘルスチェックを失敗させるために、IISへ簡単な設定を加えます。IISで利用可能な要求フィルターという機能があります。例えば、ヘッダー(Host)に文字列（EC2のプライベートIPアドレス）を指定することが可能です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3490859/509e74c5-ea3b-d47b-f8de-4536b50f506d.png)

要求フィルターを設定すると、IISは正常に動いているにも関わらず、ALBヘルスチェックへの応答に404を返送させることが可能です。

```ruby:IISのログ(ALBヘルスチェック-NG)
#Fields: date time s-ip cs-method cs-uri-stem cs-uri-query s-port cs-username c-ip cs(User-Agent) cs(Referer) sc-status sc-substatus sc-win32-status time-taken Host Connection User-Agent Accept-Encoding X-FORWARDED-FOR

2023-11-26 05:10:44 10.0.8.73 GET / - 80 - 10.0.1.88 ELB-HealthChecker/2.0 - 404 19 0 2 10.0.8.73 close ELB-HealthChecker/2.0 gzip,+compressed -
2023-11-26 05:10:52 10.0.8.73 GET / - 80 - 10.0.0.228 ELB-HealthChecker/2.0 - 404 19 0 0 10.0.8.73 close ELB-HealthChecker/2.0 gzip,+compressed -

```

AWSマネジメントコンソールからも、ヘルスチェック失敗とIISが返送している404応答が確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3490859/9abf06b6-6d50-e1f2-4aa2-32bbe09fca0b.png)

## まとめ
ALBのヘルスチェックについて、理解は深まりましたでしょうか？
ヘルスチェックは通ってしまえば意識することも少ないので、これを機会に改めて私自身も理解が深まりました。
また、IPリーチャブルであるにも関わらず、ヘルスチェックが通らない（もしくはリクエストが通らない）ことがあれば、今回記載した情報も参考に切り分け頂ければと思います。