8月 4, 2021 AWS , CloudWatch , Kinesis , S3
概要
今回は、CloudWatch Logsのサブスクリプションフィルターを使用して、CloudWatch Logs のログを定期的にS3 へ転送します。
CloudWatch Logs のログをS3 へ転送する方法は、以前こちら で紹介したCloudWatch Logsのエクスポート機能を利用する方法もあります。しかし、エクスポート機能で実現する場合は、エクスポートのタスクを定期実行するために Lambdaが必要になること、そして、同時に複数のエクスポートタスクを実行できない仕様です。そのため、CloudWatch Logsのエクスポート機能に代わる方法が良いと思います。
また、コストの観点であれば、CloudWatch Logs のデータ取り込み(例: 東京リージョン 0.76USD/GB)、S3 のPUT(例: 東京リージョン 0.0047USD/1,000リクエスト)にそれぞれ料金が掛かります。もちろん、データ取り込み以外に、保存期間に応じた料金も必要です。よって、コストの観点だけであれば、CloudWatch Logs をS3 に転送するのではなく、ログ管理をCloudWatch Logs or S3 のどちらか一方に限定する方が得策です。ログ管理の要件、ログの転送・保存に掛かるコストを踏まえた上でご検討ください。
以下、今回ご紹介する部分の構成図です。
CloudWatch Logs をS3 に転送するサブスクリプションフィルターの構築
手引き
今回は、下記AWSドキュメントを参考に構築を行います。ドキュメントと異なる手順は、CloudWatch Logs サブスクリプションフィルターの作成方法をCLI を使用せず、コンソールを使用している点となります。以前は、Kinesis Data Firehoseの CloudWatch Logs サブスクリプションフィルターはコンソールから作成できませんでした。最近のアップデートによって、コンソールを利用して、Kinesis Data Firehoseの CloudWatch Logs サブスクリプションフィルターを作成できるように変わりました。
準備
サブスクリプションフィルターを設定するロググループを確認する
今回、S3 へ転送する対象の CloudWatch Logs ロググループは下記となります。画面の表示通り、現在こちらのロググループには、サブスクリプションフィルターは設定されていません。
ログの転送先となるS3 バケットを準備する
ログの転送先となるS3 を準備します。今回使用するバケット名は niikawa-test 、Prefixは log-archive/niikawa-test-lambda/ となります。
Kinesis Data Firehose 用のロールを作成する
S3 にデータを書くための権限をKinesis Data Firehose に付与するため、新規でロールを作成します。今回はAWSドキュメントに合わせて、ロールの作成にAWS CLI を使用します。
始めに、テキストエディタにて TrustPolicyForFirehose.json のjson ファイルを作成します。下記コードを利用する場合、「アカウントID 」を環境に合わせて置き換えます。
vi を利用する場合は、"set paste" を活用ください。
{
"Statement": {
"Effect": "Allow",
"Principal": { "Service": "firehose.amazonaws.com" },
"Action": "sts:AssumeRole",
"Condition": { "StringEquals": { "sts:ExternalId":"アカウントID" } }
}
}
以下のAWS CLI を実行し、Kinesis Data Firehose に付与するロールを作成します。
ロール名(例:FirehosetoS3Role)は、環境に合わせて変更下さい。
aws iam create-role --role-name FirehosetoS3Role --assume-role-policy-document file://TrustPolicyForFirehose.json
作成されたロールにポリシーを追加するための json を作成します。テキストエディタにて、Action を定義した PermissionsForFirehose.json のjson ファイルを作成します。下記コードを利用する場合、「バケット名 」を環境に合わせて置き換えます。
{
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:AbortMultipartUpload",
"s3:GetBucketLocation",
"s3:GetObject",
"s3:ListBucket",
"s3:ListBucketMultipartUploads",
"s3:PutObject" ],
"Resource": [
"arn:aws:s3:::バケット名",
"arn:aws:s3:::バケット名/*" ]
}
]
}
aws iam put-role-policy --role-name FirehosetoS3Role --policy-name Permissions-Policy-For-Firehose --policy-document file://PermissionsForFirehose.json
Kinesis Data Firehose を作成する
次に、Kinesis のコンソールを開き、「Kinesis Data Firehose」にて「Create Delivery Stream」を押します。
Delivery stream name を入力します。
Source に、「Direct PUT or other sources」を選択します。「Next」を押します。
ログのデータ変換を行う場合は、Data transformationにて「Enabled」を選択し、変換処理を行うためのLambda関数を選択します。データ変換を行わない場合は、「Disabled」を選択します。
Record format conversionにて「Disabled」を選択します。「Next」を押します。
S3 bucketに転送先のバケットを指定します。
S3 prefix およびS3 error prefix を指定します。「Next」を押します。
複数のサブスクリプションフィルターを設定する場合、ロググループ毎にS3 のフォルダが分かれた方が見やすいと思います。その様な場合は、ロググループ名が区別できる S3 prefix 良いでしょう。
Buffer size、Buffer interval はデフォルト(Buffer size:5 MiB、Buffer interval:300 seconds)でも良いです。必要に応じて、Buffer size、Buffer interval を変更します。
S3 compression に「Disabled」を選択します。CloudWatch Logs から Kinesis Data Firehose に送信されたデータは、すでに gzip(レベル6)で圧縮されているため、Kinesis Data Firehose 送信ストリーム内で圧縮する必要はありません。
Error logging は「Enabled」を選択します。
IAM role を選択します。前述の手順にて作成した「FirehosetoS3Role」を選択します。「Next」を押します。
Reviewにて問題なければ、「Create delivery stream」を押します。
Kinesis Data Firehose が作成されました。
CloudWatch Logs 用のロールを作成する
次は、Kinesis Data Firehose にデータを書くための権限を CloudWatch Logs に付与するため、新規でロールを作成します。今回はAWSドキュメントに合わせて、ロールの作成にAWS CLI を使用します。
始めに、テキストエディタにて TrustPolicyForCWL.json のjson ファイルを作成します。下記コードを利用する場合、リージョン名の「ap-northeast-1」 を環境に合わせて置き換えます。
vi を利用する場合は、"set paste" を活用ください。
{
"Statement": {
"Effect": "Allow",
"Principal": { "Service": "logs.ap-northeast-1.amazonaws.com" },
"Action": "sts:AssumeRole"
}
}
以下のAWS CLI を実行し、CloudWatch Logs に付与するロールを作成します。
ロール名(例:CWLtoKinesisRole)は、環境に合わせて変更下さい。
aws iam create-role --role-name CWLtoKinesisRole --assume-role-policy-document file://TrustPolicyForCWL.json
作成されたロールにポリシーを追加するためのjson を作成します。テキストエディタにて、Action を定義した PermissionsForCWL.json のjson ファイルを作成します。下記コードを利用する場合、「アカウントID 」を環境に合わせて置き換えます。
{
"Statement":[
{
"Effect":"Allow",
"Action":["firehose:*"],
"Resource":["arn:aws:firehose:ap-northeast-1:アカウントID:*"]
}
]
}
aws iam put-role-policy --role-name CWLtoKinesisRole --policy-name Permissions-Policy-For-CWL --policy-document file://PermissionsForCWL.json
サブスクリプションフィルターを設定する
最後に、対象のロググループにサブスクリプションフィルターを設定します。
対象のロググループにて、「サブスクリプションフィルター」を選択し、「作成」→「Create Kinesis Firehose subscription filter」を押します。
先ず、Destination account を選択および Kinesis Firehose delivery stream にKinesis Data Firehose のリソース名を指定します。
次に、Select an existing role に前述の手順にて作成した「CWLtoKinesisRole」を選択します。
次に、ログの形式、サブスクリプションフィルターのパターン、サブスクリプションフィルター名を指定します。
最後に、「ストリーミングを開始」を押します。
ロググループの一覧からも 1つのサブスクリプションフィルターが設定されていることが確認できます。
ログの転送先となるS3 に、ログファイルが転送されていることを確認します。ファイル名に拡張子は付いていませんが、gz圧縮されています。ご注意ください。
サブスクリプションフィルターのトラブルシューティング
ストリーミングを開始できない
症状
サブスクリプションフィルター設定において、「ストリーミングを開始」を押すと以下のエラーが発生する。
以下は、コンソールから実行した場合のエラーメッセージです。
Could not deliver test message to specified Firehose stream. Check if the given Firehose stream is in ACTIVE state.
以下は、AWS CLI から実行した場合のエラーメッセージです。
An error occurred (InvalidParameterException) when calling the PutSubscriptionFilter operation: Could not deliver test message to specified Firehose stream. Check if the given Firehose stream is in ACTIVE state.
対処方法
私の経験では、CloudWatch Logs に付与するロールの権限定義に誤りがありました。ポリシーの記述を見直し後、「ストリーミングを開始」が成功しました。
S3 にログが転送されない
症状
「ストリーミングを開始」が成功したが、ログの転送先となるS3 に下記1行のみ出力され、他のログが記録されない。
{"messageType":"CONTROL_MESSAGE","owner":"CloudwatchLogs","logGroup":"","logStream":"","subscriptionFilters":[],"logEvents":[{"id":"","timestamp":1627966598067,"message":"CWL CONTROL MESSAGE: Checking health of destination Firehose."}]}
対処方法
ログの形式、サブスクリプションフィルターのパターンを指定せず、デフォルトの値を使用後にログの転送が始まりました。ログの形式、サブスクリプションフィルターのパターンを見直す必要があります。
参考資料