EBS gp2→gp3で月40万円浮かせた。実測メトリクスで見つけた地雷と対策

月100万円のEBS費用を棚卸ししてわかった真実。gp2からgp3に移行して20%削減、さらにスナップショット戦略で追加削減。実装で踏んだ地雷も全部話します。

gp2が月80万円を食ってた話

うちのチームは去年の秋、AWS費用の棚卸しをしてて気づいたんです。EBSだけで月100万円超。本番環境でgp2が大量に残ってるのに気付いて、「あ、これやばい」って。

正直、gp2から移行するまで後回しにしてたんですよ。既存リソースの設定変更だし、パフォーマンスに影響ないだろうって甘く見てたんですよ。でも実測してみたら、gp3で20%コスト下げて、IOPS・スループット性能も上げられる。これやらない手はないと。

2026年時点では、gp3はすでに標準になってるし、AWSコンソールでもgp3推奨になってます。ただ、実際に移行するとなると結構手間。スナップショット戦略もセットで見直さないと、古いスナップショットで無駄が溜まったまんまなんですよね。

EBS gp2→gp3移行の現実的なプロセス

うちがやった流れはシンプルですが、落とし穴がいくつかありました。

大事なのは、gp3に単に変更するだけじゃなく、IOPS・スループット設定を「本来の使用量に合わせる」ことなんです。デフォルトのgp3は3,000 IOPS / 125 MB/sで出荷されますが、ぶっちゃけこれ過剰な場合がほとんど。

うちの本番DBはgp2で1TB、IOPS最大化してました。これをgp3に変更する際、実際のメトリクスをCloudWatchで3週間見て、ピーク時で400〜500 IOPSだってわかった。スループットは5MB/s程度。このレベルだと、gp3のデフォルトより下げられる。

// CloudWatch メトリクスから取得した実測値
{
  "VolumeReadOps": {
    "peak": 520,
    "average": 180,
    "percentile_95": 450
  },
  "VolumeWriteOps": {
    "peak": 480,
    "average": 200,
    "percentile_95": 420
  },
  "VolumeReadBytes": {
    "peak_throughput_mbps": 6.2
  }
}

実測値をもとに、うちは以下の設定で進めました。

ボリュームタイプサイズIOPSスループット用途
本番DB用1TB以上3,000125 MB/sデフォルトのまま運用
小ぶりな環境500GB未満1,00050 MB/sコスト最適化重視
検索エンジン用-6,000200 MB/sパフォーマンス優先

実装のポイント: EC2をスナップショット→新gp3ボリュームから起動するやり方もあるし、EBS Elasticで直接変更する方法もあります。2026年ならEBS Elasticが安定してるので、本番環境に影響を最小化できるんですよね。

# AWS CLIでgp2をgp3に変更(実際に使った例)
aws ec2 modify-volume \
  --volume-id vol-1234567890abcdef0 \
  --volume-type gp3 \
  --iops 3000 \
  --throughput 125

# 進捗確認
aws ec2 describe-volumes-modifications \
  --volume-ids vol-1234567890abcdef0

スナップショット管理で月40万円浮かせた話

ここが地味だけど一番効果でかかったんです。スナップショット戦略を整理する前、うちはスナップショットが「自動で増殖」してました。

毎日深夜0時と12時に自動取得、そのまま30日分保持。これが100ボリューム以上あるから、月間で数千個のスナップショットが溜まる。古いのも新しいのも一律に保存してる状態だった。S3コスト最適化と同じですよ。古いデータをずっと保有するのは金を捨ててるのと一緒。

実装した3段階スナップショット戦略は、こんな感じです。

ホットスナップショット(直近7日) 毎日取得して、RPO を4時間程度に設定。ストレージはStandardで、緊急復旧が必要になったときのために確保。

ウォームスナップショット(8〜30日) 週1回のみ取得(木曜夜中)で、RPOは3日。古いスナップショットを圧縮して保持し、監査・コンプライアンス対応に使う感じです。

コールドスナップショット(31日〜1年) 月1回のみ取得(月初)で、手動管理。RPOは30日なので、アーカイブや法務要件への対応用ですね。

# Lambda で実装したスナップショット自動削除(実際のコード)
import boto3
from datetime import datetime, timedelta

ec2 = boto3.client('ec2')

def lambda_handler(event, context):
    # 7日以上前のスナップショットを削除
    retention_days = 7
    cutoff_date = datetime.utcnow() - timedelta(days=retention_days)
    
    response = ec2.describe_snapshots(
        OwnerIds=['self'],
        Filters=[
            {'Name': 'tag:ManagedBy', 'Values': ['auto-snapshot']}
        ]
    )
    
    for snapshot in response['Snapshots']:
        snapshot_time = snapshot['StartTime'].replace(tzinfo=None)
        
        if snapshot_time < cutoff_date:
            try:
                ec2.delete_snapshot(SnapshotId=snapshot['SnapshotId'])
                print(f"Deleted: {snapshot['SnapshotId']} (created: {snapshot_time})")
            except Exception as e:
                print(f"Failed to delete {snapshot['SnapshotId']}: {str(e)}")
    
    return {'statusCode': 200, 'deleted_count': len([s for s in response['Snapshots'] if s['StartTime'].replace(tzinfo=None) < cutoff_date])}  

正直、古いスナップショットほとんど使わないんですよ。「念のため」で保持してたけど、実際に復旧が必要になったときは直近のやつ。だから段階的に削除基準を厳しくしても問題ないわけです。

AWS構成図: EBS最適化の全体像

subgraph AWS_Region["AWS リージョン"]
    subgraph VPC["VPC"]
        subgraph AZ_A["AZ-A"]
            EC2_A["EC2インスタンス<br/>タイプ: t3.xlarge"]
            EBS_A1["EBS gp3<br/>1TB<br/>3000 IOPS<br/>$52/月"]
            EBS_A2["EBS gp3<br/>500GB<br/>1000 IOPS<br/>$18/月"]
            EC2_A -.->|attach| EBS_A1
            EC2_A -.->|attach| EBS_A2
        end
        
        subgraph AZ_B["AZ-B"]
            EC2_B["EC2インスタンス<br/>タイプ: t3.large"]
            EBS_B1["EBS gp3<br/>1TB<br/>3000 IOPS<br/>$52/月"]
            EC2_B -.->|attach| EBS_B1
        end
    end
    
    subgraph S3_Region["S3"]
        Snapshots["スナップショット管理<br/>ホット: 7日<br/>ウォーム: 30日<br/>コールド: アーカイブ"]
    end
    
    subgraph Monitoring["監視・管理"]
        CloudWatch["CloudWatch<br/>IOPS・スループット<br/>監視"]
        Lambda["Lambda<br/>自動削除スケジュール"]
        SNS["SNS通知<br/>削除実績"]
    end
    
    EBS_A1 -.->|snapshot| Snapshots
    EBS_A2 -.->|snapshot| Snapshots
    EBS_B1 -.->|snapshot| Snapshots
    
    CloudWatch -->|monitor| EBS_A1
    CloudWatch -->|monitor| EBS_B1
    Lambda -->|delete old| Snapshots
    Lambda -->|notify| SNS
end

style AWS_Region fill:#FF9900,color:#000
style Snapshots fill:#4CAF50,color:#fff
style Lambda fill:#FF9900,color:#000
style CloudWatch fill:#4CAF50,color:#fff

コスト削減の実測値

実際に2026年3月〜7月で削減がどう推移したか、データを取ってみました。

xychart-beta
    x-axis [3月, 4月, 5月, 6月, 7月]
    y-axis "月額コスト (万円)" 30, 110
    line [100, 90, 75, 68, 60]
    line [80, 80, 40, 40, 40]
    legend
        - EBS容量料
        - スナップショット料

3月時点ではEBS容量100万円 + スナップショット80万円 = 月180万円かかってたんです。

7月時点だとEBS容量60万円 + スナップショット40万円 = 月100万円に。

削減額: 月80万円(45%削減)

この中で、gp2→gp3移行で月40万円削減、スナップショット戦略で月40万円削減。ほぼ半々ですね。スナップショット戦略を甘く見てた自分たちの失敗ですよ。

2026年のEBS最適化で気付いたポイント

1. gp3のIOPS・スループットは「実測値ベース」で設定する

デフォルトの3,000 IOPS / 125 MB/sは過剰な場合がほとんど。CloudWatchで3週間観測して、95パーセンタイルで設定すれば十分です。セーフティマージンを10%とれば本番環境でも余裕なんですよね。

2. スナップショット自動削除は必須

Lambdaで自動化しないと、だんだん古いのが溜まる。最初は手動でやってたけど、人間はミスする。環境ごと・ボリュームタイプごとに異なるポリシーを持つのが現実的です。

3. gp3はAZ間のコピーコストに注意

スナップショットをコピーするときは、クロスAZだとデータ転送料が発生する。うちは同一AZ内でのみスナップショット保有にしました。

4. io1/io2との選び分けは「本当に必要か」で判断

io系は高IOPS専用ですが、月額が跳ね上がる。実測で6,000 IOPS以上が継続的に必要でなければgp3で十分なんですよ。うちの本番DBもgp3で安定してます。

まとめ

EBS最適化は地味だけど、確実に効きます。僕たちの場合は月80万円削減できた。2026年なら自動化ツールも整ってるので、実装ハードルも低い。

次のアクションとしては、こんな流れがいいと思います。

  1. CloudWatchで3週間、自分たちのEBSメトリクスを計測する → IOPS・スループット実績を把握
  2. スナップショット保有ポリシーを書く → ホット・ウォーム・コールドの3段階で十分
  3. Lambda で削除を自動化 → 毎月定期実行で古いスナップショットを削除
  4. gp2から移行するボリュームを優先順位付け → 大容量のDBボリュームから開始
  5. 1ヶ月後に削減額を確認 → CloudWatch Costで実績を可視化

正直、やらないと損です。本番環境の安定性を落とさず、50%近くコスト下げられる。これ以上に効率良い最適化は少ないと思ってますよ。

U

Untanbaby

ソフトウェアエンジニア|AWS / クラウドアーキテクチャ / DevOps

10年以上のIT実務経験をもとに、現場で使える技術情報を発信しています。 記事の誤りや改善点があればお問い合わせからお気軽にご連絡ください。

関連記事