はじめに
先日先人が作り上げた、偉大なログエクスポートを実行するStepFunctionsがコケていました。
先人ほどのスキルもないので、CloudShellを利用してCloudWatchLogsからS3へエクスポートするシェルスクリプトを作成したので記事として残します。
StepFunctionsの根本対応が必要ですが、まずは一時的な対応としてシェルスクリプトを作成。
マネジメントコンソールからGUI操作も可能ですが、対象となるCloudWatchLogsの量が多いことや、アクセスキーが払い出されていないことから、本番環境のCloudShellで作業実施を決めました。
参考:
AWS ドキュメント「AWS CLI を使用してログデータを Amazon S3 にエクスポートする」
構成図
構築
1.S3バケットの準備
実機ではStepFunctionsから毎晩ログが対象のS3バケットにエクスポートされているため設定は不要。
ただし検証するために新規作成しているため、新たに作成したS3バケットに、以下バケットポリシー
を追記する。
※東京リージョンを想定
※arn:aws:s3:::[YOUR-BUCKET-NAME]
部分は、ログが転送されるS3バケット名を入力する
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "logs.ap-northeast-1.amazonaws.com"
},
"Action": "s3:GetBucketAcl",
"Resource": "arn:aws:s3:::[YOUR-BUCKET-NAME]"
},
{
"Effect": "Allow",
"Principal": {
"Service": "logs.ap-northeast-1.amazonaws.com"
},
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::[YOUR-BUCKET-NAME]/*",
"Condition": {
"StringEquals": {
"s3:x-amz-acl": "bucket-owner-full-control"
}
}
}
]
}
2.CloudWatch LogsからS3バケットにエクスポートするシェルスクリプト
2.1.転送するCloudWatchLogsのリストファイルを作成
S3へ転送したい CloudWatchLogsのロググループをテキストファイル形式で作成して保存
※下記log.txt
の内容にS3に転送したいロググループ名を記載する
【例】log.txt
/aws/lambda/cfn-sf-inamura-LambdaHumanApprovalSendEmailFunctio-wPwtEbDmKEg3
/aws/lambda/cfn-stepfunctions-inamura-LambdaHumanApprovalSendE-3j0qFdWkOpat
/aws/lambda/cfn-stepfunctions-inamura-LambdaHumanApprovalSendE-D92dYTTBLAmy
/aws/lambda/cfn-stepfunctions-inamura-LambdaHumanApprovalSendE-IbgVOxScNeUI
/aws/lambda/cfn-stepfunctions-inamura-LambdaHumanApprovalSendE-UTH7yskJ5b79
2.2.CloudWatch LogsからS3バケットにエクスポートするシェルスクリプトを作成
【シェルスクリプト】bash export_log_groups_to_s3.sh
#!/bin/bash
#エラーが発生した場合停止する
set -e
#引数$1:ログリストパス
#引数$2:保存先S3バケット名の記載がない場合はエラー表示
if [ -z "$1" ] || [ -z "$2" ]; then
echo "Please provide both a log group name file and a bucket name as arguments."
exit 1
fi
log_group_name_file=$1
s3_bucket_name=$2
#検索開始・終了時間
start_time=$(($(date -d "2020/01/01" +%s)*1000))
end_time=$(($(date -d "2023/05/01" +%s)*1000))
#ログリストを一行ずつ実行
while IFS= read -r log_group_name
do
#空行になるまで繰り返す
if [ -z "$log_group_name" ]; then
continue
fi
#タスクネーム作成
task_name="ExportTask-$(date +%Y%m%d%H%M%S)"
#ロググループ名の一文字めの"/"を削除するr
prefix=${log_group_name#/}
#エクスポートタスクを作成しタスクIDを取得
task_id=$(aws logs create-export-task --task-name "${task_name}" --log-group-name "${log_group_name}" --from ${start_time} --to ${end_time} --destination "${s3_bucket_name}" --destination-prefix "${prefix}" --query 'taskId' --output text)
#エクスポートタスク作成が失敗した場合、どのロググループで失敗したか表示
if [ $? -ne 0 ]; then
echo "Failed to create export task for log group ${log_group_name}"
exit 1
fi
#エクスポートタスクのステータスを取得
while true; do
status=$(aws logs describe-export-tasks --task-id "${task_id}" --query 'exportTasks[0].status.code' --output text)
#エクスポートステータスが”COMPLETED”なら、次のタスクを実行するためループ終了
if [[ "${status}" == "COMPLETED" ]]; then
echo "Export task for log group ${log_group_name} completed successfully."
break
fi
#エクスポートタスクが”FAILED”なら、どのロググループで失敗したか表示して終了
if [[ "${status}" == "FAILED" ]]; then
echo "Export task for log group ${log_group_name} failed."
exit 1
fi
#エクスポートタスクが"RUNNING","PENDING"なら30秒sleep後、再実行
if [[ "${status}" == "RUNNING" ]] || [[ "${status}" == "PENDING" ]]; then
echo "Export task for log group ${log_group_name} wait please."
sleep 30
fi
done
done < "$log_group_name_file"
挙動の確認
1.CloudShellで実行
$1引数:S3へ出力したいログリストのテキストファイル
$2引数:出力先とするS3バケット
$ bash export_log_groups_to_s3.sh "log.txt" "20220214-inamura"
2.実行結果の確認
2.1.CloudShell画面での実行結果確認
何度かS3バケットに送れないということで「wait please」が出ましたが、30秒待機して再トライして送信ができてることがターミナル画面からわかる
2.2.実行後のS3バケット画面での確認
2.3.CloudWatchLogsの画面での確認
2.3.1.「Amazon S3へのすべてのエクスポートを表示」画面へ遷移
CloudWatch > ロググループ > アクション > Amazon S3へのすべてのエクスポートを表示 を押下
2.3.2.エクスポートタスクでの確認
エクスポートタスクが完了しており、ステータスが「Completed successfully」となっていることを確認
さいごに
ログ転送中は同時に2つの処理を実行できないことは新たな学びでした。
さて一時対応が済んだので、StepFuncitonsの根本解決に着手していこうと思います。
※利用したS3バケットは検証終了したため削除されています。