Amazon CloudWatch LogsをKinesis Data Firehose経由でS3に保存する

AWS

Amazon CloudWatch Logsのバックアップを目的としてKinesis Data Firehoseを経由し、S3に保存する方法を解説します。

CreateExportTaskのAPIを利用しS3にエクスポートすることも可能ですがこちらのAPIはアカウント内で並列実行出来ないという制限があるのでスケールアウトも難しく、リアルタイム処理も行いにくいです。対して、Kinesis Data Firehoseを利用するとストリーミングデータとして逐次S3へ保存していくことが可能です。

今回はサンプルとしてAWS LambdaのロググループをS3に転送してみます。構築にはAWS SAMを利用します。

注意点

・転送可能なリクエスト、転送量には制限があります。
 実際に本番運用する場合は転送ログのボリュームをしっかり見積もりしましょう。
 Amazon Kinesis Data Firehoseのクォータ
・S3に保存されるログはCloudWatchログそのままでは無くメタデータが付与れています。

ログの保存先S3バケットとサンプルログを出力するLambdaを作成

以下内容のAWS SAMです。
・サブスクリプションフィルターを設定するためにLambdaのロググループを明示的に構築
・ロググループはS3にバックアップする事を想定し、保存期間を30日に設定

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  kinesis-firehose-sample

Resources:
  # ログを出力するサンプルLambda
  HelloFunc:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: HelloFunc
      CodeUri: hello/
      Handler: app.lambda_handler
      Runtime: python3.8
      Events:
        LogOutputFuncEvent:
          Type: Api
          Properties:
            Path: /hello
            Method: get

  # Lambdaロググループ
  HelloFuncLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub /aws/lambda/${HelloFunc}
      # he number of days to retain the log events in the specified log group.
      # Possible values are: 1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1827, and 3653
      RetentionInDays: 30

  # ログの保存先バケット
  LogBackupBucket:
    Type: "AWS::S3::Bucket"
    Properties:
      BucketName: "2021-12-10-logbuckup-bucket"
      AccessControl: Private
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true

Kinesis Data Firehoseの作成

上記で作成したS3を宛先とするFirehoseの定義と必要なIAMポリシー、ロールを作成します。
Firehoseに送られたデータはバッファリングされ、指定したデータサイズ、または指定の秒数が経過した時点でS3に出力されます。

  # S3ログ出力用のFirehoseストリーム
  LogDeliveryStream:
    Type: AWS::KinesisFirehose::DeliveryStream
    Properties: 
      DeliveryStreamName: "cloudwatchlogs-backup-stream"
      DeliveryStreamType: DirectPut
      S3DestinationConfiguration:
        BucketARN: !Sub '${LogBackupBucket.Arn}'
        # バッファリング設定
        BufferingHints:
          # バッファリングサイズ。単位はMB(1-128)
          SizeInMBs: 1
          # バッファリングする時間の長さ。単位は秒数(60-900)
          IntervalInSeconds: 60
        CloudWatchLoggingOptions:
          Enabled: true
          LogGroupName: !Sub '/aws/kinesisfirehose/cludwatchlogs-backup-${AWS::StackName}'
          LogStreamName: LogDeliveryToS3
        # 圧縮形式
        # CloudWatch Logs から Kinesis Data Firehose に送信されたデータは、
        # すでに gzip(レベル6)で圧縮されているため圧縮不要。
        CompressionFormat: UNCOMPRESSED
        # 暗号化設定
        EncryptionConfiguration:
          NoEncryptionConfig: NoEncryption
        ErrorOutputPrefix: 'log-delivery-error-'
        # S3バケットに配信するファイルに追加するプレフィックス
        Prefix: 'log-deliverry-'
        RoleARN: !Sub '${FirehoseDeliveryRole.Arn}'

  # Firehose用ロール
  FirehoseDeliveryRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Sid: ''
            Effect: Allow
            Principal:
              Service: firehose.amazonaws.com
            Action: sts:AssumeRole
            Condition:
              StringEquals:
                sts:ExternalId: !Ref 'AWS::AccountId'

  # Firehose用ポリシー
  FirehoseDeliveryPolicy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyName: firehose-log-delivery-policy
      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:::${LogBackupBucket}'
              - !Sub 'arn:aws:s3:::${LogBackupBucket}*'
      Roles:
        - !Ref 'FirehoseDeliveryRole'

Lambdaロググループにサブスクリプションフィルターを設定

Lambdaのロググループにサブスクリプションフィルターを設定します。
宛先には上記で作成したFirehoseを指定します。

  # HelloFuncのロググループ用サブスクリプションフィルター
  CWLSubscriptionFilterForHelloFunc:
    Type: AWS::Logs::SubscriptionFilter
    Properties:
      # 宛先にFirehoseを指定
      DestinationArn: !GetAtt 'LogDeliveryStream.Arn'
      FilterPattern: " " 
      LogGroupName: !Ref 'HelloFuncLogGroup'
      RoleArn: !GetAtt 'SubscriptionFilterLogsRole.Arn'

  # サブスクリプションフィルター用ロール
  # Firehoseの操作権限を付与する
  SubscriptionFilterLogsRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: !Sub 'logs.${AWS::Region}.amazonaws.com'
            Action:
              - sts:AssumeRole
      Path: /
      Policies:
        - PolicyName: root
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - firehose:*
                Resource: !GetAtt 'LogDeliveryStream.Arn'

以上

タイトルとURLをコピーしました