コンテンツにスキップ

Building an Automated Cloud Cost Optimization and CI/CD Pipeline on AWS

チェック

  • [ ] 本文を確認した
  • [ ] 概要を確認した
  • [ ] タグを確認した
  • [ ] inbox/ 直下へ移行した

概要

AWS 上で、アイドルリソース検出、停止アクション、レポート保存、AI サマリ、CI/CD を組み合わせたクラウドコスト最適化パイプラインを作る記事。 Terraform で S3、DynamoDB、IAM、SNS、CloudWatch を用意し、Lambda で scanner、action、ai_insights を実装する。 GitHub Actions、Docker、ECR、Terraform workspace を使い、staging/production 環境へデプロイする流れまで扱う。

本文

記事の構成はフェーズ分けされている。 最初に Terraform で基盤を作り、その後 Lambda を実装し、Docker/ECR でパッケージ化し、GitHub Actions で CI/CD へつなげる。

全体アーキテクチャ

目的は、クラウド上の無駄なリソースを自動検出し、必要に応じて停止し、結果を保存し、通知や AI サマリを作ること。

主要コンポーネントは次の通り。

  • Terraform: AWS リソース定義。
  • Lambda scanner: アイドルリソースや未使用リソースを検出。
  • Lambda action: 停止やタグ付けなどのアクションを実行。
  • Lambda ai_insights: レポートを読み、要約や通知を行う。
  • S3: レポート、ログ、AI サマリの保存先。
  • DynamoDB: 検出履歴やアクション履歴の保存先。
  • SNS: 通知。
  • CloudWatch: ダッシュボードやスケジュール実行。
  • GitHub Actions: テスト、Docker build、ECR push、Terraform deploy。

Terraform の基盤

Terraform では、環境ごとに staging/production を分ける。 記事では Terraform workspace を使う。

cd terraform
terraform init
terraform workspace new staging
terraform workspace new production
terraform apply -var="environment=staging"

remote state は S3 に置き、DynamoDB で state lock を取る構成。 複数人や CI/CD で Terraform を実行するなら、remote state と lock は必須に近い。

Terraform で作るものは、レポート用 S3 bucket、DynamoDB table、Lambda 用 IAM role/policy、SNS topic、CloudWatch dashboard placeholder など。 環境名をリソース名に含め、staging と production が混ざらないようにする。

例として、staging なら次のようなリソースが作られる。

  • cloud-cost-optimizer-staging-reports
  • コスト検出履歴用 DynamoDB table
  • Lambda 実行 role
  • 通知用 SNS topic

scanner Lambda

scanner は、AWS リソースを調べ、コスト削減候補を findings として出す。 記事の例では EC2 と S3 が対象。

EC2 では、実行中インスタンスを列挙し、CPU 使用率が低いものを idle candidate として扱う。 S3 では、空の bucket などを検出対象にする。

検出結果は DynamoDB と S3 に保存する。 Lambda の環境変数には、DynamoDB table、report bucket、environment を渡す。

RESOURCE_TABLE
REPORT_BUCKET
ENVIRONMENT

戻り値には findings count を含める。

action Lambda

action Lambda は、検出されたリソースに対して停止などの処理を行う。 記事の例では、EC2 instance を stop し、アクション履歴を DynamoDB と S3 に保存する。

実運用では、いきなり停止するのではなく、タグ、承認、対象除外、スケジュール、所有者通知などを入れる必要がある。 ただし記事のサンプルでは、仕組みを理解するために action Lambda が直接停止処理を行う構成になっている。

ai_insights Lambda

ai_insights Lambda は、最新レポートを S3 から読み、要約を作成し、S3 の ai_summaries などへ保存し、SNS へ通知する。 記事タイトルには Bedrock も含まれており、AI によるコスト最適化サマリや推奨事項を作る位置づけ。

AI 要約は、人間が大量の findings を読む負担を減らす。 ただし、停止判断そのものを AI に任せる場合はリスクが高いため、承認フローや対象除外ルールと組み合わせる必要がある。

Lambda のテスト

記事では moto を使った Python Lambda のユニットテストが紹介されている。 AWS 実環境へアクセスせず、DynamoDB や S3 を mock して Lambda handler をテストする。

from moto import mock_aws
import boto3
import json
import os
from importlib import reload
import lambdas.scanner.main as scanner_main

@mock_aws
def test_lambda():
    os.environ["RESOURCE_TABLE"] = "test-table"
    os.environ["REPORT_BUCKET"] = "test-reports"
    os.environ["ENVIRONMENT"] = "staging"

    reload(scanner_main)
    from lambdas.scanner.main import lambda_handler

    dynamodb = boto3.client("dynamodb", region_name="us-east-1")
    s3 = boto3.client("s3", region_name="us-east-1")

    s3.create_bucket(Bucket="test-reports")
    dynamodb.create_table(
        TableName="test-table",
        KeySchema=[{"AttributeName": "resource_id", "KeyType": "HASH"}],
        AttributeDefinitions=[{"AttributeName": "resource_id", "AttributeType": "S"}],
        BillingMode="PAY_PER_REQUEST",
    )

    result = lambda_handler({}, {})
    assert result["statusCode"] == 200
    assert "findings" in json.loads(result["body"])

テストは次のように実行する。

python -m pytest -v

Docker と ECR

Lambda は container image として package する。 Python 3.12 の Lambda base image を使う Dockerfile は次のような形。

FROM public.ecr.aws/lambda/python:3.12

COPY main.py ${LAMBDA_TASK_ROOT}
COPY requirements.txt ./
RUN pip install -r requirements.txt --target "${LAMBDA_TASK_ROOT}"

CMD ["main.lambda_handler"]

build、login、ECR repository 作成、tag、push の流れは次の通り。

docker build -t scanner-lambda ./lambdas/scanner

aws ecr get-login-password --region us-east-1 \
  | docker login --username AWS --password-stdin <account-id>.dkr.ecr.us-east-1.amazonaws.com

aws ecr create-repository --repository-name scanner-lambda

docker tag scanner-lambda:latest \
  <account-id>.dkr.ecr.us-east-1.amazonaws.com/scanner-lambda:latest

docker push <account-id>.dkr.ecr.us-east-1.amazonaws.com/scanner-lambda:latest

Terraform では、Lambda function の package_typeImage にし、ECR image URI を指定する。 timeout は 300 秒、環境変数として table、bucket、environment を渡す。

記事では、PowerShell の pipe まわりや、Docker BuildKit の manifest が Lambda と合わない問題にも触れている。 BuildKit 由来の問題が出る場合は、環境変数で無効化する。

$env:DOCKER_BUILDKIT=0

GitHub Actions の CI/CD

CI では、Pull Request で lint、unit test、Docker build を行い、必要に応じて ECR push まで行う。 CD では、main branch への merge 後に Terraform apply を実行し、最新 image を Lambda に反映する。

GitHub Actions では、AWS credentials を設定し、ECR login を行い、Docker image を build/push し、Terraform を実行する。 大まかな流れは次のようになる。

name: ci

on:
  pull_request:

jobs:
  test-and-build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-region: us-east-1
      - uses: aws-actions/amazon-ecr-login@v2
      - run: python -m pytest -v
      - run: docker build -t scanner-lambda ./lambdas/scanner

本番適用では、workspace、環境変数、承認、OIDC、最小権限 IAM を整える必要がある。

読み替え

この記事は、クラウドコスト最適化の「仕組み作り」を学ぶサンプルとして使える。 ただし、実運用で自動停止を行うには、所有者確認、タグ運用、承認フロー、除外リスト、営業時間、依存関係、監査ログを必ず設計する。

AI 要約は便利だが、コスト削減アクションの最終判断をいきなり AI に任せるのは危険。 まずは検出とレポートから始め、通知、承認、自動化の順に広げるのが安全。

要点

  • Terraform で S3、DynamoDB、IAM、SNS、CloudWatch、Lambda を管理する。
  • scanner Lambda はアイドル EC2 や空 S3 bucket などを findings として検出する。
  • action Lambda は停止などの処理を行い、履歴を保存する。
  • ai_insights Lambda はレポートを要約し、SNS などで通知する。
  • Lambda は Docker image として ECR に push できる。
  • moto を使うと AWS リソースを mock して Lambda をテストできる。
  • GitHub Actions で test、build、push、Terraform apply をつなげる。
  • 実運用では、承認、除外、監査、最小権限を必ず設計する。

タグ

aws #lambda #terraform #ci-cd #cost-optimization