4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

CloudWatchログをS3に転送、Athenaでクエリしたかっただけなのに、ちょっと複雑だった話

Last updated at Posted at 2025-10-19

はじめに

やりたかったこと:CloudWatchログをS3に保存し、そのログをAthenaでクエリしたかった

上記を実現するために、CloudFormationで、CloudWatch LogsSubscription FilterKinesis Data FirehoseS3 という構成を実装したものの、S3に保存されたファイルがバイナリ形式になり、Athenaでクエリできませんでした。

また、各リソースごとの設定やデフォルト動作も微妙に異なるといったことがあり、この機に全体的に調査をしました。

構成

マネージドサービスのみで、CloudWatch Logsから、S3へ転送する場合、以下の構成になります。
CloudWatch LogsSubscription FilterKinesis Data FirehoseS3

image.png

問題:S3のオブジェクトがバイナリ扱いになる

最初にFirehoseでS3にログを転送したところ、S3上のオブジェクトがバイナリファイルとして扱われ、Athenaでクエリできない状態になりました。

※オブジェクトをダウンロードしたときに発覚し、.gz拡張子を付けた場合に正常に開けることを確認しました。

原因の調査

上記構成の場合、Subscription Filterの設定と、Firehoseの設定により、送信されるログには以下の4つのパターンがあることが判明しました。:

  1. Subscription Filterで圧縮 + Firehose で圧縮 → 2重gzip圧縮
  2. Subscription Filterで圧縮 + Firehose で圧縮しない → gzip圧縮
  3. Subscription Filterで圧縮 → Firehose で解凍 → 再圧縮 → gzip圧縮
  4. Subscription Filterで圧縮 → Firehose で解凍 → 圧縮しない → 未圧縮
  • Subscription Filterでは、デフォルトで圧縮が行われます。
  • 1, 2, 3の場合はデフォルトでは、ファイル拡張子がつきません。

そのため、全部のパターンで、Athenaでクエリできないじゃないか、、、という話になります。

元々の実装は2番のパターンで、ファイルはgzip圧縮されているものの拡張子がないため、Athenaが圧縮ファイルとして認識できない状態でした。

CSV、TSV、および JSON のデータについては、Athena がファイル拡張子から圧縮タイプを判断します。ファイル拡張子がない場合、Athena はデータを非圧縮のプレーンテキストとして扱います。

Athena で圧縮を使用する - 注意事項とリソース

解決策:ExtendedS3DestinationConfigurationを使用

この問題の解決策は、最終的なオブジェクトにファイル拡張子を付けることです。
また、上記の変更にあたっては、S3DestinationConfigurationプロパティからExtendedS3DestinationConfigurationプロパティへの変更が必要でした。

変更前(問題のある設定)

S3DestinationConfiguration:
  BucketARN: !Sub "arn:aws:s3:::${S3BucketName}"
  CompressionFormat: UNCOMPRESSED
  # FileExtension指定不可

変更後(解決後の設定)

ExtendedS3DestinationConfiguration: # 変更
  BucketARN: !Sub "arn:aws:s3:::${S3BucketName}"
  CompressionFormat: UNCOMPRESSED
  FileExtension: ".gz"  # 変更、また、拡張子指定

この後、Athenaでデータベース等を作成して、実際にクエリできることも確認しました。

重要なポイント

  • S3DestinationConfigurationプロパティではFileExtensionプロパティが使用できない
  • ExtendedS3DestinationConfigurationプロパティでのみファイル拡張子の指定が可能
  • CloudFormationでの変更は「No interruption」で実行可能

学んだこと

  1. Athenaでの圧縮ファイル読み込み - ファイル拡張子が必須
  2. FirehoseのDestination設定 - S3DestinationConfigurationでは拡張子指定不可
  3. 圧縮パターンの理解 - Subscription FilterとFirehoseの組み合わせによる違い

デモ用CloudFormationテンプレート

完全なCloudFormationテンプレート(クリックして展開)
AWSTemplateFormatVersion: '2010-09-09'

Parameters:
  S3BucketName:
    Type: String
    Description: S3 bucket name for storing logs
  LogGroupName:
    Type: String
    Description: CloudWatch log group name

Resources:
# ------------------------------#
#  IAM Policy, Role for Firehose
# ------------------------------#
  FirehosePolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      Description: "Allow Firehose to put logs to S3"
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action:
              - "s3:AbortMultipartUpload"
              - "s3:GetBucketLocation"
              - "s3:GetObject"
              - "s3:ListBucket"
              - "s3:ListBucketMultipartUploads"
              - "s3:PutObject"
            Resource:
              - !Sub "arn:aws:s3:::${S3BucketName}"
              - !Sub "arn:aws:s3:::${S3BucketName}/*"

  FirehoseRole:
    Type: AWS::IAM::Role
    Properties:
      ManagedPolicyArns:
        - !Ref FirehosePolicy
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action: sts:AssumeRole
            Principal:
              Service: firehose.amazonaws.com

# ------------------------------------------#
#  IAM Policy, Role for Subscription Filter
# ------------------------------------------#
  SubscriptionFilterPolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      Description: "Allow Subscription Filter to put logs to Firehose"
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Action:
              - "firehose:PutRecord"
              - "firehose:PutRecords"
              - "firehose:PutRecordBatch"
            Resource: !Sub 'arn:aws:firehose:${AWS::Region}:${AWS::AccountId}:deliverystream/*'
            Effect: "Allow"
            
  SubscriptionFilterRole:
    Type: AWS::IAM::Role
    Properties:
      ManagedPolicyArns:
        - !Ref SubscriptionFilterPolicy
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action: sts:AssumeRole
            Principal:
              Service: !Sub "logs.${AWS::Region}.amazonaws.com"

# ------------------------------#
#  Firehose
# ------------------------------#
  Log1Firehose:
    Type: AWS::KinesisFirehose::DeliveryStream
    Properties:
      DeliveryStreamEncryptionConfigurationInput:
        KeyType: AWS_OWNED_CMK
      DeliveryStreamName: !Sub "firehose${LogGroupName}"
      DeliveryStreamType: DirectPut
      ExtendedS3DestinationConfiguration:
        BucketARN: !Sub "arn:aws:s3:::${S3BucketName}"
        BufferingHints:
          IntervalInSeconds: 900
          SizeInMBs: 128
        Prefix: !Sub "firehose${LogGroupName}/"
        ErrorOutputPrefix: !Sub "firehose/error${LogGroupName}/"
        FileExtension: ".gz"
        CompressionFormat: UNCOMPRESSED
        CloudWatchLoggingOptions:
          Enabled: false
        RoleARN: !GetAtt FirehoseRole.Arn

# ------------------------------#
#  Subscription Filter
# ------------------------------#
  SubscriptionFilter1:
    Type: AWS::Logs::SubscriptionFilter
    Properties:
      DestinationArn: !GetAtt Log1Firehose.Arn
      FilterName: !Sub "subscription-filter${LogGroupName}"
      FilterPattern: ""
      LogGroupName: !Ref LogGroupName
      RoleArn: !GetAtt SubscriptionFilterRole.Arn

まとめ

ただ「ログをS3に保存、クエリがしたい」だけでしたが、ファイル拡張子の設定が重要でした。
そもそも、もっと簡単にCloudWatch LogsからS3に転送したり、各リソースからS3に直接送れるようになると嬉しいな・・・

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?