ALBがサポートしたヘルスチェックログをCloudFormationで設定して確認してみた

ALBがサポートしたヘルスチェックログをCloudFormationで設定して確認してみた

2025年11月のアップデートで追加されたALBの「ヘルスチェックログ」機能を検証しました。CloudFormationテンプレートを使ってアクセスログとヘルスチェックログを分離保存する環境を構築し、S3に出力された実際のヘルスチェックログの中身を確認しました。
2025.11.23

2025年11月21日、Application Load Balancer (ALB) で「ヘルスチェックログ」が利用可能になるアップグレードがありました。

https://aws.amazon.com/jp/about-aws/whats-new/2025/11/application-load-balancer-health-check-logs/

これまで、ALBのターゲットが「Unhealthy」になった際、その原因調査に苦労した経験はないでしょうか?ターゲット側のアプリケーションログを確認しようにも、リクエストが届いていなかったり、Auto Scalingですでにインスタンスが終了していたりと、原因特定が困難なケースも少なくありませんでした。

今回、CloudFormationを使用して検証環境を構築し、実際にS3に出力されたヘルスチェックログを確認する機会がありましたので、紹介させていただきます。

検証環境

ALBのヘルスチェックログを有効にしたALBを作成します。 ターゲットとしてEC2を2台用意し、そのうち1台は意図的にヘルスチェックに失敗(Webサーバー未起動)する設定としました。

構成図

AWS構成図

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}/*'

https://dev.classmethod.jp/articles/alb-access-log-s3-bucket-policy-tips/

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) が明確に記録されており、ネットワーク経路の問題ではなく、インスタンス内部(またはアプリケーション応答)の問題であることを即座に判別できました。

ターゲットまでのネットワーク疎通に不安がある環境や、ダウンタイムが許されない重要なワークロードにおいて、再発防止のための詳細な調査が求められる場合、このヘルスチェックログが活用できる可能性があります。ぜひ一度お試しください。

この記事をシェアする

FacebookHatena blogX

関連記事