ELBのヘルスチェックは、作成時の既定値のまま本番に出しても動いてしまうぶん、障害が起きてから設定の意味を調べ直すことになりがちな機能です。ALBは成功コードの既定が200だけなので、トップページがリダイレクトを返すアプリでは登録直後からunhealthyになります。NLBは同じ項目名でも既定値が違い、成功コードは200-399、タイムアウトはプロトコルで変わる仕様です。この記事では、ターゲットグループのヘルスチェック設定をALBとNLBで比較し、AWS CLIでの変更、unhealthyの理由コードからの切り分け、S3へ出すヘルスチェックログ、Auto ScalingとECSの入れ替え連動までを公式ドキュメントに沿って整理します。LivenessとReadinessの分け方といった概念の整理はヘルスチェックとは?LivenessとReadinessの分離と閾値設計を実装目線で解説に任せ、本記事はELBの設定と調査の手順に絞ります。
まとめ|ELBヘルスチェックで誤検知と見逃しを防ぐため先に決める5項目
先に結論です。ELBのヘルスチェックを本番に出す前に決めておきたいのは次の5点になります。
- 専用のパス(例:
/healthz)を用意し、リダイレクトや認証を通らずに200を返すようにする - 間隔と閾値から「異常を検知するまで」と「復帰するまで」の秒数を計算し、許容できる値に詰める
- unhealthyになったら、まず
describe-target-healthの理由コードで原因の側(ELBかターゲットか)を分ける - ALBではヘルスチェックログをS3へ出しておき、失敗した時刻と理由を後から追えるようにする
- Auto ScalingやECSと組み合わせるなら猶予期間を設定し、起動中のサーバーが入れ替えで消されないようにする
ヘルスチェックの判定先にデータベースなどの依存先を含めるかどうかは、全台が同時に落ちる危険と引き換えになります。判断の基準は最終章で示します。
ALB・NLB・CLBで異なるヘルスチェックの既定値と正常判定の仕組み
ELBには種類が4つありますが、ヘルスチェックを持つのはALB・NLB・CLBです。種類ごとの用途の違いはElastic Load Balancingとは?ELBとALBの違い・4種類の使い分けと料金・設定を解説で比べているので、ここではヘルスチェックの設定値に絞ります。
ALBとNLBのターゲットグループで既定値が違う7項目の比較表
ALBとNLBは、どちらもターゲットグループ単位でヘルスチェックを設定します。項目名は共通でも、既定値はALBのヘルスチェック設定とNLBのヘルスチェック設定で次のように違います(2026年10月時点)。
| 項目(API名) | ALBの既定値と範囲 | NLBの既定値と範囲 |
|---|---|---|
| HealthCheckProtocol | HTTP(HTTP・HTTPS) | TCP(TCP・HTTP・HTTPS) |
| HealthCheckPath | / | /(HTTP・HTTPSのみ) |
| HealthCheckTimeoutSeconds | 5秒(2〜120秒) | HTTPは6秒、TCP・HTTPSは10秒 |
| 判定間隔(秒) | 30秒(5〜300秒) | 30秒(5〜300秒) |
| HealthyThresholdCount | 5回(2〜10回) | 5回(2〜10回) |
| UnhealthyThresholdCount | 2回(2〜10回) | 2回(2〜10回) |
| Matcher(成功コード) | 200(200〜499で指定) | 200-399(200〜599で指定) |
表の「判定間隔(秒)」に対応するAPI名はHealthCheckIntervalSecondsです。移行で事故が起きやすいのは成功コードです。NLBで301を返すパスをそのままALBへ移すと、ALBは200以外を失敗と数えるのでunhealthyになります。ポートの既定は両方とも「トラフィックを受けるポート」です。ALBのターゲットがLambdaの場合は、タイムアウト30秒・間隔35秒が既定になります。CLBは別系統の設定で、CLBのヘルスチェックのページで扱われています。
登録直後のinitialから1回の合格でhealthyになるまでの判定の流れ
ALBでは、ターゲットを登録するとまずinitialになり、ヘルスチェックに1回合格した時点でhealthyへ移ります。その後は連続失敗がUnhealthyThresholdCountに達するとunhealthy、連続成功がHealthyThresholdCountに達するとhealthyへ戻る仕組みです。
状態はほかに、登録解除中のdraining、リスナーで使われていないなどのunused、ヘルスチェック無効のunavailableがあります。ヘルスチェックは各ロードバランサーノードがそれぞれのターゲットへ送るので、アクセスログに残る回数は「間隔ごとに1回」より多くなる仕組みです。NLBは判定を複数ノードの合議で行うため、公式ドキュメントにも設定した回数より多く届くと明記されています。HTTPで重い処理を判定先にすると、この回数分だけ負荷が乗ります。なお、ALBのヘルスチェックはWebSocketに対応していません。
全ターゲットが異常のとき全台へ流すフェイルオープンの挙動と例外
見落とされやすいのがフェイルオープンです。ALBは、ターゲットグループ内の登録済みターゲットがすべてunhealthyのとき、状態に関係なく全台へリクエストを流します。全滅したら503を返すと思っていると、エラーを返すサーバーにも通常どおり振り分けが続くので、監視の見え方が想定と変わります。
NLBでは、正常なターゲットがなくなった場合の挙動が異なる仕様です。あるアベイラビリティーゾーンに正常なターゲットが1台も無くなると、そのゾーンのIPアドレスをDNSから外します。全ゾーンで同時に全滅したときと、ターゲットグループが空のときにフェイルオープンになります。unhealthyになったターゲットに紐づくクライアント接続へはTCP RSTを返す点も、ALBとの違いです。
AWS CLIでヘルスチェック専用パスと閾値を設定して検知時間を詰める手順
既定値のまま運用すると、異常の検知に約1分、復帰に約2分半かかります。ここでは専用パスを用意し、CLIで設定を詰める手順を示します。
modify-target-groupでパス・間隔・成功コードを変更するコマンド例
設定の変更はmodify-target-groupのリファレンス(AWS CLI 2.37.9の表記)にあるオプションで行います。成功コードは--matcherにHttpCode=の形で渡します。
# ターゲットグループのARNを名前から取得
TG_ARN=$(aws elbv2 describe-target-groups --names example-web-tg \
--query 'TargetGroups[0].TargetGroupArn' --output text)
# 専用パス・10秒間隔・閾値3回と2回・成功コード200に変更
aws elbv2 modify-target-group --target-group-arn "$TG_ARN" \
--health-check-protocol HTTP \
--health-check-path /healthz \
--health-check-interval-seconds 10 \
--health-check-timeout-seconds 5 \
--healthy-threshold-count 3 \
--unhealthy-threshold-count 2 \
--matcher HttpCode=200
成功コードを複数にするときはHttpCode='200,204'のように引用符で囲みます。囲まないとCLIがカンマで値を分割してしまうためです。タイムアウトは間隔より短くしておきます。
アプリ側に置くhealthzの実装例と起動中に503を返す書き方
判定先のパスは、認証・リダイレクト・重いクエリを通らない専用の入口にします。次はNode.jsで、初期化が終わるまで503、終わったら200を返す最小の例です。
const http = require('node:http');
let ready = false; // 設定読み込みやキャッシュ準備が終わるまでfalse
const server = http.createServer((req, res) => {
if (req.url === '/healthz') {
// ELBからの判定用。認証やリダイレクトの処理より前で返す
res.writeHead(ready ? 200 : 503, { 'Content-Type': 'text/plain' });
res.end(ready ? 'ok' : 'starting');
return;
}
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('hello');
});
server.listen(8080, async () => {
await new Promise((r) => setTimeout(r, 5000)); // 初期化処理の代わり
ready = true;
});
ALBが送る判定リクエストはUser-Agent: ELB-HealthChecker/2.0を名乗ります。アクセスログから除外したい場合は、この値で振り分けると集計が汚れません。
ALBの異常検知と復帰にかかる秒数を間隔と閾値から計算する目安
検知までの時間は、おおよそ「間隔×UnhealthyThresholdCount」で見積もれます。既定のALBなら30秒×2回で約60秒、復帰は30秒×5回で約150秒です。上の例の10秒・2回・3回に変えると、検知は約20秒、復帰は約30秒まで縮みます。
ただし詰めすぎにも代償があります。間隔5秒・閾値2回にすると、GCの停止やデプロイ直後の一瞬の遅れでも切り離しが起きやすくなるうえ、判定リクエストの回数も6倍になります。実務では間隔10秒・異常2回・正常3回あたりから始め、誤検知が出たら閾値を上げる順で調整するのが無難でしょう。振り分け方式との関係はロードバランサーとは?仕組み・種類・振り分け方式を実装目線で解説にまとめています。
unhealthyの原因を理由コードから切り分けるELBのトラブルシューティング
unhealthyになったら、推測で設定を触る前に理由コードを確認します。Elb.で始まるコードはロードバランサー側、Target.で始まるコードはターゲット側に原因があります。
describe-target-healthで状態と理由コードを一覧するコマンド
describe-target-healthのリファレンスにある出力から、ターゲットごとの状態・理由・説明を表で並べます。
aws elbv2 describe-target-health --target-group-arn "$TG_ARN" \
--query 'TargetHealthDescriptions[].[Target.Id,Target.Port,TargetHealth.State,TargetHealth.Reason,TargetHealth.Description]' \
--output table
ALBでunhealthyに付く理由コードはTarget.ResponseCodeMismatch・Target.Timeout・Target.FailedHealthChecks・Elb.InternalErrorの4種類です。Target.NotInUseなら、ターゲットグループがリスナーのルールに入っていないか、ターゲットのゾーンがロードバランサーで有効になっていません。設定ではなく構成の問題なので、ヘルスチェックの値を変えても直りません。
Target.Timeoutで疑うセキュリティグループとネットワークACLの許可
Target.Timeoutは、応答が返ってこなかったことを示します。ALBのトラブルシューティングで最初に挙がるのは、ターゲットのセキュリティグループがロードバランサーからの判定ポートを許可していないケースです。ロードバランサーのセキュリティグループを送信元に指定したルールを、ターゲット側に1本足すのが定石です。
ネットワークACLを使っている場合は戻りの通信も要ります。ターゲットのサブネットでは判定ポートの受信とエフェメラルポート(1024-65535)の送信、ロードバランサーのサブネットではその逆を許可します。疎通が取れているのにタイムアウトするなら、同じネットワーク内から対象のプライベートIPへ直接リクエストし、判定先のページが5秒以内に返るかを測ってください。
応答コード不一致で確認する301・302・404とHostヘッダーの落とし穴
Target.ResponseCodeMismatchは、応答はあったものの成功コードと一致しなかった状態です。説明欄に実際のコードがHealth checks failed with these codes: [302]の形で出ます。302ならログイン画面へのリダイレクト、301ならHTTPSへの転送、404ならパスの誤りが典型です。
見落としやすいのがHostヘッダーです。判定リクエストのHostには、ドメイン名ではなくターゲットのプライベートIP(既定以外のポートならポート付き、例:Host: 10.0.0.10:8080)が入ります。nginxやApacheで名前ベースのバーチャルホストだけを定義していると、判定は既定のサーバーへ届いて404になります。DjangoやRailsの許可ホストの設定で弾かれ、400が返る構成も同じ原因です。
ヘルスチェックログをS3へ出力して失敗の時刻と理由を後から追う設定
describe-target-healthは「今の状態」しか返しません。夜間に一瞬だけunhealthyになった原因を追うには、ALBのヘルスチェックログが役に立ちます。
health_check_logs属性とバケットポリシーで有効化するCLI手順
有効化の手順によると、保存先のバケットはロードバランサーと同じリージョンに置き、暗号化はSSE-S3だけが使えます。まずバケットポリシーで書き込みを許可し、次にロードバランサーの属性を変えます。
# policy.json:ELBのログ配信サービスにPutObjectを許可(アカウントIDは自社のもの)
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "Service": "logdelivery.elasticloadbalancing.amazonaws.com" },
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::example-elb-logs/hc/AWSLogs/123456789012/*"
}]
}
aws s3api put-bucket-policy --bucket example-elb-logs --policy file://policy.json
aws elbv2 modify-load-balancer-attributes --load-balancer-arn "$LB_ARN" \
--attributes Key=health_check_logs.s3.enabled,Value=true \
Key=health_check_logs.s3.bucket,Value=example-elb-logs \
Key=health_check_logs.s3.prefix,Value=hc
設定が通ると、バケットにELBHealthCheckLogTestFileというテストファイルが作られます。これが無ければポリシーのResourceにあるプレフィックスかアカウントIDが食い違っています。課金はS3の保存料金だけで、ログ転送の帯域は課金されません。
ログ10項目の読み方とTimedOut・TargetErrorの見分け方
ログの形式は空白区切りの10項目で、ノードごとに5分おきにgzipファイルが届きます。失敗したときの1行は次の形です。
http 2025-10-31T12:44:58.901409Z 1.121980746 172.31.31.9:80 HCLogsTestIPs FAIL 502 TargetError
左から、種別・開始時刻・所要秒数・ターゲットのIPとポート・ターゲットグループ・結果・ステータスコード・理由コードと並び、最後にロードバランサーのIDとノードのIPが続きます。理由コードは、接続や応答が時間内に返らないTimedOut、5xxを返したTargetError、成功コードと合わないResponseCodeMismatch、接続を切られたConnectionResetなどの分類です。所要秒数の列を時系列で見れば、タイムアウトの直前から応答が遅くなっていたかどうかまで判断できます。
Auto ScalingとECSでヘルスチェックの結果を入れ替えに連動させる設定
ELBの判定は、そのままではリクエストの振り分けを止めるだけで、壊れたサーバーを作り直してはくれません。入れ替えまで自動化するには、Auto ScalingやECS側の設定が要ります。
Auto ScalingのヘルスチェックをELBに切り替える猶予期間の決め方
Auto Scalingのヘルスチェックの既定はEC2のステータスチェックで、ELBの判定結果は無視されます。ELBでunhealthyになったインスタンスを置き換えるには、ヘルスチェックの種別にELBを追加します。
aws autoscaling update-auto-scaling-group \
--auto-scaling-group-name example-web-asg \
--health-check-type ELB \
--health-check-grace-period 300
猶予期間の既定値は、猶予期間のドキュメントによるとコンソール作成なら300秒、CLIやSDKで作ると0秒です。TerraformやCloudFormationでグループを作った場合は0のまま残っていないかを確認してください。0だと、起動に2分かかるアプリは判定に合格する前にunhealthyとされ、作っては消すループに入ります。置き換えは一度に希望容量の10%までに抑えられ、全台がunhealthyのときはさらに少なくなる仕様です。サーバー自体の構成はAmazon EC2とは?仕組み・インスタンスタイプと料金モデル・採用判断を実装者目線で解説を参照してください。
ECSの起動後に設けるヘルスチェック猶予期間と登録解除の遅延300秒
ECSでは、サービスのhealthCheckGracePeriodSecondsが同じ役割を持ちます。サービス定義のパラメータでは、タスクの起動後この秒数のあいだ、ELB・VPC Lattice・コンテナのヘルスチェックの失敗を無視すると定義されています。既定は0秒です。
aws ecs update-service --cluster example-cluster --service example-web \
--health-check-grace-period-seconds 60
入れ替えの反対側、つまり古いタスクを外すときに効くのが登録解除の遅延です。ターゲットグループ属性のドキュメントでは既定300秒で、その間はdrainingとして処理中のリクエストの完了を待ちます。待ち時間より先にアプリが接続を閉じると、クライアントには5xxが返ります。デプロイの所要時間を縮めたい場合は、最長リクエストの処理時間より少し長い値(例:60秒)へ下げるのが妥当です。ECSのデプロイ方式との組み合わせはローリングアップデートとは?仕組みとKubernetes・ECSの設定値・採用判断を解説で、ECS自体の構築はECSでDockerコンテナを動かす手順:ECRへのpush・タスク定義・料金とEKSとの違いで扱っています。
ELBヘルスチェックの判定先に依存先を含めるかを決める受託開発の判断基準
最後に、設定値よりも影響の大きい「何を判定するか」の線引きです。
DBまで確認する深いチェックを採用しない条件と例外になる2つの場面
結論から言うと、判定先のパスでデータベースや外部APIへの疎通まで確認する構成は、原則として採用しません。データベースが数十秒止まっただけで全ターゲットが同時にunhealthyになり、ALBはフェイルオープンで全台へ流し、Auto Scalingは全台を入れ替え始めるからです。1台の故障を切り離すための仕組みが、共通の依存先の障害を増幅する側に回ります。
例外は2つです。1つ目は、ターゲットごとに個別の依存先を持つ場合で、ローカルのキャッシュやサイドカーが壊れたときはその1台だけを外したいケースが当たります。2つ目は、ブルーグリーン切り替え前の検証用ターゲットグループです。ブルーグリーンデプロイメントとは|仕組み・他方式との違い・AWS/Kubernetes実装をコード例で解説のように本番トラフィックを流す前なら、深い確認で新環境の欠陥を止める価値があります。依存先の死活はCloudWatch Syntheticsとは?Canaryの作り方・ランタイム選定・料金をAWS外形監視の実装目線で解説のような外形監視で別に見るのが筋です。
ヘルスチェックの設計は、デプロイ方式・Auto Scaling・監視と一緒に決めないと整合が取れません。既存のAWS構成の見直しを含めて相談したい場合は、インフラ構築(AWS・Google Cloud・Azure)で要件整理から対応しています。
よくある質問
ELBのヘルスチェックについて検索されている質問に、公式ドキュメントの記載をもとに答えます。
ALBのヘルスチェックで302が返ってunhealthyになるのはなぜですか?
ALBの成功コードの既定は200だけなので、ログイン画面やHTTPSへのリダイレクトで302や301が返ると失敗と数えられます。認証やリダイレクトを通らない専用パスを用意して判定先にするのが根本の対処です。成功コードに302を加える方法もありますが、アプリが壊れていてもリダイレクトだけは返る状態を見逃すため、おすすめしません。
ELBのヘルスチェックは料金がかかりますか?
ヘルスチェックの実行に対する個別の課金項目はなく、ロードバランサーの料金の範囲で動きます。ALBのヘルスチェックログを有効にした場合は、S3の保存料金が課金の対象です。ログの転送帯域は課金されないと公式ドキュメントに明記されています。
ELBのヘルスチェックの送信元IPアドレスはどこですか?
判定はロードバランサーの各ノードから送られるため、送信元はロードバランサーのサブネット内のIPです。ノードのIPは固定ではないので、ターゲットのセキュリティグループではIPを列挙せず、ロードバランサーに付けたセキュリティグループを送信元に指定します。ヘルスチェックログにはノードのIPが記録されます。
ELBのヘルスチェックとRoute 53のヘルスチェックはどう違いますか?
ELBのヘルスチェックはターゲットグループ内のサーバーごとに状態を見て、リクエストの振り分け先を決めます。Route 53のヘルスチェックはエンドポイントの死活を外部から見て、DNSの応答先を切り替える仕組みです。リージョンをまたぐ切り替えはRoute 53、リージョン内のサーバーの切り離しはELBと役割が分かれます。DNS側の設計はAmazon Route 53とは:DNS・ドメイン登録・ルーティング設計とAWS CLI構築手順を実装者目線で解説にあります。
ヘルスチェックを一時的に止めることはできますか?
止められるのはターゲットタイプがLambdaのターゲットグループだけです。CLIリファレンスには、Lambdaは既定で無効、インスタンス・IP・ALBのタイプは常に有効で無効化できないと書かれています。EC2やコンテナをメンテナンスで1台だけ外したいなら、ターゲットの登録を解除するのが正しい手順です。
関連記事
- ヘルスチェックとは?LivenessとReadinessの分離と閾値設計を実装目線で解説:ヘルスチェックの概念と閾値設計の一般論。
- Elastic Load Balancingとは?ELBとALBの違い・4種類の使い分けと料金・設定を解説:ALB・NLB・GWLB・CLBの選び方。
- ロードバランサーとは?仕組み・種類・振り分け方式を実装目線で解説【2026年版】:振り分け方式とL4・L7の違い。
- ローリングアップデートとは?仕組みとKubernetes・ECSの設定値・採用判断を解説:ヘルスチェックと組み合わせるデプロイ方式。
- CloudWatch Syntheticsとは?Canaryの作り方・ランタイム選定・料金をAWS外形監視の実装目線で解説:依存先を含めた外形監視の作り方。