Edited at

CloudWatch LogsにあるログをLambdaを使用してS3に転送する


このページはなに

表題通りとなりますが、Lambda関数を使用してCloudWatch LogsにあるログをS3に転送する仕組みを実装します。


参考にさせていただいたURL

https://qiita.com/blackriver/items/372f5e9c667a8a107f13


背景

AWSサービスの中でファイルの配置の料金が一番安いのはS3です。

なので、例えばCloudWatch Logsに格納しているログについては7日保管にして、S3には1年間保管のような仕組みをつくりたいと考えました。


仕組み(構成図)

構成図.png


作業手順


IAMロール(ポリシー)の作成

以下、IAMポリシーを作成してIAMロールにアタッチしてLambdaに紐づけてください。

(LambdaがS3バケットへの操作の権限が必要なため)

{

"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:ListBucket"     # S3バケット内のリスト表示に必要(最初に接続確認をするため)
],
"Resource": "arn:aws:s3:::S3バケット名"
},
{
"Effect": "Allow",
"Action": [
"s3:GetObject",     # S3バケットにログを配置する前にS3バケット内のファイルの存在を確認するための権限
"s3:PutObject"     # S3バケットにログを配置するための権限
],
"Resource": "arn:aws:s3:::S3バケット名/*"
}
]
}


S3バケットポリシーの設定

・吐き出す先のS3バケットポリシーに、以下のバケットポリシーを設定してください。

・補足:この設定は、CloudWatch LogsからS3バケットにログを配置するために必要なバケットポリシーになります。

 (CloudWatch LogsにIAMロールは紐づけられないので、S3のバケットポリシーで制御します。)

{

"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "logs.対象リージョン.amazonaws.com"
},
"Action": "s3:GetBucketAcl",     # CloudWatch Logsから S3バケットのアクセス(s3apiコール)制御のポリシー
"Resource": "arn:aws:s3:::S3バケット名"
},
{
"Effect": "Allow",
"Principal": {
"Service": "logs.対象リージョン.amazonaws.com"
},
"Action": "s3:PutObject",     # CloudWatch Logsから S3バケットへログ配置するためのS3バケットポリシー
"Resource": "arn:aws:s3:::S3バケット名/*",
"Condition": {
"StringEquals": {
"s3:x-amz-acl": "bucket-owner-full-control"
}
}
}
]
}


Lambda関数の作成

・使用言語は、python3。バージョンは、3.7です。


コード

コードは「参考にさせていただいたサイト」に記載してある内容とほぼ同じです。

コード内に記載されているコメントアウト部分を、環境ごとに適宜編集していく形になります。

import datetime

import time
import boto3

log_group_name = 'ロググループ名' #CloudWatchLogsのロググループ名
s3_bucket_name = 'S3バケット名' #保存先S3バケット名
s3_string_prefix = 'S3内のプレフィックス' #S3内のプレフィックス(文字列)を定義
s3_date_prefix = '/%s' % (datetime.date.today() - datetime.timedelta(days = 1)) #S3内のプレフィックス(日付)を定義

def get_from_timestamp():
today = datetime.date.today()
yesterday = datetime.datetime.combine(today - datetime.timedelta(days = 1), datetime.time(0, 0, 0))
timestamp = time.mktime(yesterday.timetuple())
return int(timestamp)

def get_to_timestamp(from_ts):
return from_ts + (60 * 60 * 24) - 1

def lambda_handler(event, context):
from_ts = get_from_timestamp()
to_ts = get_to_timestamp(from_ts)
print('Timestamp: from_ts %s, to_ts %s' % (from_ts, to_ts))

client = boto3.client('logs')
response = client.create_export_task(
logGroupName = log_group_name, #取得するCloudWatchロググループ名
fromTime = from_ts * 1000,
to = to_ts * 1000,
destination = s3_bucket_name, #保存先S3バケット名
destinationPrefix = s3_string_prefix + s3_date_prefix #保存先S3バケット名配下の任意のサブフォルダ名(プレフィックス名)
)
return response


CloudWatch Eventの作成

毎日AM4:00にイベント発生する場合、CloudWatch Eventに以下のcron設定をすればおkです。

(時間がUTC形式のため、UTCから+9時間することを忘れずに。)

0 19 * * ? *


悩んだこと

S3に吐き出したときのファイル名が「000000.gz」になってしまうので、ファイル名を変える方法を調べましたが、boto3のapiの仕様で「000000」で吐き出されてしまうようです(ファイル名を別ネームで保存したい場合は、別途仕組みを実装する必要あり)。


boto3_URL

https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/logs.html#CloudWatchLogs.Client.create_export_task