個人的備忘録
1. 概要
-
設定内容
-
Amazon API Gateway のアクセスログを出力する設定
- API名: hogehoge
- CloudWatch Logs ロググループ名: /aws/apigateway/hogehoge-accesslog
-
アクセスログのステータスコード監視設定 - 方法1
- CloudWatch Logs メトリックフィルター + CloudWatch メトリックアラーム + Amazon SNS (AWS Chatbot) + Slack
-
アクセスログのステータスコード監視設定 - 方法2
- Lambda + CloudWatch イベント (Amazon EventBridge) + Slack
- [参考] https://qiita.com/chii-08/items/e20651e7912596e9a556
-
監視方法
-
方法1
- 監視間隔
- 1分おき
- 監視ワード
- status = 4* || status = 5*
- Slackへの通知条件
- 上記監視ワードにマッチしたアクセスログのカウント数が閾値を超えたときと、下回ったとき
(閾値を超えている間はSlackへの通知はない)
- 上記監視ワードにマッチしたアクセスログのカウント数が閾値を超えたときと、下回ったとき
- Slackへの通知内容
- カウント数
- 監視間隔
-
方法2
- 監視間隔
- 5分おき
- 監視ワード
- status >= 400
- Slackへの通知条件
- 直近5分前のアクセスログが上記監視ワードにマッチしたとき
- Slackへの通知内容
- マッチしたアクセスログの行
- 監視間隔
-
監視例
- 方法1で第一報と収束を通知、方法2でエラー中の実際のログを1分毎に通知する
2. 設定
2.1. API Gateway アクセスログ出力
[参考] https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/http-api-logging.html
- API Gateway が CloudWatch Logs ロググループへアクセスログを書き込むことを許可するIAMロールを作成
- API Gateway のアクセスログを記録する CloudWatch Logs ロググループ を作成
- API Gateway のアクセスログ出力を有効にする設定
1. API Gateway が CloudWatch Logs ロググループへアクセスログを書き込むことを許可するIAMロールを作成
- IAMロール を作成する terraform サンプル
data "aws_iam_policy_document" "apigw-assume-role-policy" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["apigateway.amazonaws.com"]
}
}
}
resource "aws_iam_role" "role_AmazonAPIGatewayPushToCloudWatchLogs" {
name = "AmazonAPIGatewayPushToCloudWatchLogs"
assume_role_policy = data.aws_iam_policy_document.apigw-assume-role-policy.json
path = "/service-role/"
}
resource "aws_iam_role_policy_attachment" "AmazonAPIGatewayPushToCloudWatchLogs" {
role = aws_iam_role.role_AmazonAPIGatewayPushToCloudWatchLogs.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs"
}
2. API Gateway のアクセスログを記録する CloudWatch Logs ロググループ を作成
- ロググループ を作成する terraform サンプル
resource "aws_cloudwatch_log_group" "apigateway-hogehoge-accesslog" {
name = "/aws/apigateway/hogehoge-accesslog"
retention_in_days = "90"
}
3. API Gateway のアクセスログ出力を有効にする設定
[参考]https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/set-up-logging.html
-
AWS マネジメントコンソール
- 「Access Log Destination ARN」は、 2. で作成したロググループの ARN を設定
- ログの形式は JSON を選択
-
アクセスログ例 (JSON)
{
"requestId": "※※※※※※※※-※※※※-※※※※-※※※※-※※※※※※※※※※※※",
"ip": "***.***.***.***",
"caller": "-",
"user": "-",
"requestTime": "18/Nov/2020:05:28:56 +0000",
"httpMethod": "POST",
"resourcePath": "/v1",
"status": "403",
"protocol": "HTTP/1.1",
"responseLength": "42"
}
2.2. アクセスログのステータスコード監視設定 - 方法1
- ディレクトリ構成
└── apigateway-accesslog-check.tf
- CloudWatch Logs メトリックフィルター を作成する terraform のサンプル
resource "aws_cloudwatch_log_metric_filter" "APIGateway_hogehoge_accesslog_4xx5xxError" {
name = "APIGateway_hogehoge_accesslog_4xx5xxError"
pattern = "{ ($.status = 4*) || ($.status = 5*) }"
log_group_name = aws_cloudwatch_log_group.apigateway-hogehoge-accesslog.name
metric_transformation {
name = "APIGateway_hogehoge_accesslog_4xx5xxError"
namespace = "LogMetrics"
value = "1"
}
}
- CloudWatch メトリックアラーム を作成する terraform のサンプル
resource "aws_cloudwatch_metric_alarm" "APIGateway_hogehoge_accesslog_4xx5xxError" {
alarm_name = "APIGateway hogehoge accesslog 4xx5xxError"
threshold = "1"
alarm_description = ""
alarm_actions = [※※※※※]
ok_actions = [※※※※※]
comparison_operator = "GreaterThanOrEqualToThreshold"
namespace = "LogMetrics"
metric_name = "APIGateway_hogehoge_accesslog_4xx5xxError"
period = "60"
evaluation_periods = "1"
statistic = "Sum"
treat_missing_data = "notBreaching"
}
2.3. アクセスログのステータスコード監視設定 - 方法2
- ディレクトリ構成
├── apigateway-accesslog-check.tf
└── source_code
└── apigateway-accesslog-check
└── main.py
- Lambda にアタッチする IAM ロール を作成する terraform のサンプル
data "aws_iam_policy_document" "lambda-assume-role-policy" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["lambda.amazonaws.com"]
}
}
}
resource "aws_iam_role" "role_lambda-apigateway-accesslog-check" {
name = "lambda-apigateway-accesslog-check"
assume_role_policy = data.aws_iam_policy_document.lambda-assume-role-policy.json
path = "/service-role/"
}
resource "aws_iam_role_policy_attachment" "lambda-apigateway-accesslog-check_CloudWatchLogsReadOnlyAccess" {
role = aws_iam_role.role_lambda-apigateway-accesslog-check.name
policy_arn = "arn:aws:iam::aws:policy/CloudWatchLogsReadOnlyAccess"
}
resource "aws_iam_role_policy_attachment" "lambda-apigateway-accesslog-check_AWSLambdaBasicExecutionRole" {
role = aws_iam_role.role_lambda-apigateway-accesslog-check.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}
- Lambda を作成する terraform のサンプル
data "archive_file" "apigateway-accesslog-check" {
type = "zip"
source_dir = "./source_code/apigateway-accesslog-check"
output_path = "./source_code/apigateway-accesslog-check.zip"
}
resource "aws_lambda_function" "apigateway-accesslog-check_hogehoge" {
filename = data.archive_file.apigateway-accesslog-check.output_path
function_name = "apigateway-accesslog-check_hogehoge"
role = aws_iam_role.role_lambda-apigateway-accesslog-check.arn
handler = "main.lambda_handler"
source_code_hash = data.archive_file.apigateway-accesslog-check.output_base64sha256
runtime = "python3.8"
memory_size = 128
timeout = 300
environment {
variables = {
LOGGROUP_NAMES = "/aws/apigateway/hogehoge-accesslog"
SLACK_CHANNEL = "#※※※※※"
SLACK_TEXT = "Slack通知テスト"
SLACK_USER_NAME = "名無し"
SLACK_ICON_EMOJI = ":fearful:"
SLACK_COLOR = "warning"
SLACK_WEBHOOK_URL = "https://hooks.slack.com/services/※※※※※/※※※※※/※※※※※"
}
}
}
import os
import datetime
import boto3
client = boto3.client('logs')
import time
import ast
import json
from urllib.request import Request, urlopen
from urllib.error import URLError, HTTPError
logGroupName = os.environ['LOGGROUP_NAMES']
slackWebhookURL = os.environ['SLACK_WEBHOOK_URL']
slackChannel = os.environ['SLACK_CHANNEL']
slackUserNmae = os.environ['SLACK_USER_NAME']
slackText = os.environ['SLACK_TEXT']
slackColor = os.environ['SLACK_COLOR']
slackIconEmoji = os.environ['SLACK_ICON_EMOJI']
def lambda_handler(event, context):
five_minutes_ago = datetime.datetime.now() - datetime.timedelta(minutes = 5)
startTime = five_minutes_ago.replace(second = 0, microsecond = 0)
endTime = startTime + datetime.timedelta(minutes = 5) - datetime.timedelta(milliseconds = 1)
queryString = 'fields @timestamp, @message | filter (status >= 400)'
error_logs = results(logGroupName, startTime, endTime, queryString)
if len(error_logs) > 0:
for error_log in error_logs:
dict_message = ast.literal_eval(error_log[1]['value'])
text = str(logGroupName) + '\n' + str(dict_message)
print(text)
post_slack(text)
def results(logGroupName, startTime, endTime, queryString):
start_query_res = client.start_query(
logGroupName=logGroupName,
startTime=int(startTime.timestamp()),
endTime=int(endTime.timestamp()),
queryString=queryString
)
queryId = start_query_res['queryId']
get_results_res = client.get_query_results(
queryId=queryId
)
while get_results_res['status'] == 'Running':
time.sleep(5)
get_results_res = client.get_query_results(
queryId=queryId
)
return get_results_res['results']
def post_slack(text):
print(slackWebhookURL)
slack_message = {
'channel': slackChannel,
'username': slackUserNmae,
'text': slackText,
'icon_emoji': slackIconEmoji,
'attachments': [
{
"color": slackColor,
"text": text
}
]
}
req = Request(slackWebhookURL, json.dumps(slack_message).encode('utf-8'))
response = urlopen(req)
response.read()
- CloudWatch イベントルール を作成する terraform のサンプル
resource "aws_cloudwatch_event_rule" "apigateway-accesslog-check" {
name = "alarm_apigateway-accesslog-check"
is_enabled = true
schedule_expression = "rate(5 minutes)"
}
resource "aws_cloudwatch_event_target" "apigateway-accesslog-check_hogehoge" {
rule = aws_cloudwatch_event_rule.apigateway-accesslog-check.name
target_id = "apigateway-accesslog-check_hogehoge"
arn = aws_lambda_function.apigateway-accesslog-check_hogehoge.arn
}
※ terraform で CloudWatch イベントルール を作成した場合、スケジュールが来ても lambda が実行されない時がある。
※ そのときは、AWSマネジメントコンソールから 対象のイベントルールを無編集で更新する。