CloudWatchLogsをLambdaで自動的にS3へアップロードする1つの仕掛け


あなたはCloudWatchで収集したログを放置していないだろうか?

データは活用しないと意味がない。

ログはS3に集約しよう。

そうしてこそ、分析に活かせたり、運用も一元化できたり、、

何より注目したいのは保管料金だ。
より安価なアーカイブサービスのGlacierの保管も視野に入る。

■CloudWatchの保管料金
0.033 USD/GB

■S3の保管料金
0.025 USD/GB

■Glacierの保管料金
0.005 USD/GB

賢いあなたは1GBにつき、約3円の無駄金を許せないはずだ。

【要件】
毎日0時にLambda関数が起動し、CloudWatchロググループ「cloudwatch-logs-messages」の
前日0:00〜23:59のログをS3のバケット「mys3bucketname/test_prifix」にアップロードする

では、設定していこう。
(というかAWSさん、そろそろこういう機能リリースしてくれないかなー)

S3の設定

保存先のS3バケットにバケットポリシーを付与する

以下を自分の環境に置き換える
- バケット名(以下の例ではmys3bucketnameとしている)

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "logs.ap-northeast-1.amazonaws.com"
      },
      "Action": "s3:GetBucketAcl",
      "Resource": "arn:aws:s3:::mys3bucketname"
    },
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "logs.ap-northeast-1.amazonaws.com"
      },
      "Action": "s3:PutObject",
      "Resource": "arn:aws:s3:::mys3bucketname/*",
      "Condition": {
        "StringEquals": {
          "s3:x-amz-acl": "bucket-owner-full-control"
        }
      }
    }
  ]
}

Lambdaの作成

python3.6でLambda関数を一から作成する。
(ロールにはCloudWatchLogsの権限とS3の書き込み権限を付与して下さい)

例:S3FullとCloudwatchLogsFUllなど

Lambda関数を作成する。

以下を自分の環境に置き換える
  - Lambda関数名
  - 保存先S3バケット名
  - 取得するCloudWatchロググループ名
  - 保存先S3バケット名配下の任意のプリフィックス名(任意の名前で構わない)

lambda_function.py
import datetime
import time
import boto3

lambda_name = 'PutLogFromCWLtoS3Function'    #Lambda関数名
log_group_name = '/aws/lambda/' + lambda_name
s3_bucket_name = 'mys3bucketname'    #保存先S3バケット名
s3_prefix = lambda_name + '/%s' % (datetime.date.today() - datetime.timedelta(days = 1))

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      = 'cloudwatch-logs-messages',     #取得するCloudWatchロググループ名
        fromTime          = from_ts * 1000,
        to                = to_ts * 1000,
        destination       = 'mys3bucketname',     #保存先S3バケット名
        destinationPrefix = 'test_prifix'    #保存先S3バケット名配下の任意のサブフォルダ名(プリフィックス名)
    )
    return response

Lambda関数のテストは以下の形で「作成」を押下してOK

この関数をCloudWatchイベントに登録し、毎日0:00に起動するようにすればOK
(時刻はUTC基準なので気をつける。)

UTC + 9時間 = JST

0 15 * * ? *

CloudWatchイベントの登録方法は以下の記事を参照してみて欲しい。

朝7時にCloudWatchイベントからLambdaを介しAmazon Connectのコールスクリプトを発動し、
バーチャル彼女(女性の機会音声)に事前登録したスクリプトを読み上げさせるモーニングコールを
サーバーレスで設計したものだ。

AWSバーチャル彼女から毎朝モーニングコールで起こしてもらう1つのアーキテクト

おわりに

 ・バケット名、プリフィクス名をパラメーターで渡せば多数のロググループも対応可能と思われる。
 ・日次ではなく、1週間ごとにまとめてアップロードも可能。時間がかかりそうな際はLambdaの実行時間制約に気をつけて(デフォルト3秒となっているので、1分とかに延ばすこと)

ありがとうございました。
少しでもお役にたちましたら「いいね」をよろしくお願いします。