概要
ロードバランサーの配下にあるサーバーが 1 台おかしくなっても、そのサーバーにリクエストを送らずに済むのは ヘルスチェック のおかげです。ロードバランサーは、ターゲットグループに登録したターゲットへ定期的に確認のリクエストを送り、合格したターゲットだけにトラフィックを流します。
ただし、何をもって「正常」とみなすかは、ロードバランサーの種類で変わります。Application Load Balancer(ALB) はレイヤー 7 で動き、HTTP / HTTPS で決めたパスにリクエストを送って、返ってきたステータスコードで判定します。Network Load Balancer(NLB) はレイヤー 4 で動き、既定は TCP の接続 ができるかどうかで判定します(HTTP / HTTPS のヘルスチェックも選べます)。そのため、アプリが 404 や 500 を返していても、ポートさえ開いていれば NLB の TCP ヘルスチェックは合格してしまいます。
この記事では、ALB と NLB のヘルスチェックの設定項目と既定値、ターゲットが unhealthy になったときの理由の見方を整理します。実践では、同じ 2 台の EC2 を ALB と NLB の両方に登録し、片方のヘルスチェック用ページを消したときに、それぞれのロードバランサーがどう判定するかを比べます。

クラウドおさる猿山の監視当番には、倉庫番に「今日の在庫は?」と聞いて答えの中身まで確かめる当番と、戸を叩いて返事があれば良しとする当番がいるのでござる。ALB と NLB のヘルスチェックも、この違いでござるな。
この記事のメリット
- ALB と NLB のヘルスチェックで、使えるプロトコルと「正常」の判定方法の違いを説明できるようになる
- パス・成功コード・しきい値・間隔・タイムアウトの意味と既定値が分かる
- ターゲットが
unhealthyになったとき、理由コードから原因を切り分けられるようになる - TCP ヘルスチェックではアプリの異常を見逃すことがある、という落とし穴を実機で確かめられる
執筆者とキャラクター紹介
執筆者:土肥(とひ)
株式会社ジェニュインの代表であり元エンジニア。これまでオンプレミスと AWS 環境のインフラ業務を担当。AWS 認定資格の学習で得た知識を、ハンズオンとともに解説するのが得意。直近では新人の教育も行っており、これから技術を学ぶ方が納得できる解説を心がけています。基礎編・Cloud Practitioner の記事では、解説役としても登場します。


クラウドおさる



はじめましてでござる。弊猿はクラウドおさる。IT インフラが異常に発達した猿山から、雲に乗って地上の AWS を学びに降りてきたのでござる。



基礎編や Cloud Practitioner の記事では、とひさんに素朴な疑問をぶつける役でござるな。Security - Specialty などの記事では、要所でワンポイントや注意点をひとこと添えるでござる。



猿山にもデータセンターや監視当番があって、地上で起きることはだいたい猿山にも同じ仕組みがあるのでござる。例え話がつい猿山に寄るのは、弊猿の癖でござる。
技術解説
ヘルスチェックはターゲットグループに設定する
ヘルスチェックの設定は、ロードバランサー本体ではなく ターゲットグループ に持たせます。ロードバランサーは、リスナー(またはリスナールール)から参照されているターゲットグループに登録されたターゲットへ、決めた間隔で確認を送ります。
- 連続して 非正常のしきい値 の回数だけ失敗すると
unhealthyになり、そのターゲットへのルーティングが止まる unhealthyのターゲットが連続して 正常のしきい値 の回数だけ合格するとhealthyに戻り、ルーティングが再開する- ターゲットグループがどのリスナーからも使われていないと、ヘルスチェックは行われない(状態は
unused、理由はTarget.NotInUse)
ALB と NLB の設定項目の違い
| 項目 | ALB | NLB |
|---|---|---|
| 動くレイヤー | レイヤー 7(HTTP / HTTPS) | レイヤー 4(TCP / UDP / TLS) |
| ヘルスチェックのプロトコル | HTTP / HTTPS(既定 HTTP) | TCP / HTTP / HTTPS(既定 TCP) |
| パス | 既定 / | HTTP / HTTPS のときだけ。既定 / |
| 成功コード | 200〜499 から指定。既定 200 | HTTP / HTTPS のときだけ。200〜599 から指定。既定 200-399 |
| 正常のしきい値 | 2〜10。既定 5 | 2〜10。既定 5 |
| 非正常のしきい値 | 2〜10。既定 2 | 2〜10。既定 2 |
| タイムアウト | 2〜120 秒。既定 5 秒(インスタンス / IP) | 2〜120 秒。既定 HTTP 6 秒、TCP / HTTPS 10 秒 |
| 間隔 | 5〜300 秒。既定 30 秒(インスタンス / IP) | 5〜300 秒。既定 30 秒 |
ALB のヘルスチェックに TCP はありません。逆に NLB の既定の TCP ヘルスチェックは、ターゲットのポートに接続できれば合格です。Web サーバーのプロセスが生きていてポートが開いていれば、アプリがエラーを返していても NLB は気づきません。アプリの中身まで見たいなら、NLB でもヘルスチェックのプロトコルを HTTP / HTTPS にして、パスを指定します。



ALB は「答えの中身」を、NLB の TCP は「戸を叩いて返事があるか」を見るのでござる。大量の通信を低遅延でさばく API 層には NLB、パスで振り分けたい Web 層には ALB、と役割で選び、確かめたい深さに合わせてヘルスチェックを決めるのでござるな。


unhealthy の原因は理由コードで見る
ターゲットグループの「ターゲット」タブには、ターゲットごとのヘルスステータスと、その理由(詳細)が表示されます。CLI の aws elbv2 describe-target-health でも同じ理由コードが返ります。Elb で始まるコードはロードバランサー側、Target で始まるコードはターゲット側の理由です。
| ヘルスステータス | 主な理由コード | 見るところ |
|---|---|---|
initial | Elb.RegistrationInProgress / Elb.InitialHealthChecking | 登録直後。最初のヘルスチェックに 1 回合格すれば healthy になる |
unhealthy | Target.ResponseCodeMismatch(ALB) | アプリが成功コード以外(404・500 など)を返している。パスと成功コードの設定も確認 |
unhealthy | Target.Timeout(ALB) | 応答がタイムアウトした。ターゲットのセキュリティグループ、アプリの応答時間 |
unhealthy | Target.FailedHealthChecks | ヘルスチェックに失敗した。NLB ではこのコードにまとまる |
unused | Target.NotInUse / Target.NotRegistered / Target.InvalidState | リスナーから使われていない、未登録、インスタンスが停止している |



ターゲットがすべて unhealthy になると、ロードバランサーは「フェイルオープン」して、全部のターゲットにリクエストを送るのでござる。全部倒れたから全部止まる、とはならぬので、ヘルスチェックの設定ミスで全滅していないかも気にしておくでござるぞ。
ヘルスチェックの通信は、ターゲットのセキュリティグループで許可しておく必要があります。ALB・NLB ともに、ロードバランサーのセキュリティグループをターゲット側のインバウンドルールの送信元に指定するのが確実です。NLB はセキュリティグループを付けずに作成すると後から関連付けられない点(作成時に付けておけば、あとで入れ替えられます)と、ヘルスチェックには NLB のアウトバウンドルールが適用される点に注意します。
実践
同じ 2 台の EC2(soa-hc-web-a / soa-hc-web-b)を、ALB 用と NLB 用のターゲットグループに登録します。どちらの EC2 も /health.html で ok を返します。途中で soa-hc-web-b の /health.html だけを消し、アプリの一部が壊れた状態を作ったときに、ALB(HTTP ヘルスチェック)と NLB(TCP ヘルスチェック)の判定がどう変わるかを比べます。
前提条件
- リージョン:
ap-northeast-1(東京) - 以下のリソースが作成済みであること
まだ用意していない場合は、「作り方の参考」の記事の手順で作れます。リソース名は、この記事の名前に読み替えてください。
| リソース種別 | リソース名 | 内容 | 作り方の参考 |
|---|---|---|---|
| EC2 インスタンス | soa-hc-web-a(ap-northeast-1a) | Apache で / に web-a、/health.html に ok を返す | Amazon EC2 でインスタンスを起動しSSH接続する |
| EC2 インスタンス | soa-hc-web-b(ap-northeast-1c) | 同上(/ は web-b) | 同上 |
| IAM ロール | soa-hc-ec2-role | 2 台の EC2 に付与。AmazonSSMManagedInstanceCore をアタッチ(Run Command 用) | AWS IAM でユーザー・ロール・ポリシーを作成しアクセス制御する |
| セキュリティグループ | soa-hc-web-sg | EC2 用。TCP 80 を soa-hc-alb-sg と soa-hc-nlb-sg から許可 | |
| Application Load Balancer | soa-hc-alb | インターネット向け、ap-northeast-1a / 1c、soa-hc-alb-sg(HTTP 80 を許可)。リスナーはまだ無い | |
| Network Load Balancer | soa-hc-nlb | インターネット向け、ap-northeast-1a / 1c、soa-hc-nlb-sg(TCP 80 を許可)。リスナーはまだ無い |
作成するリソース一覧
| リソース種別 | リソース名 | 用途 |
|---|---|---|
| ターゲットグループ | soa-hc-alb-tg | ALB 用。HTTP で /health.html を確認する |
| ターゲットグループ | soa-hc-nlb-tg | NLB 用。TCP の接続を確認する(後で HTTP に変える) |
| リスナー | soa-hc-alb の HTTP:80 | soa-hc-alb-tg へ転送 |
| リスナー | soa-hc-nlb の TCP:80 | soa-hc-nlb-tg へ転送 |
手順 1: ALB 用のターゲットグループを作る
AWS マネジメントコンソールにログインし、リージョンが ap-northeast-1(東京)であることを確認します。上部の検索バーに EC2 と入力し、「EC2」を選択します。
左側ナビゲーションの「ロードバランシング」→「ターゲットグループ」を選択し、「ターゲットグループの作成」をクリックします。以下を設定します。
- ターゲットタイプの選択: 「インスタンス」
- ターゲットグループ名:
soa-hc-alb-tg - プロトコル : ポート:
HTTP:80 - VPC:
soa-lab-vpc - ヘルスチェックプロトコル:
HTTP - ヘルスチェックパス:
/health.html
「ヘルスチェックの詳細設定」を開き、確認が早く進むように以下へ変更します。
- 正常のしきい値:
2 - 非正常のしきい値:
2 - タイムアウト:
5 - 間隔:
10 - 成功コード:
200
その他の項目はデフォルトのままにします。


「次へ」をクリックし、「ターゲットを登録」で soa-hc-web-a と soa-hc-web-b にチェックを入れ、「保留中として以下を含める」をクリックします。「ターゲットグループの作成」をクリックします。
手順 2: ALB にリスナーを追加する
左側ナビゲーションの「ロードバランサー」を選択し、soa-hc-alb を開きます。「リスナーとルール」タブで「リスナーの追加」をクリックし、以下を設定して「追加」をクリックします。
- プロトコル:
HTTP、ポート:80 - デフォルトアクション: 「ターゲットグループへ転送」→
soa-hc-alb-tg
リスナーから参照されたことで、soa-hc-alb-tg のヘルスチェックが始まります。ターゲットグループ soa-hc-alb-tg の「ターゲット」タブを開き、しばらく待って(画面右上の更新ボタンで再読み込み)2 台とも「ヘルスステータス」が Healthy になったことを確認します。


手順 3: NLB 用のターゲットグループとリスナーを作る
「ターゲットグループ」に戻り、「ターゲットグループの作成」をクリックします。以下を設定します。
- ターゲットタイプの選択: 「インスタンス」
- ターゲットグループ名:
soa-hc-nlb-tg - プロトコル : ポート:
TCP:80(ポートは自動では入らないので入力します) - VPC:
soa-lab-vpc - ヘルスチェックプロトコル:
TCP(プロトコルを TCP にしても、ここは HTTP のまま残ることがあるので選び直します)
「ヘルスチェックの詳細設定」を開き、以下へ変更します。TCP では、パスと成功コードの項目がありません。タイムアウトは既定の 10 秒のままにします。
- 正常のしきい値:
2 - 非正常のしきい値:
2 - 間隔:
10


「次へ」をクリックし、手順 1 と同じく 2 台を登録して「ターゲットグループの作成」をクリックします。
「ロードバランサー」から soa-hc-nlb を開き、「リスナー」タブで「リスナーの追加」をクリックします。プロトコル TCP、ポート 80、デフォルトアクションの転送先に soa-hc-nlb-tg を選んで「追加」をクリックします。soa-hc-nlb-tg の「ターゲット」タブで、2 台とも Healthy になるまで待ちます。
手順 4: web-b のヘルスチェック用ページを消す
soa-hc-web-b のアプリの一部が壊れた状態を作ります。上部のツールバーの CloudShell アイコンをクリックして AWS CloudShell を開き、次のコマンドで Run Command を実行します。soa-hc-web-b の /health.html だけが消え、/(トップページ)と Apache のプロセスはそのまま動き続けます。
aws ssm send-command \
--document-name AWS-RunShellScript \
--targets Key=tag:Name,Values=soa-hc-web-b \
--parameters 'commands=["rm -f /var/www/html/health.html"]' \
--query Command.CommandId --output text手順 5: ALB と NLB の判定を比べる
20〜30 秒ほど待ってから、soa-hc-alb-tg の「ターゲット」タブを再読み込みします。soa-hc-web-b の「ヘルスステータス」が Unhealthy になり、「ヘルスステータスの詳細」に Health checks failed with these codes: [404] のように、成功コード(200)以外が返ったことが表示されます。


次に soa-hc-nlb-tg の「ターゲット」タブを開きます。こちらは 2 台とも Healthy のままです。TCP ヘルスチェックはポート 80 に接続できるかだけを見ているので、/health.html が 404 を返していることには気づきません。


CloudShell で理由コードも確認しておきます。<ALB 用ターゲットグループの ARN> は、soa-hc-alb-tg の詳細に表示される ARN に置き換えます。
aws elbv2 describe-target-health \
--target-group-arn <ALB 用ターゲットグループの ARN> \
--query 'TargetHealthDescriptions[].[Target.Id,TargetHealth.State,TargetHealth.Reason]' \
--output tablesoa-hc-web-b のインスタンス ID の行に unhealthy と Target.ResponseCodeMismatch が表示されます。コンソールの表には詳細の文だけが出て、この理由コードは出ません。





戸を叩けば返事はあるので、NLB の当番は「元気でござる」と帳面に書いてしまうのでござるな。中身まで確かめる当番でないと、このずれは見つからぬのでござる。
手順 6: NLB のヘルスチェックを HTTP に変える
soa-hc-nlb-tg の「ヘルスチェック」タブで「編集」をクリックし、以下に変更して「変更内容の保存」をクリックします。
- ヘルスチェックプロトコル:
HTTP - ヘルスチェックパス:
/health.html - 「ヘルスチェックの詳細設定」を開き、成功コード:
200
成功コードは空のままだと「ヘルスチェック http コードマッチャーを空にすることはできません。」と表示されて保存できないので、必ず入力します。


ヘルスチェックの設定を変えると、ターゲットはいったん initial(初期のヘルスチェック中)に戻ります。3 分ほど待って「ターゲット」タブを再読み込みすると、NLB でも soa-hc-web-b が Unhealthy になります。NLB の理由は Target.FailedHealthChecks(ヘルスチェックに失敗)としてまとめて表示されます。



Web 層は ALB でパスと成功コード、大量のリクエストを低遅延でさばく層は NLB。どの層に何を使い、ヘルスチェックで何を確かめるかを組み合わせで問われると、TCP が使えるのはどちらかで迷いやすいのでござる。
まとめ
| 比べるところ | ALB | NLB |
|---|---|---|
| ヘルスチェックのプロトコル | HTTP / HTTPS(TCP は無い) | TCP(既定)/ HTTP / HTTPS |
| 「正常」の判定 | パスへのリクエストのステータスコードが成功コードに入るか | TCP は接続できるか。HTTP / HTTPS ならステータスコード |
| 成功コード | 200〜499 から指定(既定 200) | 200〜599 から指定(既定 200-399) |
| unhealthy の理由 | Target.ResponseCodeMismatch / Target.Timeout など | Target.FailedHealthChecks |
- ヘルスチェックはターゲットグループに設定する。リスナーから使われていないターゲットグループはチェックされない(
unused) - 非正常のしきい値の回数だけ続けて失敗すると
unhealthy、正常のしきい値の回数だけ続けて合格するとhealthyに戻る - NLB の TCP ヘルスチェックはポートが開いていれば合格する。アプリの異常まで検知したいなら HTTP / HTTPS にしてパスを指定する
- 全ターゲットが
unhealthyになるとフェイルオープンし、全ターゲットにトラフィックを送る - ターゲットのセキュリティグループは、ロードバランサーのセキュリティグループからのヘルスチェックを許可する
参照先
- Application Load Balancer ターゲットグループのヘルスチェック – Elastic Load Balancing
- Network Load Balancer ターゲットグループのヘルスチェック – Elastic Load Balancing
- Network Load Balancer のセキュリティグループを更新する – Elastic Load Balancing
- Elastic Load Balancing とは? – Elastic Load Balancing
- AWS Systems Manager Run Command – AWS Systems Manager













