CloudWatch Logs を定期的にS3 へ転送する

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」を押します。

  • Destination に「S3」を選択します。

  • 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."}]}

 

対処方法

  • ログの形式、サブスクリプションフィルターのパターンを指定せず、デフォルトの値を使用後にログの転送が始まりました。ログの形式、サブスクリプションフィルターのパターンを見直す必要があります。

 

 

参考資料

 

AWS,CloudWatch,Kinesis,S3

Posted by takaaki