ALBがサポートしたヘルスチェックログをCloudFormationで設定して確認してみた
2025年11月21日、Application Load Balancer (ALB) で「ヘルスチェックログ」が利用可能になるアップグレードがありました。
これまで、ALBのターゲットが「Unhealthy」になった際、その原因調査に苦労した経験はないでしょうか?ターゲット側のアプリケーションログを確認しようにも、リクエストが届いていなかったり、Auto Scalingですでにインスタンスが終了していたりと、原因特定が困難なケースも少なくありませんでした。
今回、CloudFormationを使用して検証環境を構築し、実際にS3に出力されたヘルスチェックログを確認する機会がありましたので、紹介させていただきます。
検証環境
ALBのヘルスチェックログを有効にしたALBを作成します。 ターゲットとしてEC2を2台用意し、そのうち1台は意図的にヘルスチェックに失敗(Webサーバー未起動)する設定としました。
構成図

S3バケットポリシー
バケットポリシーを利用して、ALBログの書き込みを許可しました。
LogsBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref LogsBucket
PolicyDocument:
Version: '2012-10-17'
Statement:
- Sid: AWSLogDeliveryWrite
Effect: Allow
Principal:
Service: logdelivery.elasticloadbalancing.amazonaws.com
Action: s3:PutObject
Resource:
# アクセスログ用のパス許可
- !Sub '${LogsBucket.Arn}/access-logs/AWSLogs/${AWS::AccountId}/*'
# ヘルスチェックログ用のパス許可
- !Sub '${LogsBucket.Arn}/health-check-logs/AWSLogs/${AWS::AccountId}/*'
ALB
LoadBalancerAttributes で、今回の新機能であるヘルスチェックログ設定(health_check_logs)を追加しました。
ApplicationLoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
LoadBalancerAttributes:
# --- Health Check Logs Settings ---
- Key: health_check_logs.s3.enabled
Value: 'true'
- Key: health_check_logs.s3.bucket
Value: !Ref LogsBucket
- Key: health_check_logs.s3.prefix
Value: 'health-check-logs'
CloudFormation
検証環境作成テンプレート
AWSTemplateFormatVersion: '2010-09-09'
Description: 'ALB Access & Health Check Logs (Separated Prefixes) Test Environment'
Parameters:
LatestAmiId:
Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
Default: /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-arm64
VpcId:
Type: AWS::EC2::VPC::Id
Description: Default VPC ID
SubnetIds:
Type: List<AWS::EC2::Subnet::Id>
Description: At least 2 public subnets in different AZs
Resources:
# S3 Bucket for Logs
LogsBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub 'alb-logs-${AWS::AccountId}-${AWS::Region}'
OwnershipControls:
Rules:
- ObjectOwnership: BucketOwnerEnforced
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
# S3 Bucket Policy
LogsBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref LogsBucket
PolicyDocument:
Version: '2012-10-17'
Statement:
- Sid: AWSLogDeliveryWrite
Effect: Allow
Principal:
Service: logdelivery.elasticloadbalancing.amazonaws.com
Action: s3:PutObject
Resource:
# アクセスログ用のパス許可
- !Sub '${LogsBucket.Arn}/access-logs/AWSLogs/${AWS::AccountId}/*'
# ヘルスチェックログ用のパス許可
- !Sub '${LogsBucket.Arn}/health-check-logs/AWSLogs/${AWS::AccountId}/*'
# Security Group for ALB
ALBSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group for ALB
VpcId: !Ref VpcId
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
# Security Group for EC2
EC2SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group for EC2 instances
VpcId: !Ref VpcId
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
SourceSecurityGroupId: !Ref ALBSecurityGroup
# IAM Role for EC2
EC2Role:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: ec2.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
EC2InstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Roles:
- !Ref EC2Role
# EC2 Instance 1 (Healthy)
EC2Instance1:
Type: AWS::EC2::Instance
Properties:
ImageId: !Ref LatestAmiId
InstanceType: t4g.nano
IamInstanceProfile: !Ref EC2InstanceProfile
NetworkInterfaces:
- AssociatePublicIpAddress: true
DeviceIndex: '0'
GroupSet:
- !Ref EC2SecurityGroup
SubnetId: !Select [0, !Ref SubnetIds]
UserData:
Fn::Base64: !Sub |
#!/bin/bash
yum update -y
yum install -y httpd
systemctl start httpd
systemctl enable httpd
echo "<h1>Healthy Instance 1 (ARM)</h1>" > /var/www/html/index.html
Tags:
- Key: Name
Value: ALB-Target-Healthy
# EC2 Instance 2 (Unhealthy)
EC2Instance2:
Type: AWS::EC2::Instance
Properties:
ImageId: !Ref LatestAmiId
InstanceType: t4g.nano
IamInstanceProfile: !Ref EC2InstanceProfile
NetworkInterfaces:
- AssociatePublicIpAddress: true
DeviceIndex: '0'
GroupSet:
- !Ref EC2SecurityGroup
SubnetId: !Select [1, !Ref SubnetIds]
Tags:
- Key: Name
Value: ALB-Target-Unhealthy
# Target Group
TargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Name: alb-logs-test-tg
Port: 80
Protocol: HTTP
VpcId: !Ref VpcId
HealthCheckEnabled: true
HealthCheckIntervalSeconds: 30
HealthCheckPath: /
HealthCheckProtocol: HTTP
Targets:
- Id: !Ref EC2Instance1
- Id: !Ref EC2Instance2
# Application Load Balancer
ApplicationLoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
DependsOn: LogsBucketPolicy
Properties:
Name: alb-logs-test
Type: application
Scheme: internet-facing
SecurityGroups:
- !Ref ALBSecurityGroup
Subnets: !Ref SubnetIds
LoadBalancerAttributes:
# --- Access Logs Settings ---
- Key: access_logs.s3.enabled
Value: 'true'
- Key: access_logs.s3.bucket
Value: !Ref LogsBucket
- Key: access_logs.s3.prefix
Value: 'access-logs' # フォルダ分け: access-logs/
# --- Health Check Logs Settings ---
- Key: health_check_logs.s3.enabled
Value: 'true'
- Key: health_check_logs.s3.bucket
Value: !Ref LogsBucket
- Key: health_check_logs.s3.prefix
Value: 'health-check-logs' # フォルダ分け: health-check-logs/
# Listener
ALBListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
LoadBalancerArn: !Ref ApplicationLoadBalancer
Port: 80
Protocol: HTTP
DefaultActions:
- Type: forward
TargetGroupArn: !Ref TargetGroup
Outputs:
ALBDNSName:
Description: ALB DNS Name
Value: !GetAtt ApplicationLoadBalancer.DNSName
S3BucketName:
Description: S3 Bucket for Logs
Value: !Ref LogsBucket
動作確認
デプロイ後、ALBに疎通することを確認しました。
# ALB DNS名を変数に設定
ALB_DNS="<alb-name>-<random-id>.<region>.elb.amazonaws.com"
# 疎通確認
curl http://${ALB_DNS}
ヘルスチェックログ出力の確認
数分待機した後、ヘルスチェックログの出力先S3を確認しました。
health-check-logs プレフィックス配下にログが生成されていました。
BUCKET_NAME="alb-logs-<account-id>-<region>"
aws s3 ls s3://${BUCKET_NAME}/health-check-logs/
階層のイメージ
s3://alb-logs-123456789012-ap-northeast-1/
├── access-logs/AWSLogs/123456789012/elasticloadbalancing/ap-northeast-1/2025/11/22/
└── health-check-logs/AWSLogs/123456789012/elasticloadbalancing/ap-northeast-1/2025/11/22/
※ヘルスチェックログ、従来からのアクセスログも同じS3バケット、異なるPrefix指定で出力しています。
ファイル名 health_check_log_<account-id>_elasticloadbalancing_<region>_<load-balancer-name>.<load-balancer-id>_<timestamp>_<ip-address>_<random-string>.log.gz の形式で、ヘルスチェックログが出力されている事を確認できました。
2025-11-22 15:35:08 362 health-check-logs/AWSLogs/<account-id>/elasticloadbalancing/<region>/2025/11/22/health_check_log_<account-id>_elasticloadbalancing_<region>_app.<alb-name>.<alb-id>_20251122T1535Z_<ip-1>_<random>.log.gz
2025-11-22 15:35:08 358 health-check-logs/AWSLogs/<account-id>/elasticloadbalancing/<region>/2025/11/22/health_check_log_<account-id>_elasticloadbalancing_<region>_app.<alb-name>.<alb-id>_20251122T1535Z_<ip-2>_<random>.log.gz
2025-11-22 15:40:08 357 health-check-logs/AWSLogs/<account-id>/elasticloadbalancing/<region>/2025/11/22/health_check_log_<account-id>_elasticloadbalancing_<region>_app.<alb-name>.<alb-id>_20251122T1540Z_<ip-1>_<random>.log.gz
2025-11-22 15:40:08 357 health-check-logs/AWSLogs/<account-id>/elasticloadbalancing/<region>/2025/11/22/health_check_log_<account-id>_elasticloadbalancing_<region>_app.<alb-name>.<alb-id>_20251122T1540Z_<ip-2>_<random>.log.gz
ログファイルをダウンロードして中身を確認しました。
# ログのダウンロードと解凍
aws s3 cp s3://<bucket-name>/path/to/your/log.gz .
gzip -d log.gz
cat log
2台のEC2に対するヘルスチェック結果(成功と失敗)が記録されていました。
> download: s3://<bucket-name>/health-check-logs/AWSLogs/<account-id>/elasticloadbalancing/<region>/YYYY/MM/DD/health_check_log_<account-id>_elasticloadbalancing_<region>_<alb-name>.<alb-id>_<timestamp>_<ip>_<random>.log.gz to /tmp/hc.log.gz
http 2025-11-22T15:35:10.504741Z 0.001469625 172.31.x.x:80 <target-group-name> PASS 200 -
http 2025-11-22T15:35:10.504211Z 0.002958522 172.31.x.x:80 <target-group-name> FAIL 502 TargetError
http 2025-11-22T15:35:40.535012Z 0.001567201 172.31.x.x:80 <target-group-name> PASS 200 -
http 2025-11-22T15:35:40.534548Z 0.002960985 172.31.x.x:80 <target-group-name> FAIL 502 TargetError
http 2025-11-22T15:36:10.547468Z 0.00154518 172.31.x.x:80 <target-group-name> PASS 200 -
http 2025-11-22T15:36:10.547032Z 0.002790372 172.31.x.x:80 <target-group-name> FAIL 502 TargetError
http 2025-11-22T15:36:40.578086Z 0.001601312 172.31.x.x:80 <target-group-name> PASS 200 -
http 2025-11-22T15:36:40.577634Z 0.002930846 172.31.x.x:80 <target-group-name> FAIL 502 TargetError
http 2025-11-22T15:37:10.608677Z 0.001456432 172.31.x.x:80 <target-group-name> PASS 200 -
http 2025-11-22T15:37:10.608229Z 0.00288121 172.31.x.x:80 <target-group-name> FAIL 502 TargetError
http 2025-11-22T15:37:40.635578Z 0.001517185 172.31.x.x:80 <target-group-name> PASS 200 -
http 2025-11-22T15:37:40.635025Z 0.003032974 172.31.x.x:80 <target-group-name> FAIL 502 TargetError
http 2025-11-22T15:38:10.666143Z 0.001580969 172.31.x.x:80 <target-group-name> PASS 200 -
http 2025-11-22T15:38:10.665657Z 0.003204206 172.31.x.x:80 <target-group-name> FAIL 502 TargetError
http 2025-11-22T15:38:40.695716Z 0.001406524 172.31.x.x:80 <target-group-name> PASS 200 -
http 2025-11-22T15:38:40.695266Z 0.00285128 172.31.x.x:80 <target-group-name> FAIL 502 TargetError
http 2025-11-22T15:39:10.716992Z 0.001626345 172.31.x.x:80 <target-group-name> PASS 200 -
http 2025-11-22T15:39:10.716548Z 0.002873815 172.31.x.x:80 <target-group-name> FAIL 502 TargetError
http 2025-11-22T15:39:40.739579Z 0.001600648 172.31.x.x:80 <target-group-name> PASS 200 -
http 2025-11-22T15:39:40.739106Z 0.00284948 172.31.x.x:80 <target-group-name> FAIL 502 TargetError
ログフォーマット
| 項目 | 値 | 説明 |
|---|---|---|
| プロトコル | http | ヘルスチェックのプロトコル |
| タイムスタンプ | 2025-11-22T... | 実行日時 |
| 処理時間 | 0.001469625 | ヘルスチェック応答時間 |
| ターゲット | 172.31.x.x:80 | 対象のIPアドレスとポート |
| ターゲットグループ | alb-logs-test-tg | 所属するターゲットグループ名 |
| ステータス | PASS | ヘルスチェック結果 (PASS/FAIL) |
| HTTPコード | 200 | ターゲットからの応答コード |
| 理由 | - | 失敗理由 (成功時はハイフン) |
まとめ
これまで、ALBのヘルスチェックが失敗しターゲットが「Unhealthy」となった場合、調査はターゲット側のアプリログやアクセスログに依存していました。しかし、リソース枯渇でログが残せなかったり、Auto Scaling等でインスタンス自体が交換されてしまったりすると、手がかりを得ることが困難なケースもありました。
今回サポートされたヘルスチェックログを利用することで、ALB側から見た「失敗の真因」を詳細に追跡できるようになりました。 本検証でも、Webサーバー停止に伴う TargetError (502) が明確に記録されており、ネットワーク経路の問題ではなく、インスタンス内部(またはアプリケーション応答)の問題であることを即座に判別できました。
ターゲットまでのネットワーク疎通に不安がある環境や、ダウンタイムが許されない重要なワークロードにおいて、再発防止のための詳細な調査が求められる場合、このヘルスチェックログが活用できる可能性があります。ぜひ一度お試しください。







