こんにちは、ゲームソリューショングループのsoraです。
今回は、Compute OptimizerのレコメンデーションをEventBridge Scheduler+Lambdaで定期通知してみたことについて書いていきます。
構成
EventBridge Schedulerで指定した間隔でLambdaを呼び出して、Compute Optimizerのレコメンデーションを取得する構成です。
取得した後は、SNS経由でSlack通知するようにします。
取得する情報について、EC2で過剰なプロビジョニングとなっているサーバに関してのサマリーを取得します。
AWSマネジメントコンソールで表示されているようなサーバごとの推奨事項を通知しようと思ったのですが、単純に取得できずに処理が複雑化しそうだったため、サマリーとしています。
サーバごとの推奨事項を取得するには、CSVエクスポートを絡めればおそらく可能だと思います。
Terraformでの作成
Terraformを使って作成します。
Compute Optimizerのチェック結果はバージニア北部から取得する必要があります。
main.tf
terraform {
#AWSプロバイダーのバージョン指定
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.51.0"
}
}
}
#AWSプロバイダーの定義
provider aws {
# Compute Optimizerのチェック結果はバージニア北部から取得する必要がある
region = "us-east-1"
}
#AWSアカウントIDの取得
data aws_caller_identity current {}
#============ IAMロール作成 start ============
##EventBridge Sheduler
data aws_iam_policy_document events_assume_role {
statement {
effect = "Allow"
principals {
type = "Service"
identifiers = ["scheduler.amazonaws.com"]
}
actions = ["sts:AssumeRole"]
}
}
resource aws_iam_role iam_for_events {
name = "EventBridge_ComputeOptimizer_Notification_Role"
assume_role_policy = data.aws_iam_policy_document.events_assume_role.json
managed_policy_arns = ["arn:aws:iam::aws:policy/service-role/AWSLambdaRole"]
}
##Lambda(Compute Optimizerからの情報取得用)
data aws_iam_policy_document lambda_assume_role {
statement {
effect = "Allow"
principals {
type = "Service"
identifiers = ["lambda.amazonaws.com"]
}
actions = ["sts:AssumeRole"]
}
}
resource aws_iam_role iam_for_lambda {
name = "Lambda_ComputeOptimizer_Notification_Role"
assume_role_policy = data.aws_iam_policy_document.lambda_assume_role.json
inline_policy {
name = "my_inline_policy"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = ["sns:Publish"]
Effect = "Allow"
Resource = "*"
},
]
})
}
managed_policy_arns = [
"arn:aws:iam::aws:policy/ComputeOptimizerReadOnlyAccess"
]
}
#============ IAMロール作成 end ============
#============ リソース作成 start ============
##SNS(トピック)
###トピックの作成
resource aws_sns_topic compute_optimizer_topic {
name = "ComputeOptimizer-notification-topic"
}
data aws_iam_policy_document sns_assume_role {
statement {
effect = "Allow"
principals {
type = "Service"
identifiers = ["lambda.amazonaws.com"]
}
actions = ["sns:Publish"]
resources = [aws_sns_topic.compute_optimizer_topic.arn]
}
}
resource aws_sns_topic_policy sns_topic_policy {
arn = aws_sns_topic.compute_optimizer_topic.arn
policy = data.aws_iam_policy_document.sns_assume_role.json
}
##Lambda(Compute Optimizerからの情報取得用)
data archive_file lambda_computeoptimizer {
type = "zip"
source_file = "lambda/lambda_computeoptimizer.py"
output_path = "lambda_computeoptimizer.zip"
}
resource aws_lambda_function lambda_computeoptimizer {
filename = "lambda_computeoptimizer.zip"
function_name = "compute-optimizer-notification"
role = aws_iam_role.iam_for_lambda.arn
handler = "lambda_computeoptimizer.lambda_handler"
source_code_hash = data.archive_file.lambda_computeoptimizer.output_base64sha256
runtime = "python3.9"
environment {
variables = {
SNSTopicArn = aws_sns_topic.compute_optimizer_topic.arn,
AccountId = data.aws_caller_identity.current.account_id
}
}
}
##Lambda(Slack通知用)
data archive_file lambda_slack {
type = "zip"
source_file = "lambda/lambda_slack.py"
output_path = "lambda_slack.zip"
}
resource aws_lambda_function lambda_slack {
filename = "lambda_slack.zip"
function_name = "compute-optimizer-to-slack"
role = aws_iam_role.iam_for_lambda.arn
handler = "lambda_slack.lambda_handler"
source_code_hash = data.archive_file.lambda_slack.output_base64sha256
runtime = "python3.9"
#slack通知先
environment {
variables = {
HookURL = "[SlackのWebHookURL]"
ChannelName = "[チャンネル名]"
}
}
}
resource aws_lambda_permission lambda_slack_permission {
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.lambda_slack.function_name
principal = "sns.amazonaws.com"
source_arn = aws_sns_topic.compute_optimizer_topic.arn
}
##EventBridge Sheduler
resource aws_scheduler_schedule eventbridge {
name = "compute-optimizer-notification-schedule"
description = "Compute Optimizer Notification"
flexible_time_window {
mode = "OFF"
}
schedule_expression = "cron(0 0 1 * ? *)"
schedule_expression_timezone = "Asia/Tokyo"
target {
arn = aws_lambda_function.lambda_computeoptimizer.arn
role_arn = aws_iam_role.iam_for_events.arn
}
}
##SNS(サブスクリプション)
resource aws_sns_topic_subscription slack_subscription {
topic_arn = aws_sns_topic.compute_optimizer_topic.arn
protocol = "lambda"
endpoint = aws_lambda_function.lambda_slack.arn
}
Lambdaコード
今回の構成では、Compute Optimizerから情報取得する用とSlack通知用の2つがあります。
Compute Optimizerから取得する用のLambda(Python)で使っているSDKについては、以下をご参照ください。
ComputeOptimizer — Boto3 Docs 1.26.87 documentation
lambda_computeoptimizer.py
import boto3
import json
import os
AccountID = os.environ['AccountId']
SNSTopicArn = os.environ['SNSTopicArn']
def lambda_handler(event, context):
# Compute Optimizerから推奨事項を取得
# クライアント作成
client = boto3.client('compute-optimizer')
# リクエスト発信
response = client.get_recommendation_summaries(
accountIds=[AccountID],
nextToken="",
maxResults=10
)
# メッセージ生成に必要な情報の抽出
message = []
message.append("Compute OptimizerのEC2に関するコスト最適化の推奨事項を通知")
# メッセージ作成
for flag in response['recommendationSummaries']:
if flag['recommendationResourceType'] == "Ec2Instance":
for flag_summary in flag['summaries']:
if flag_summary['name'] == "OVER_PROVISIONED":
message.append("EC2インスタンス")
message.append(f"ステータス: {flag_summary['name']} ,アカウントID: {flag['accountId']} 推定削減額: {flag_summary['value']}")
# メッセージをSNSに送信
# 通知を送信するSNSトピックのARNを指定
sns_client = boto3.client('sns')
sns_topic_arn = SNSTopicArn
sns_client.publish(
TopicArn=sns_topic_arn,
Message=json.dumps(message, indent=3, ensure_ascii=False)
)
Slack通知用のLambdaは割愛します。
実行結果
EventBridge Schedulerで指定したタイミングで通知がきました。
推定削減額は0ですが、正常に動作して通知できています。
最後に
今回は、Compute OptimizerのレコメンデーションをEventBridge Scheduler+Lambdaで定期通知してみたことを記事にしました。
どなたかの参考になると幸いです。