ENECHANGE Developer Blog

ENECHANGE開発者ブログ

AWSコスト監視システムを作ってみた

こんにちは、ENECHANGEの石橋です。

AWSのコストを管理することは、企業にとって非常に重要です。
本ブログでは、AWS Budgets, SNS, Chatbot, Event Bridge, Lambda, Slackを統合したコスト監視システムの開発について解説します。
TerraformやLambdaの実装例も提供しますので、ご参考になれば幸いです。

システム機能概要

  1. 全体および環境別の予算を設定し、超過時にアラートをSlack通知します。
  2. 設定した予算に対して月次コストレポートを作成し、Slack通知します。

システムアーキテクチャ概要

以下は、AWSコスト監視システムのアーキテクチャ図です:

主要コンポーネント

  1. AWS Budgets:

    • 全体および環境別の予算を設定。
    • 予算超過時に通知をトリガーします。
  2. Amazon SNS:

    • 通知のハブとして機能し、アラートをAWS Chatbotに転送します。
  3. AWS Chatbot:

    • アラートをリアルタイムでSlackチャンネルに送信します。
  4. AWS Event Bridge:

    • 月次の定期トリガーを設定し、Lambdaを起動することでレポートの自動化を実現します。
  5. AWS Lambda:

    • 月次コストレポートを処理し、AWS Cost Explorerにクエリを投げてコストの詳細を取得します。
  6. Slack:

    • 予算アラートや月次レポートの通知を一元化します。

実装の詳細

0. タグの設定

  • コスト監視したいサービスの全てにServiceタグをつけてください。
  • コスト監視したいサービスの全てにEnvironmentタグをつけてください。
    • 共通部分:Common
    • 個別部分:Production, Staging, Development 等

1. AWS Budgetsの設定

AWS Budgetsを使用してコストの閾値を設定します。 以下はServiceタグとEnvironmentタグでフィルタリングした実装例です。

AWS BudgetsのTerraformコード例

resource "aws_budgets_budget" "monthly_cost_budget_development" {
  name              = "Monthly-Cost-Budget-Common"
  budget_type       = "COST"
  limit_amount      = "1000" # USD
  limit_unit        = "USD"
  time_unit         = "MONTHLY"

  cost_filter {
    name = "TagKeyValue"
    values = [
      "user:Service$your-service-name",
      "user:Environment$development"
    ]
  }

  notification {
    comparison_operator = "GREATER_THAN"
    threshold           = 100
    threshold_type      = "PERCENTAGE"
    notification_type   = "FORECASTED"

    subscriber {
      address = aws_sns_topic.cost_alert_topic.arn
      type    = "SNS"
    }
  }
}

2. SNSとChatbotの統合

SNSは予算アラートをAWS Chatbotに送信し、それをSlackに転送します。

SNSのTerraformコード例

resource "aws_sns_topic" "cost_alert_topic" {
  name = "cost-alert-topic"
}

resource "aws_sns_topic_policy" "cost_alert_policy" {
  arn = aws_sns_topic.cost_alert_topic.arn

  policy = jsonencode({
    Version = "2008-10-17",
    Statement = [
      {
        Sid       = "AllowBudgetsToPublish",
        Effect    = "Allow",
        Principal = {
          Service = "budgets.amazonaws.com"
        },
        Action    = "SNS:Publish",
        Resource  = aws_sns_topic.cost_alert_topic.arn
      },
      {
        Sid       = "DefaultPermissions",
        Effect    = "Allow",
        Principal = {
          AWS = "*"
        },
        Action = [
          "SNS:GetTopicAttributes",
          "SNS:SetTopicAttributes",
          "SNS:AddPermission",
          "SNS:RemovePermission",
          "SNS:DeleteTopic",
          "SNS:Subscribe",
          "SNS:ListSubscriptionsByTopic",
          "SNS:Publish"
        ],
        Condition = {
          StringEquals = {
            "AWS:SourceOwner": data.aws_caller_identity.current.account_id
          }
        },
        Resource  = aws_sns_topic.cost_alert_topic.arn
    ]
  })
}

3. AWS Cost Explorerへのクエリ処理

月次で前月の詳細なコストデータを取得するために、Lambdaを使用してAWS Cost Explorerにクエリを投げます。

Cost ExplorerクエリのLambda関数例

import boto3
import datetime
import json
import requests

# Cost Explorerクライアント
ce = boto3.client('ce')

def lambda_handler(event, context):
    today = datetime.date.today()
    start_of_month = today.replace(day=1)
    end_of_month = today

    response = ce.get_cost_and_usage(
        TimePeriod={
            'Start': start_of_month.strftime('%Y-%m-%d'),
            'End': end_of_month.strftime('%Y-%m-%d')
        },
        Granularity='MONTHLY',
        Metrics=['UnblendedCost'],
        Filter={
            'And': [
                {
                    'Tags': {
                        'Key': 'Service',
                        'Values': 'your-service-name'
                    }
                },
               {
                   'Tags': {
                       'Key': 'Environment',
                       'Values': 'development'
                   }
               }
            ]
        }
    )

    dev_cost = float(response['ResultsByTime'][0]['Total'].get('UnblendedCost', {}).get('Amount', 0.0))

4. 月次レポートの自動化

月次コストレポートはEventBridgeを使用してトリガーされ、Slackに送信されます。

月次トリガーのEventBridgeルール

resource "aws_cloudwatch_event_rule" "monthly_trigger" {
  name                = "monthly-cost-report"
  schedule_expression = "cron(0 0 1 * ? *)" # 毎月1日に実行
}

resource "aws_cloudwatch_event_target" "lambda_target" {
  rule      = aws_cloudwatch_event_rule.monthly_trigger.name
  arn       = aws_lambda_function.report_lambda.arn
}

resource "aws_lambda_permission" "allow_cloudwatch_to_invoke_lambda" {
  statement_id  = "AllowExecutionFromCloudWatch"
  action        = "lambda:InvokeFunction"
  function_name = aws_lambda_function.report_lambda.function_name
  principal     = "events.amazonaws.com"
  source_arn    = aws_cloudwatch_event_rule.monthly_trigger.arn
}

Slack通知のLambda関数例

    url = "https://slack.com/api/chat.postMessage"
    headers = {
        "Authorization": f"Bearer your-slack-token",
        "Content-Type": "application/json"
    }
    payload = {
      "channel": "your-slack-channel",
      "text": f"development cost: {dev_cost}"
    }
    response = requests.post(url, headers=headers, json=payload)
    if not response.json().get("ok"):
        raise Exception(f"Slack API Error: {response.json()}")

今後の拡張

将来的には以下の機能を追加予定です:

  1. Cost Explorerレポート:

    • 詳細なレポートを生成してエクスポート。
  2. カスタムダッシュボード:

    • Quicksightや外部ツールと統合して高度な可視化を実現。

結論

AWS Budgets, SNS, Chatbot, Event Bridge, Lambda, Slackを統合したこのシステムにより、リアルタイムのコスト監視、及び月次レポートのSlack通知が可能になります。
このシステムをご参考にしていただき、AWSコスト管理の最適化にお役立てください。