ALB のアクセスログを出力する S3バケットは ALB と同じリージョンに存在する必要がある

ALB のアクセスログを出力する S3バケットは ALB と同じリージョンに存在する必要がある

ALB のアクセスログを出力先の S3 バケットは、ALB と同じリージョンに存在する必要があるので注意です。
2026.09.18

お久しぶりです。岩城です。

皆さん、マルチリージョンで動くワークロードのログを 1 つの S3 バケットに集約したいと思うことはありませんか。

私はありまして、大阪リージョンで動いている ALB のアクセスログを大阪リージョンの S3 バケットに集約しようとして失敗しました。

ALB の仕様が関係していたので、原因と回避策を紹介したいと思います。

まとめ

  • ALB のアクセスログの出力先バケットは、同じリージョンに存在しなければなりません
  • ALB の公式ドキュメントに記載を見つけられなかったのですが、API リファレンスには記載があります

The S3 bucket must exist in the same Region as the load balancer and must have a bucket policy that grants Elastic Load Balancing permissions to write to the bucket.
(機械翻訳)
S3 バケットはロードバランサーと同じリージョンに存在し、Elastic Load Balancing によるバケットへの書き込みを許可するバケットポリシーが設定されている必要があります。

  • 考えられる対応
対応案 構成 料金 難易度 備考
リージョンごとに集約バケットを用意 各リージョンの ALB が、それぞれ同一リージョンのバケットにログを保持する(1 つには集約しない) 各リージョンのストレージ料のみ。クロスリージョン転送は発生しない 構成がシンプルで安価。ただし集約されないため、横断的に見るには各バケットを個別に参照する必要がある
S3 クロスリージョンレプリケーション(CRR) ALB が東京の S3 バケットへログを配信し、CRR が大阪の S3 バケットへレプリケーションする 各リージョンのストレージ料、CRR の前提となるバージョニングによる追加のストレージ料、クロスリージョン転送料が掛かる 低〜中 マネージドで運用しやすい。送信元と送信先の両方でバージョニングが必須
S3 イベント通知と Lambda ALB が東京の S3バケットへログを配信し、ObjectCreated で Lambda を起動して大阪の S3 バケットへコピーする クロスリージョン転送料、Lambda 実行料、両バケットのストレージ料が掛かる コピー時にプレフィックス変換や絞り込みができる。ログ数が多いと呼び出しと転送のコストが増え、Lambda の運用と監視も必要になる

それでは、検証してみましょう。

ALB と別リージョンの S3 バケットに出力できないことを確認する

下図のような検証環境を作りました。

Untitled(5).png

東京リージョンの ALB のアクセスログを、大阪リージョンの S3 バケットに出力するように設定してみます。

同じリージョンに存在する S3 バケットを指定していないためエラーになりました。

~ $ aws elbv2 modify-load-balancer-attributes \
>   --region ap-northeast-1 \
>   --load-balancer-arn arn:aws:elasticloadbalancing:ap-northeast-1:xxxxxxxxxxxx:loadbalancer/app/devio-iwaki-alb/5449dd9807b7d76c \
>   --attributes \
>     Key=access_logs.s3.enabled,Value=true \
>     Key=access_logs.s3.bucket,Value=devio-iwaki-s3-osaka-xxxxxxxxxxxx

aws: [ERROR]: An error occurred (InvalidConfigurationRequest) when calling the ModifyLoadBalancerAttributes operation: S3Bucket: devio-iwaki-s3-osaka-xxxxxxxxxxxx is not located in the same region with ELB: app/devio-iwaki-alb/5449dd9807b7d76c

当然、同じリージョンに存在する S3 バケットを指定すれば、問題なく設定できます。

~ $ aws elbv2 modify-load-balancer-attributes \
>   --region ap-northeast-1 \
>   --load-balancer-arn arn:aws:elasticloadbalancing:ap-northeast-1:xxxxxxxxxxxx:loadbalancer/app/devio-iwaki-alb/5449dd9807b7d76c \
>   --attributes \
>     Key=access_logs.s3.enabled,Value=true \
>     Key=access_logs.s3.bucket,Value=devio-iwaki-s3-tokyo-xxxxxxxxxxxx

{
    "Attributes": [
        {
            "Key": "access_logs.s3.enabled",
            "Value": "true"
        },
        {
            "Key": "access_logs.s3.bucket",
            "Value": "devio-iwaki-s3-tokyo-992031286038"
        },
        {
            "Key": "access_logs.s3.prefix",
            "Value": ""
        },
(~省略~)		
    ]
}
(END)

ALB が存在するリージョンごとに S3 バケットを用意するのでも良いですが、ログは 1 箇所に集約したいじゃないですか。

なので、東京リージョンの S3 バケットから大阪リージョンの S3 バケットにクロスリージョンレプリケーションして、1 箇所に集約する構成にします。

S3 バケットのクロスリージョンレプリケーションを使って 1 箇所にログを集約する

以下のような構成で東京リージョンから大阪リージョンの S3 バケットにアクセスログをコピーする構成を試してみました。

Untitled(6).png

レプリケーション元と先の S3 バケットの設定が気になる方は CFn テンプレートを確認してください。

レプリケーション元(東京)の CFn テンプレート
AWSTemplateFormatVersion: "2010-09-09"
Description: >
  CRR verification - Tokyo (ap-northeast-1) SOURCE bucket.
  Receives ALB access logs (same-region requirement satisfied) and replicates
  ALL objects to the Osaka destination bucket via S3 Cross-Region Replication.
  Source objects are expired quickly by a lifecycle rule to minimize cost.

Parameters:
  DestinationBucketArn:
    Type: String
    Description: ARN of the Osaka destination bucket (from the crr-osaka-dest stack output).
  SourceExpireDays:
    Type: Number
    Default: 7
    Description: Days after which current-version objects in the Tokyo source bucket expire.

Resources:
  SourceBucket:
    Type: AWS::S3::Bucket
    DeletionPolicy: Delete
    UpdateReplacePolicy: Delete
    Properties:
      BucketName: !Sub "devio-iwaki-s3-tokyo-${AWS::AccountId}"
      VersioningConfiguration:
        Status: Enabled
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      OwnershipControls:
        Rules:
          - ObjectOwnership: BucketOwnerEnforced
      LifecycleConfiguration:
        Rules:
          # Keep the source lean: expire current objects quickly (they are already
          # replicated to Osaka), and clean up old versions and stale uploads.
          - Id: expire-current-objects
            Status: Enabled
            ExpirationInDays: !Ref SourceExpireDays
          - Id: expire-noncurrent-versions
            Status: Enabled
            NoncurrentVersionExpiration:
              NoncurrentDays: 3
          - Id: abort-incomplete-multipart
            Status: Enabled
            AbortIncompleteMultipartUpload:
              DaysAfterInitiation: 3
      ReplicationConfiguration:
        Role: !GetAtt ReplicationRole.Arn
        Rules:
          - Id: replicate-all-to-osaka
            Status: Enabled
            Priority: 0
            Filter: {}            # empty filter = ALL objects
            DeleteMarkerReplication:
              Status: Disabled    # keep objects on destination even if deleted at source
            Destination:
              Bucket: !Ref DestinationBucketArn

  # Bucket policy for ALB access log delivery (same as the same-region setup).
  SourceBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref SourceBucket
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Sid: AllowELBLogDelivery
            Effect: Allow
            Principal:
              Service: logdelivery.elasticloadbalancing.amazonaws.com
            Action: s3:PutObject
            Resource: !Sub "arn:aws:s3:::${SourceBucket}/AWSLogs/${AWS::AccountId}/*"

  # IAM role assumed by S3 to perform replication.
  ReplicationRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub "devio-iwaki-role-crr-${AWS::AccountId}"
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: s3.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: devio-iwaki-crr-policy
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              # Read source objects + replication metadata
              - Effect: Allow
                Action:
                  - s3:GetReplicationConfiguration
                  - s3:ListBucket
                Resource: !Sub "arn:aws:s3:::devio-iwaki-s3-tokyo-${AWS::AccountId}"
              - Effect: Allow
                Action:
                  - s3:GetObjectVersionForReplication
                  - s3:GetObjectVersionAcl
                  - s3:GetObjectVersionTagging
                Resource: !Sub "arn:aws:s3:::devio-iwaki-s3-tokyo-${AWS::AccountId}/*"
              # Write replicas to the destination bucket
              - Effect: Allow
                Action:
                  - s3:ReplicateObject
                  - s3:ReplicateDelete
                  - s3:ReplicateTags
                Resource: !Sub "${DestinationBucketArn}/*"

Outputs:
  SourceBucketName:
    Description: Name of the Tokyo source bucket (set this as the ALB access log bucket)
    Value: !Ref SourceBucket
  ReplicationRoleArn:
    Description: ARN of the replication IAM role
    Value: !GetAtt ReplicationRole.Arn
レプリケーション先(大阪)の CFn テンプレート
AWSTemplateFormatVersion: "2010-09-09"
Description: >
  CRR verification - Osaka (ap-northeast-3) DESTINATION bucket.
  Receives replicated objects from the Tokyo source bucket via S3 Cross-Region
  Replication. Versioning enabled (required for replication target).

Resources:
  DestinationBucket:
    Type: AWS::S3::Bucket
    DeletionPolicy: Delete
    UpdateReplacePolicy: Delete
    Properties:
      BucketName: !Sub "devio-iwaki-s3-osaka-repl-${AWS::AccountId}"
      VersioningConfiguration:
        Status: Enabled
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      OwnershipControls:
        Rules:
          - ObjectOwnership: BucketOwnerEnforced
      LifecycleConfiguration:
        Rules:
          # Keep costs low: expire noncurrent versions quickly on the destination.
          - Id: expire-noncurrent-versions
            Status: Enabled
            NoncurrentVersionExpiration:
              NoncurrentDays: 7
          - Id: abort-incomplete-multipart
            Status: Enabled
            AbortIncompleteMultipartUpload:
              DaysAfterInitiation: 3

Outputs:
  DestinationBucketName:
    Description: Name of the Osaka destination bucket
    Value: !Ref DestinationBucket
  DestinationBucketArn:
    Description: ARN of the Osaka destination bucket (use as ReplicationDestinationArn in the Tokyo stack)
    Value: !GetAtt DestinationBucket.Arn

レプリケーション元の S3 バケットに作成されたアクセスログ(TestFile)が大阪リージョンにレプリケーションされましたね。

CleanShot 2026-09-17 at 13.13.11.png

CleanShot 2026-09-17 at 13.13.51.png

ログのタイムスタンプを見ると同時刻なのでリアルタイムでレプリケーションされていると思いがちですが、ここには注意が必要です。

メタデータを保持してレプリケーションされるので、ログのタイムスタンプは同一になります。しかし、レプリケーションはリアルタイムではありません。

ほとんどのレプリケーションは 15 分以内に完了しますが、場合によってはレプリケーションに最大 48 時間かかることがあります。

どうしても 15 分以内にログをレプリケーションさせたい場合は、S3 レプリケーションタイムコントロール (S3 RTC) の利用を検討してください。

S3 Replication Time Control (S3 RTC)を使用してコンプライアンス要件を満たす - Amazon Simple Storage Service

わざわざ S3 イベント通知 + Lambda など別の仕組みを使ってコピーしなくても、マネージドな機能で実現できるのが良いですね。

さいごに

ALB のアクセスログは同一リージョンの S3 バケットにしか出力できない、というのは意外と見落としがちなポイントでした。

今回は東京リージョンにもログ集約バケットを作り、リージョンごとに集約先を分けました。どうしても 1 つのバケットに集約したい場合は CRR が手軽ですが、クロスリージョンの転送料が乗るので、ログの保持期間やライフサイクル設定とあわせて検討するのが良さそうです。

どなたかのお役に立てれば幸いです。

この記事をシェアする

AWSのお困り事はクラスメソッドへ

関連記事