#🔷 はじめに
AWSの各種プロダクトのログは、CloudWatchに出力する事ができます。
そして、CloudWatchに出力したログは、手動でS3にエクスポートする事もできます。
しかし、S3に自動でログローテーションする機能はありません。(2018年12月時点)
そこで、今回は、ログローテーションをLambdaで実現したいと思います。
#🔷 作業手順
今回は、CloudWatchのログを日次処理でS3にエクスポートする処理を行う、Lambda関数を作成します。
作業手順は大きく3つになります。
- S3のバケットを作成する。
- CloudWatch LogsのログをS3にエクスポートする処理を行う、Lambda関数を作成する。
- CloudWatch Eventsで、Lambda関数が日次処理で実行するように設定する。
#🔷 実装
##🔶 S3のバケット作成
S3は「バケット」と呼ばれる空間にデータを格納します。
まずは、ログの格納先となるバケットを作成します。
Amazon S3
https://s3.console.aws.amazon.com/s3/home
1. S3の[バケットを作成する] ボタンをクリックします。
必要項目を入力します。
⬛バケット名
・任意のバケット名を入力します。
・ただし、この名称がそのままURLなるので、他と重複しない名前にする必要があります。
・今回は、「test-logs-2-0-1-8」とします。
⬛リージョン
・CloudWatchのログと同じリージョンを選択する必要があります。
・今回は米国東部(バージニア北部)
を選択します。
2. バケットが作成されたら、バケットに対する権限を設定します。
対象バケットを開き、[アクセス権限]→[バケットポリシー]をクリックします。
バケットポリシーはJSON形式で記述します。
以下の内容を貼り付けます。
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "s3:GetBucketAcl",
"Effect": "Allow",
"Resource": "arn:aws:s3:::test-logs-2-0-1-8",
"Principal": { "Service": "logs.us-east-1.amazonaws.com" }
},
{
"Action": "s3:PutObject" ,
"Effect": "Allow",
"Resource": "arn:aws:s3:::test-logs-2-0-1-8/log-okiba/*",
"Condition": { "StringEquals": { "s3:x-amz-acl": "bucket-owner-full-control" } },
"Principal": { "Service": "logs.us-east-1.amazonaws.com" }
}
]
}
簡単に説明すると、バケットに以下の権限を付与しています。
- 「test-logs-2-0-1-8」バケットにアクセスする権限(s3:GetBucketAcl)
- 「test-logs-2-0-1-8」バケットの「log-okiba」プレフィックス(フォルダ)に、ファイルを書き込む権限(s3:PutObject)
- バケット利用対象サービスは、CloudWatch Logs
3. 作成したバケットの設定が上手くいっているか、実際にログをエクスポートして確認します。
CloudWatch Logsでロググループを選択して、[アクション]→[データをS3にエクスポート]をクリックします。
⬛開始日
・エクスポートしたいログの範囲の、開始日を設定します。
⬛終了日
・エクスポートしたいログの範囲の、終了日を設定します。
⬛バケット名
・エクスポート先のバケット名を設定します。
・今回は作成した「test-logs-2-0-1-8」バケットを設定します。
⬛バケットプレフィックス
・エクスポート先のバケットの、プレフィックス名を設定します。
・今回は権限設定でarn:aws:s3:::test-logs-2-0-1-8/log-okiba/*
と設定したので、「log-okiba」とします。
・もしarn:aws:s3:::test-logs-2-0-1-8/*
と設定していたら、記載しなくても大丈夫でした。
入力したら[エクスポートの開始]ボタンをクリックします。
4. S3で「test-logs-2-0-1-8」バケットの中を確認します。
「log-okiba」プレフィックスが作られています。さらにその中にはログがエクスポートされています。
確認できたら、バケットの中身は削除しましょう。
5. ログの保持期間の設定。
ログローテーションで毎日ログをエクスポートするので、ログの保持期間は短くします。
CloudWatch Logsで、対象のロググループのイベント失効期間をクリックします。
保持期間を変更します。
⬛保持期間
・任意の保持期間を選択します。
・今回は毎日エクスポートするので、3日間
にします。
[OK]ボタンをクリックしたら、CloudWatch Logsの設定は完了です。
##🔶 Lambda関数の作成
先程、CloudWatchのログをS3にエクスポートする処理を手動で行いましたが、同じ事を自動で行うLambda関数を作成します。
AWS Lambda
https://console.aws.amazon.com/lambda/home
1. [関数の作成]ボタンをクリックして、作成を開始します。
2. 今回は、「一から作成」でLambda関数を作成します。
⬛名前
・任意の名称を入力します。
・今回は「exportLogTest」とします。
⬛ランタイム
・今回はNode.js 8.10
を選択します。
⬛ロール
・LambdaにS3やCloudWatchを操作する権限を設定します。
・そのような権限のみが設定されたロールは都合よく無いので、今回はロールを作成します。
・カスタムロールの作成
を選択します。
カスタムロールの作成
を選択すると、ロール作成画面が開きます。
⬛IAMロール
・新しいIAMロールの作成
を選択します。
⬛ロール名
・任意の名称を入力します。
・今回は「exportLogTestRole」とします。
権限の内容はポリシードキュメントにJSON形式で書きます。編集リンクをクリックします。
デフォルトは、以下のようにLambdaからCloudWatch Logsにログを出力するための、3つの権限のみが設定されています。
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
ここに、以下のようなログをエクスポートする権限と、S3バケットにアクセスして、書き込む権限を追加します。
- logs:CreateExportTask
- s3:GetBucketAcl
- s3:PutObject
よってポリシードキュメントは以下のように編集します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:CreateExportTask",
"s3:GetBucketAcl",
"s3:PutObject"
],
"Resource": "*"
}
]
}
編集したら、[許可]ボタンをクリックします。
Lambda関数の作成画面に戻るので、[関数の作成]ボタンをクリックします。
3. Lambda関数のコードを書きます。
作成したLambda関数に、CloudWatch LogsとS3への権限が設定されていれば下のように、表示されます。
index.jsに、ログをバケットにエクスポートする処理を書きます。
'use strict';
const aws = require('aws-sdk');
aws.config.update({region: 'us-east-1'}); // バージニア北部にグローバルリージョンを指定
/**
* メイン処理
* @param event : 呼び出し元サービスから渡される値
* @param context : AWS側の各種情報
* @param callback : 呼び出し元サービスへ返す値
*
*/
exports.handler = (event, context, callback) => {
const cloudwatchlogs = new aws.CloudWatchLogs();
let params;
let getToTime = [];
getToTime = getTimeData();
/*
"destination"で設定したS3バケットに、"destinationPrefix"で設定したフォルダを作り、
"logGroupName"で設定したCludWatchのロググループのlogをexportする。
exportするlogの範囲は、"from"から"to"で設定した範囲。
*/
params = {
'destination': process.env.BucketName,
'from': getToTime[0],
'to': getToTime[1],
'logGroupName': process.env.LogGroupName,
'destinationPrefix': `${process.env.DestinationPrefix}/${getToTime[2]}`,
'taskName': `${process.env.TaskName}/${getToTime[2]}`
};
console.log(JSON.stringify(params));
/* logのexport処理 */
cloudwatchlogs.createExportTask(params, (err, data) => {
let response;
if (err) {
console.log(err, err.stack);
response = err.stack;
} else {
console.log(data);
response = data;
}
callback(null, response);
});
};
/**
* ログ取得の開始日時、終了日時、日付フォーマットを返す。
* ログデータは、エクスポートできるようになるまで最大 12時間かかる場合があるので、
* 1日前のログを取得するように日時を調整。
* @return arr : [from, to, format]
*
*/
function getTimeData() {
let arr=[];
let now = new Date();
now.setTime(now.getTime() + 1000*60*60*9);// JSTに変換
let yyyy,mm,dd;
console.log(now);
/* 開始日:fromをarrにセット */
now.setDate(now.getDate()-2);
now.setHours(0);
now.setMinutes(0);
now.setSeconds(0);
now.setMilliseconds(0);
arr.push(now.getTime());
console.log(`ログ取得from:${now.getFullYear()}/${now.getMonth() + 1}/${now.getDate()}`);
/* 終了日:toをarrにセット */
now.setDate(now.getDate()+1);
arr.push(now.getTime());
console.log(`ログ取得to:${now.getFullYear()}/${now.getMonth() + 1}/${now.getDate()}`);
yyyy = now.getFullYear();
mm = ('0' + (now.getMonth() + 1)).slice(-2);
dd = ('0' + now.getDate()).slice(-2);
arr.push(`${yyyy}-${mm}-${dd}`);
return arr;
}
キモとなる処理は、createExportTask
に、param
でまとめたdestination
やlogGroupName
等の必要パラメータを渡して、ログのエクスポートを実行しているところです。(参考:createExportTask)
getTimeData
でログの取り込み範囲を2日前〜1日前にしていますが、これはドキュメントに、
ログデータは、エクスポートできるようになるまで最大 12時間かかる場合があります。
と書かれているためです。(参考:Amazon S3 へのログデータのエクスポート
)
params
で渡している必要パラメータは、バケットの変更等に対応しやすいように、直接ソースに書かずに、process.env.○○
で環境変数に設定しています。
(必要なければ、設定しないでソースの中に直接書き込んでOKです。)
⬛環境変数
-
BucketName
: test-logs-2-0-1-8 -
DestinationPrefix
: log-okiba -
LogGroupName
: hello-api-logs -
TaskName
: export_task
また、エクスポート処理は数秒では完了しない可能性が高いので、タイムアウトする時間も延長します。
⬛タイムアウト
・Lambda関数を実行した時の、タイムアウトまでの時間。
・今回は1分とします。
##🔶 日次実行ルールの作成。
1. Lambda関数を実行するトリガを設定します。
LambdaのDesignerメニューの中から、CloudWatch Events
をクリックします。
すると、下図のようにトリガとしてCloudWatch Events
がセットされるので、さらにセットされたアイコンをクリックします。
「トリガーの設定」メニューが表示されます。
⬛ルール
・新規ルールの作成
を選択します。
ルールの詳細を設定します。
⬛ルール名
・任意の名称をセットします。
・今回はdaily_exe_test_rule
とします。
⬛ルールの説明
・分かりやすい説明を入力します。
・今回は、毎日、日本時間の01:05にLambda関数を実行するルールを作るので、その旨を記載します。
⬛ルールタイプ
・イベントパターン
は指定のAWSプロダクトで発生するイベントをトリガとするタイプです。
・今回は、毎日指定の時間をトリガにしたいので、スケジュール式
を選択します。
⬛スケジュール式
・実行ルールの式をセットします。
・今回は、毎日、日本時間の01:05にLambda関数を実行するルールを作るので、cron(5 16 * * ? *)
をセットします。(UTCの時差9時間を足した時間)
・式の書き方はルールのスケジュール式を参照。
最後に、[保存]ボタンをクリックして、Lambdaの設定は完了です。
##🔶 動作確認
正常に処理が実行できていれば、以下のように日毎にログがエクスポートされます。
##🔶 注意
手動やLambadaから、エクスポートを行った時に、「エクスポートタスク」が上がりますが、エクスポートタスクを複数立ち上げる事はできません。
ドキュメントにも以下のように書かれています。
アカウントごとに、一度に 1 つのアクティブ (実行中または保留中) のエクスポートタスクがあります。この制限は変更できません。
そのため、エクスポートの自動実行を複数設定している場合、他と実行のタイミングが被らないように、注意が必要です。
#🔷 参考
AWS CLI を使用した Amazon S3 へのログデータのエクスポート
https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/logs/S3ExportTasks.html
CloudWatch Logs に蓄積したログをS3に定期的にエクスポートする
https://honeybe.hatenablog.jp/entry/2018/03/14/114635