Cloudwatch LogsのログをKinesisのサブスクリプションフィルタを使って(ほぼリアルタイムに)S3に転送するCFnを作りました。
■ このテンプレートで作成するもの
※ ロググループが2つの場合
- S3:ログ転送先
- IAMロール:
- subscription_filter_role:CWLからFirehoseデータストリームへ書き込み権限与えたもの
- firehose_delivery_role:S3にデータを転送するのに必要な権限与えたもの
- Firehoseデータストリーム (×2)
- CWLサブスクリプションフィルタ (×2)
■ テンプレート
(パラメータの値をいい感じに変えると汎用的に使えるはずです。)
AWSTemplateFormatVersion: '2010-09-09'
Description: CloudwatchLogs to S3
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: "S3"
Parameters:
- S3BucketName
- S3ExpireDate
- Label:
default: "LogGroupNames"
Parameters:
- LogGroupName01
- LogGroupName02
- Label:
default: "CWL"
Parameters:
- SizeInMBs
- IntervalInSeconds
- CompressionFormat
Parameters:
# CWL
SizeInMBs:
Description: "The size of the buffer, in MBs, that Kinesis Data Firehose uses for incoming data before delivering it to the destination."
Type: Number
Default: 5
MinValue: 1
MaxValue: 128
IntervalInSeconds:
Description: The length of time, in seconds, that Kinesis Data Firehose buffers incoming data before delivering it to the destination.
Type: Number
Default: 300
MinValue: 60
MaxValue: 900
CompressionFormat:
Description: "The type of compression that Kinesis Data Firehose uses to compress the data that it delivers to the Amazon S3 bucket. "
Type: String
Default: 'UNCOMPRESSED'
# S3
S3ExpireDate:
Description: Number of days to keep S3 file
Type: String
Default: 730
S3BucketName:
Description: Name of the S3 bucket
Type: String
Default: ''
# LogGroupNames
LogGroupName01:
Description: The log group to associate with the subscription filter01
Type: String
Default: ''
LogGroupName02:
Description: The log group to associate with the subscription filter02
Type: String
Default: ''
Resources:
#--------------------------------------------------#
# subscription_filter_role
#--------------------------------------------------#
SubscriptionFilterRole:
Type: AWS::IAM::Role
Properties:
RoleName: subscription_filter_role
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: !Sub 'logs.${AWS::Region}.amazonaws.com'
Action:
- sts:AssumeRole
Path: /
Policies:
- PolicyName: subscription_filter_policy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- firehose:PutRecord
- firehose:PutRecords
Resource: !Sub 'arn:aws:firehose:${AWS::Region}:${AWS::AccountId}:deliverystream/*'
#--------------------------------------------------#
# firehose_delivery_role
#--------------------------------------------------#
DeliveryPolicy:
Type: AWS::IAM::Policy
Properties:
PolicyName: firehose_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:::${S3bucket}'
- !Sub 'arn:aws:s3:::${S3bucket}*'
Roles:
- !Ref 'DeliveryRole'
DeliveryRole:
Type: AWS::IAM::Role
Properties:
RoleName: firehose_delivery_role
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Sid: ''
Effect: Allow
Principal:
Service: firehose.amazonaws.com
Action: sts:AssumeRole
Condition:
StringEquals:
sts:ExternalId: !Ref 'AWS::AccountId'
#--------------------------------------------------#
# SubscriptionFilter
#--------------------------------------------------#
SubscriptionFilter01:
Type: AWS::Logs::SubscriptionFilter
Properties:
DestinationArn: !GetAtt 'DeliveryStream01.Arn'
FilterPattern: ''
LogGroupName: !Ref 'LogGroupName01'
RoleArn: !GetAtt 'SubscriptionFilterRole.Arn'
SubscriptionFilter02:
Type: AWS::Logs::SubscriptionFilter
Properties:
DestinationArn: !GetAtt 'DeliveryStream02.Arn'
FilterPattern: ''
LogGroupName: !Ref 'LogGroupName02'
RoleArn: !GetAtt 'SubscriptionFilterRole.Arn'
#--------------------------------------------------#
# Firehose DeliveryStream
#--------------------------------------------------#
DeliveryStream01:
Type: AWS::KinesisFirehose::DeliveryStream
Properties:
DeliveryStreamName: !Sub '${LogGroupName01}-stream'
DeliveryStreamType: 'DirectPut'
S3DestinationConfiguration:
BucketARN: !Sub 'arn:aws:s3:::${S3bucket}'
BufferingHints:
SizeInMBs: !Ref SizeInMBs
IntervalInSeconds: !Ref IntervalInSeconds
CompressionFormat: !Ref CompressionFormat
Prefix: !Sub ${LogGroupName01}/
RoleARN: !GetAtt 'DeliveryRole.Arn'
DeliveryStream02:
Type: AWS::KinesisFirehose::DeliveryStream
Properties:
DeliveryStreamName: !Sub '${LogGroupName02}-stream'
DeliveryStreamType: 'DirectPut'
S3DestinationConfiguration:
BucketARN: !Sub 'arn:aws:s3:::${S3bucket}'
BufferingHints:
SizeInMBs: !Ref SizeInMBs
IntervalInSeconds: !Ref IntervalInSeconds
CompressionFormat: !Ref CompressionFormat
Prefix: !Sub ${LogGroupName02}/
RoleARN: !GetAtt 'DeliveryRole.Arn'
#--------------------------------------------------#
# S3
#--------------------------------------------------#
S3bucket:
Type: AWS::S3::Bucket
DeletionPolicy: Delete
Properties:
BucketName: !Ref 'S3BucketName'
LifecycleConfiguration:
Rules:
- Id: !Sub 'ExpirationIn-${S3ExpireDate}Days'
Status: Enabled
ExpirationInDays: !Ref 'S3ExpireDate'
■ CFn作成時のポイント
Point1 : サブスクリプションフィルターの確認とディレクトリ
ロググループ単位でフィルタをかけるので、ロググループ単位でS3内にディレクトリが作成されます。
Cloudwatchのコンソールから、サブスクリプションフィルターがロググループにアタッチされていることを確認できます。
Point2 : プレフィックス
送信先のディレクトリ構成はYYYY/MM/DD/HH” (UTC)形式のプレフィックスが自動的作成されます。
AWS::KinesisFirehose::DeliveryStream
でPrefixを指定することでこれより上の階層のディレクトリを指定できます。今回はロググループ名をプレフィックスにしているのでLogGroupName/YYYY/MM/DD/HH/各ファイル
になっています。
Point3 : ファイルの形式
CWLからFirehoseに送信されるデータはgzipに圧縮されます。CompressionFormatでzipなど指定してしまうと、S3からダウンロードファイルが解凍したときに文字化けしてしまうので注意です。(パラメータで書かなくてもよかったな。。)
CloudWatch Logs から Amazon Kinesis Data Firehose に送信されたデータは、すでに gzip レベル 6 圧縮で圧縮されているため、Kinesis Data Firehose 配信ストリーム内で圧縮を使用する必要はありません。
CloudWatch Logs サブスクリプションフィルタの使用 - Amazon CloudWatch Logs
■ おわり
設定したバッファーの時間に応じてS3に出力されるタイミングは変わってきますが、ほぼリアルタイムでCloudwatch LogsのログをS3に転送することができました。