はじめに
前回の記事でAWS CLIコマンドを利用してCloudWatchAlermを設定する方法を調査・検証した。
せっかくなので、Alermと一緒に大量に設定する必要のあるCloudWatchLogsのロググループと
サブスクリプションフィルタをAWS CLIから設定する方法を調査・検証した。
環境
CloudShellからの実行を想定。記事作成時点のCloudShell上でのバージョンは以下の通り。
- AWS CLI:2.15.21
- jq:1.6(設定確認時に利用)
準備:SNS連携用Lambda作成
なんでもよいのでサブスクリプションフィルタの送信先となるLambda関数を作っておく。
今回はログ監視を想定し、サブスクリプションフィルタに指定した文字列を検知した場合、
メール整形用Lambdaへ連携、LmabdaからSNSのトピックを呼びメールを送信するLambda関数を用意する。
Lambda関数作成にあたり参考にさせていただいた記事
下記のコードを利用する場合は環境変数SNS_TOPIC_ARN
にメール通知先となるSNSのARNを設定すること。
また、LambdaのIAMロールにsns:Publish
の許可があること。
import json
import logging
import base64
import gzip
import os
from datetime import datetime, timezone, timedelta
import boto3
# logger初期化
logger = logging.getLogger()
logger.setLevel(logging.INFO)
# タイムスタンプ時刻変換
def convert_time(timestamp_unix):
# UNIXミリ秒から秒への変換
timestamp_sec = timestamp_unix / 1000
dt = datetime.fromtimestamp(timestamp_sec, timezone(timedelta(hours=9)))
# ISO:8601形式に変換
timestamp_jst = dt.isoformat()
return timestamp_jst
# SNSへの連携
def publish_sns(loggroup, logstream, logfiltername, logtimestamp, message):
sns_client = boto3.client('sns')
sns_topic_arn = os.environ['SNS_TOPIC_ARN']
sns_client.publish (
TopicArn = sns_topic_arn,
Subject = "【HOGEHOGEシステム】ログ監視アラーム",
Message =(
"========================================" + "\n" +
"ログ グループ名: " + loggroup + "\n" +
"ログ ストリーム名: " + logstream + "\n" +
"ログ フィルタ名: " + logfiltername + "\n" +
"ログ 検知日時(JST): " + logtimestamp + "\n" +
"ログ 内容: " + message + "\n" +
"========================================"
)
)
def lambda_handler(event, context):
# CWlogsサブスクリプションフィルタ連携データからJSONデータ取り出し
logger.info("LOAD Function: " + context.function_name)
decode_data = base64.b64decode(event['awslogs']['data'])
json_data = json.loads(gzip.decompress(decode_data))
# データ取得
log_group = json_data['logGroup']
log_stream = json_data['logStream']
log_filtername = json_data['subscriptionFilters'][0]
log_timestamp = json_data['logEvents'][0]['timestamp']
log_message = json_data['logEvents'][0]['message']
# タイムスタンプ時刻変換
log_timestamp = convert_time(log_timestamp)
# SNSへの連携
publish_sns(log_group, log_stream, log_filtername, log_timestamp, log_message)
logger.info("END Function: " + context.function_name)
設定コマンド
aws logs put-subscription-filter
を利用すればサブスクリプションフィルタを設定できる。
aws logs put-subscription-filter \
--log-group-name <ロググループ名> \
--filter-name <フィルタ名> \
--filter-pattern <フィルタパターン> \
--destination-arn <連携先>
注意点
GUI上からサブスクリプションフィルタを設定する場合はAWS側で自動的にやってくれるため意識しなかったが、
Lambda側のリソースポリシーでCloudWatchLogsからアクセスできるように設定しておかないと以下のエラーが出る。
An error occurred (InvalidParameterException) when calling the PutSubscriptionFilter operation: Could not execute the lambda function. Make sure you have given CloudWatch Logs permission to execute your function.
以下のようにCloudWatchLogsからのlambda:InvokeFunctionを許可しておくこと。
コマンドサンプル
aws logs put-subscription-filter \
--log-group-name "messages" \
--filter-name "gp-dev-log-messages" \
--filter-pattern "?\".err>\" ?\".crit>\" ?\".alert>\" ?\".emerg>\"" \
--destination-arn "arn:aws:lambda:ap-northeast-1:xxxxxxxxxx"
--filter-pattern
内でダブルクォーテーション使う場合はエスケープしないとエラーになる点は注意
動作確認
EC2から/var/log/messagesをロググループに出力しているため、
今回はloggerコマンドでsyslogにエラーを出力する。
AWS CLIからaws log put-log-events
でロググループにログを送信しても良い。
設定コマンド実行後GUI確認
メール受信確認
サブスクリプションフィルタ設定確認
aws logs describe-subscription-filters
を利用すればサブスクリプションフィルタ設定を取得できる。
オプションとして--log-group-name
の指定が必須となる。
CloudWatchAlermの時と違い、AWS CLIコマンド単体では全てのロググループと
それに紐づくサブスクリプションフィルタを一括で取得できないのは残念。
一気に取得する場合はロググループ名を読み込んでループさせる
シェルスクリプトを作る必要がありそう。
JSON形式で取得できるので、jqに渡して成形・出力する。
コマンドサンプル
aws logs describe-subscription-filters --log-group-name "messages" | jq -c '.subscriptionFilters[] | [.logGroupName, .filterName, .filterPattern, .destinationArn]'
実行結果サンプル
["messages","gp-dev-log-messages","?\".err>\" ?\".crit>\" ?\".alert>\" ?\".emerg>\"","arn:aws:lambda:ap-northeast-1:xxxxxxxxxx"]
以上。
CLIでの設定より連携先Lambdaの中身を書く方が大変でした。