mcpField
A
aws-cost-optimization

aws-cost-optimization

github.com/bagelholeクラウド・DevOps1.2k320 回インストール23 週間前に更新
リポジトリ

aws-cost-optimization について

AWS ワークロードを管理するチーム向けの実践的な FinOps スキルです。AWS CLI と Terraform の例を使い、支出分析、リソースの適正化、割引、アラート、不要リソースの整理を扱います。

主な機能

  1. 01サービスとタグ別の支出分析
  2. 02コンピュートとストレージの適正化
  3. 03Savings Plans と RIs の比較
  4. 04予算とアラートの設定
  5. 05未使用リソースの特定

活用シーン

  1. 01AWS 請求額の急増原因を調査
  2. 02負荷テスト後のリソースサイズ確認
  3. 03予算とコスト異常アラートの設定
  4. 04未接続ボリュームと古いスナップショットの整理

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/bagelhole/devops-security-agent-skills --skill aws-cost-optimization

インストール後、Claude Code で/aws-cost-optimizationを入力して読み込まれたか確認します。

詳細

AWS コスト最適化

信頼性やパフォーマンスを損なうことなく AWS の支出を削減するため、実践的な FinOps の管理策を適用します。

このスキルを使用するタイミング

  • AWS の月額料金が予想外に急増した、または予算のしきい値を超えた場合
  • エンジニアリングチームや財務チームとコストレビューを準備する場合
  • 負荷テスト後に EC2、RDS、EKS、Lambda のワークロードを適正規模に調整する場合
  • Savings Plans、Reserved Instances、オンデマンド料金のいずれを選ぶか検討する場合
  • 予算アラートと異常検出を自動化する場合
  • 使用していないリソース(未接続の EBS、アイドル状態のロードバランサー、古いスナップショット)を整理する場合
  • リージョンや AZ 間のデータ転送料を最適化する場合

前提条件

  • AWS CLI v2 がインストールされ、設定済みであること(aws configure)
  • IAM 権限:ce:*、budgets:*、ec2:Describe*、cloudwatch:PutMetricAlarm、s3:PutLifecycleConfiguration
  • AWS 請求コンソールで Cost Explorer が有効になっていること(データが反映されるまで 24 時間かかります)
  • Billing コンソールでコスト配分タグが有効になっていること

コストレビューワークフロー

  1. すべてのリソースに、チーム、サービス、環境、コストセンターを示すタグを付けます。
  2. Cost Explorer を有効にし、Cost and Usage Reports(CUR)を有効化して S3 に保存します。
  3. サービス、アカウント、タグ別に、支出の大きい項目を特定します。
  4. CloudWatch のメトリクスに基づいて、使用率の低いコンピューティングリソースとストレージのサイズを適正化します。
  5. 安定したベースライン使用量に対して、コミットメント割引(Savings Plans または RI)を適用します。
  6. 予算と異常アラートを設定し、KPI ダッシュボードを構築します。
  7. 毎月レビューして改善を繰り返します。

Cost Explorer CLI コマンド

# Get cost and usage for the last 30 days grouped by service
aws ce get-cost-and-usage \
  --time-period Start=2026-02-01,End=2026-03-01 \
  --granularity MONTHLY \
  --metrics "BlendedCost" "UnblendedCost" "UsageQuantity" \
  --group-by Type=DIMENSION,Key=SERVICE

# Get cost forecast for the next 30 days
aws ce get-cost-forecast \
  --time-period Start=2026-03-24,End=2026-04-24 \
  --metric UNBLENDED_COST \
  --granularity MONTHLY

# Get cost grouped by a specific tag (e.g., team)
aws ce get-cost-and-usage \
  --time-period Start=2026-02-01,End=2026-03-01 \
  --granularity MONTHLY \
  --metrics "UnblendedCost" \
  --group-by Type=TAG,Key=team

# Get rightsizing recommendations for EC2
aws ce get-rightsizing-recommendation \
  --service "AmazonEC2" \
  --configuration '{"RecommendationTarget":"SAME_INSTANCE_FAMILY","BenefitsConsidered":true}'

# Get Savings Plans purchase recommendation
aws ce get-savings-plans-purchase-recommendation \
  --savings-plans-type COMPUTE_SP \
  --term-in-years ONE_YEAR \
  --payment-option NO_UPFRONT \
  --lookback-period-in-days SIXTY_DAYS

# Get Savings Plans utilization
aws ce get-savings-plans-utilization \
  --time-period Start=2026-02-01,End=2026-03-01 \
  --granularity MONTHLY

# Get Reserved Instance utilization
aws ce get-reservation-utilization \
  --time-period Start=2026-02-01,End=2026-03-01 \
  --granularity MONTHLY

予算アラート

# Create a monthly cost budget with email alert at 80% and 100%
aws budgets create-budget \
  --account-id 123456789012 \
  --budget '{
    "BudgetName": "monthly-total",
    "BudgetLimit": {"Amount": "5000", "Unit": "USD"},
    "TimeUnit": "MONTHLY",
    "BudgetType": "COST",
    "CostFilters": {},
    "CostTypes": {
      "IncludeTax": true,
      "IncludeSubscription": true,
      "UseBlended": false
    }
  }' \
  --notifications-with-subscribers '[
    {
      "Notification": {
        "NotificationType": "ACTUAL",
        "ComparisonOperator": "GREATER_THAN",
        "Threshold": 80,
        "ThresholdType": "PERCENTAGE"
      },
      "Subscribers": [{"SubscriptionType": "EMAIL", "Address": "[email protected]"}]
    },
    {
      "Notification": {
        "NotificationType": "ACTUAL",
        "ComparisonOperator": "GREATER_THAN",
        "Threshold": 100,
        "ThresholdType": "PERCENTAGE"
      },
      "Subscribers": [{"SubscriptionType": "EMAIL", "Address": "[email protected]"}]
    }
  ]'

# List all budgets
aws budgets describe-budgets --account-id 123456789012

# Enable Cost Anomaly Detection monitor for all services
aws ce create-anomaly-monitor \
  --anomaly-monitor '{
    "MonitorName": "all-services",
    "MonitorType": "DIMENSIONAL",
    "MonitorDimension": "SERVICE"
  }'

# Create anomaly subscription (alert when impact > $50)
aws ce create-anomaly-subscription \
  --anomaly-subscription '{
    "SubscriptionName": "cost-alerts",
    "MonitorArnList": ["arn:aws:ce::123456789012:anomalymonitor/monitor-id"],
    "Subscribers": [{"Type": "EMAIL", "Address": "[email protected]"}],
    "Threshold": 50,
    "Frequency": "DAILY"
  }'

CloudWatch コストアラーム

# Create alarm for estimated charges exceeding $4000
aws cloudwatch put-metric-alarm \
  --alarm-name "billing-alarm-4000" \
  --alarm-description "Alert when estimated charges exceed $4000" \
  --metric-name EstimatedCharges \
  --namespace AWS/Billing \
  --statistic Maximum \
  --period 21600 \
  --threshold 4000 \
  --comparison-operator GreaterThanThreshold \
  --evaluation-periods 1 \
  --dimensions Name=Currency,Value=USD \
  --alarm-actions "arn:aws:sns:us-east-1:123456789012:billing-alerts" \
  --treat-missing-data notBreaching

未使用リソースの検出と整理

# List unattached EBS volumes (wasted storage spend)
aws ec2 describe-volumes \
  --filters Name=status,Values=available \
  --query "Volumes[].{ID:VolumeId,Size:Size,Created:CreateTime}" \
  --output table

# Find old EBS snapshots (older than 90 days)
aws ec2 describe-snapshots \
  --owner-ids self \
  --query "Snapshots[?StartTime<='2025-12-24'].{ID:SnapshotId,Size:VolumeSize,Date:StartTime}" \
  --output table

# List unused Elastic IPs (charged when not associated)
aws ec2 describe-addresses \
  --query "Addresses[?AssociationId==null].{IP:PublicIp,AllocId:AllocationId}" \
  --output table

# Find idle load balancers (zero healthy targets)
aws elbv2 describe-target-health \
  --target-group-arn arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/my-tg/abc123

# List RDS instances and their utilization
aws cloudwatch get-metric-statistics \
  --namespace AWS/RDS \
  --metric-name CPUUtilization \
  --dimensions Name=DBInstanceIdentifier,Value=mydb \
  --start-time 2026-03-17T00:00:00Z \
  --end-time 2026-03-24T00:00:00Z \
  --period 86400 \
  --statistics Average

S3 ライフサイクルのコスト最適化

# Apply tiered lifecycle policy to reduce storage costs
aws s3api put-bucket-lifecycle-configuration \
  --bucket my-data-bucket \
  --lifecycle-configuration '{
    "Rules": [
      {
        "ID": "TierDownOldData",
        "Status": "Enabled",
        "Filter": {"Prefix": ""},
        "Transitions": [
          {"Days": 30, "StorageClass": "STANDARD_IA"},
          {"Days": 90, "StorageClass": "GLACIER"},
          {"Days": 365, "StorageClass": "DEEP_ARCHIVE"}
        ],
        "NoncurrentVersionTransitions": [
          {"NoncurrentDays": 30, "StorageClass": "GLACIER"}
        ],
        "NoncurrentVersionExpiration": {"NoncurrentDays": 90}
      },
      {
        "ID": "CleanupIncompleteUploads",
        "Status": "Enabled",
        "Filter": {"Prefix": ""},
        "AbortIncompleteMultipartUpload": {"DaysAfterInitiation": 7}
      }
    ]
  }'

Terraform の予算とアラームの例

resource "aws_budgets_budget" "monthly" {
  name         = "monthly-total"
  budget_type  = "COST"
  limit_amount = "5000"
  limit_unit   = "USD"
  time_unit    = "MONTHLY"

  notification {
    comparison_operator        = "GREATER_THAN"
    threshold                  = 80
    threshold_type             = "PERCENTAGE"
    notification_type          = "ACTUAL"
    subscriber_email_addresses = ["[email protected]"]
  }

  notification {
    comparison_operator        = "GREATER_THAN"
    threshold                  = 100
    threshold_type             = "PERCENTAGE"
    notification_type          = "ACTUAL"
    subscriber_email_addresses = ["[email protected]"]
  }
}

resource "aws_cloudwatch_metric_alarm" "billing" {
  alarm_name          = "billing-alarm-4000"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = 1
  metric_name         = "EstimatedCharges"
  namespace           = "AWS/Billing"
  period              = 21600
  statistic           = "Maximum"
  threshold           = 4000
  alarm_description   = "Billing exceeds $4000"
  alarm_actions       = [aws_sns_topic.billing_alerts.arn]

  dimensions = {
    Currency = "USD"
  }
}

非本番環境のシャットダウンのスケジュール設定

# Stop all dev instances tagged Environment=dev (run via EventBridge + Lambda)
aws ec2 describe-instances \
  --filters "Name=tag:Environment,Values=dev" "Name=instance-state-name,Values=running" \
  --query "Reservations[].Instances[].InstanceId" \
  --output text | xargs -n1 aws ec2 stop-instances --instance-ids

# Scale down dev ECS services to zero at night
aws ecs update-service \
  --cluster dev-cluster \
  --service dev-api \
  --desired-count 0

トラブルシューティング

問題原因対処方法
Cost Explorer にデータが表示されないCE が有効になっていない、または有効化から 24 時間未満Billing コンソールで有効にし、24 時間待ちます
予算アラートが発火しないSNS サブスクリプションが確認されていないメールを確認し、サブスクリプションを承認します
適正化の推奨事項が表示されない使用状況データが不足している十分なメトリクスが得られるまで 14 日間待ちます
Savings Plans の利用率が低い購入しすぎた、またはワークロードが変わったSP のカバレッジを確認して調整します
未接続の EBS が表示されない問い合わせたリージョンが違うすべての有効なリージョンを順に確認します
請求アラームが一度も発火しない請求メトリクスは us-east-1 でのみ利用可能us-east-1 リージョンにアラームを作成します
CUR データが S3 にないレポートが設定されていない、またはバケットポリシーに誤りがあるBilling コンソールで CUR の設定を確認します
タグベースのコスト配分が空タグが有効化されていないBilling でコスト配分タグを有効にします

関連スキル

  • aws-ec2 - EC2 の運用、サイズ調整、Spot インスタンス
  • aws-s3 - S3 ストレージクラスとライフサイクル管理
  • aws-rds - RDS インスタンスのサイズ調整とリザーブドインスタンス
  • aws-lambda - Lambda の料金と同時実行数の調整
  • terraform-aws - IaC でコストガードレールをコード化

コメント