[小ネタ] バケット別に非現行バージョンが占めるストレージサイズの割合を計算しソートしてみた

[小ネタ] バケット別に非現行バージョンが占めるストレージサイズの割合を計算しソートしてみた

実運用では Storage Lens から GUI で確認し、API 経由で確認したくなったら、 CloudWatch メトリクスを発行して計算、最終手段で今回のような処理の実装の順番をオススメします。
Clock Icon2025.01.16

こんにちは!クラウド事業本部コンサルティング部のたかくに(@takakuni_)です。

S3 バケットを使っていると、非現行バージョンが全体の何%を占めているのか知りたいシーンがあるのではないでしょうか。

バージョニングはオブジェクトに対しての誤操作からの復旧や、意図して特定時点への復元したいシーンに非常に有効な手段なものの、非現行バージョンが溜まり続けるとコスト増につながります。

S3 Storage Lens を使うと、以下のようにバケット別に非現行バージョンが占める割合を確認できます。非常に便利ですよね。

2025-01-16 at 00.35.07-default-account-dashboard - S3 Storage Lens ダッシュボード  S3  us-east-1@2x.png

API で取得したい

この Storage Lens の機能ですが、ダッシュボードの値はデフォルトでは S3 コンソールから確認できます。

API 経由で取得するとなると、次のように CloudWatch メトリクスを発行するよう設定し計算するような処理が必要です。

https://dev.classmethod.jp/articles/amazon-s3-storage-lens-metrics-cloudwatch/

ただ、このメトリクスの発行はデフォルトでは無効となっており、すべてのアカウントで有効化していれば、一貫して確認できるような立ち位置の仕様になります。

すべてのアカウントで確認したい

今日の本題です。無料ではあるものの、大人の事情で上記のメトリクスの発行が行えないアカウントもあると思います。

そこで今回はメトリクスの発行を行わずとも API 経由で取得した情報をもとに、バケットに含まれる非現行バージョンのオブジェクトサイズを計算してみたいと思います。

ListObjectVersions

ListObjectVersions API は非現行バージョンを含むバケット内のオブジェクトに関するメタデータを取得する API です。

https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjectVersions.html

boto3 の Response Syntax を確認すると Versions の各オブジェクトに IsLatest キーが付与されていることがわかります。

こちらを利用して現行/非現行の識別ができそうです。

{
    'IsTruncated': True|False,
    'KeyMarker': 'string',
    'VersionIdMarker': 'string',
    'NextKeyMarker': 'string',
    'NextVersionIdMarker': 'string',
    'Versions': [
        {
            'ETag': 'string',
            'ChecksumAlgorithm': [
                'CRC32'|'CRC32C'|'SHA1'|'SHA256',
            ],
            'Size': 123,
            'StorageClass': 'STANDARD',
            'Key': 'string',
            'VersionId': 'string',
            'IsLatest': True|False,
            'LastModified': datetime(2015, 1, 1),
            'Owner': {
                'DisplayName': 'string',
                'ID': 'string'
            },
            'RestoreStatus': {
                'IsRestoreInProgress': True|False,
                'RestoreExpiryDate': datetime(2015, 1, 1)
            }
        },
    ],
    'DeleteMarkers': [
        {
            'Owner': {
                'DisplayName': 'string',
                'ID': 'string'
            },
            'Key': 'string',
            'VersionId': 'string',
            'IsLatest': True|False,
            'LastModified': datetime(2015, 1, 1)
        },
    ],
    'Name': 'string',
    'Prefix': 'string',
    'Delimiter': 'string',
    'MaxKeys': 123,
    'CommonPrefixes': [
        {
            'Prefix': 'string'
        },
    ],
    'EncodingType': 'url',
    'RequestCharged': 'requester'
}

やってみた

次のコードを作成し CloudWatch メトリクス無しでオブジェクトの情報を取得してみました。

サイズ計算は力技ですが、地道に Size キーを合計するような処理にしてみました。(もっといい方法があれば教えてください)

main.py
import boto3

def calculate_non_latest_percentage(bucket_name):
    """
    指定されたバケット内の非現行バージョンの割合を計算する。
    """
    try:
        # バケット内のすべてのオブジェクトのバージョンをリスト
        response = s3.list_object_versions(Bucket=bucket_name)

        # 全てのオブジェクトの合計サイズを計算
        total_objects = response.get('Versions', [])
        total_size_all = sum(obj['Size'] for obj in total_objects)

        # 非現行バージョンのオブジェクトの合計サイズを計算
        non_latest_objects = [obj for obj in total_objects if not obj['IsLatest']]
        non_latest_objects_total_size = sum(obj['Size'] for obj in non_latest_objects)

        # 割合を計算
        percentage = (non_latest_objects_total_size / total_size_all * 100) if total_size_all > 0 else 0.0
        return {
            "bucket_name": bucket_name,
            "total_size_all": total_size_all,
            "non_latest_size": non_latest_objects_total_size,
            "percentage": percentage
        }
    except Exception as e:
        print(f"バケット {bucket_name} の処理中にエラーが発生しました: {e}")
        return {
            "bucket_name": bucket_name,
            "total_size_all": 0,
            "non_latest_size": 0,
            "percentage": 0.0
        }

# S3クライアントの作成
s3 = boto3.client('s3')

# 全てのバケットを取得
buckets = s3.list_buckets().get('Buckets', [])

# 各バケットの計算結果を格納するリスト
results = []

for bucket in buckets:
    bucket_name = bucket['Name']
    result = calculate_non_latest_percentage(bucket_name)
    results.append(result)

# 非現行バージョンの割合でソート(降順)
sorted_results = sorted(results, key=lambda x: x['percentage'], reverse=True)

# 結果を出力
print("\n=== 非現行バージョンの割合(降順) ===")
for res in sorted_results:
    print(f"バケット名: {res['bucket_name']}")
    print(f"  全てのオブジェクトの合計サイズ: {res['total_size_all']} バイト")
    print(f"  非現行バージョンの合計サイズ: {res['non_latest_size']} バイト")
    print(f"  非現行バージョンの割合: {res['percentage']:.2f}%\n")

コードを実行してみます。うまく計算できているようです。

他のバケットは 0.00 % だったため、 A-Z 順に出力されていますね。

今回は Print で出力していますが、 JSON や CSV データなど扱いやすいデータ型に変換できればレポートが作れそうですね。

takakuni@ % python main.py

=== 非現行バージョンの割合(降順) ===
バケット名: aws-sam-cli-managed-default-hogehogehogehoge
  全てのオブジェクトの合計サイズ: 107042090 バイト
  非現行バージョンの合計サイズ: 38467453 バイト
  非現行バージョンの割合: 35.94%

バケット名: amplify-workshop-hogehoge
  全てのオブジェクトの合計サイズ: 424495823 バイト
  非現行バージョンの合計サイズ: 0 バイト
  非現行バージョンの割合: 0.00%

バケット名: amplify-workshop-fugafuga
  全てのオブジェクトの合計サイズ: 424495823 バイト
  非現行バージョンの合計サイズ: 0 バイト
  非現行バージョンの割合: 0.00%
...

まとめ

以上、簡単ですが「バケット別に非現行バージョンが占めるストレージサイズの割合を計算しソートしてみた」でした。

実運用では Storage Lens から GUI で確認し、API 経由で確認したくなったら、 CloudWatch メトリクスを発行して計算、最終手段で今回のような処理の実装の順番をオススメします。

このブログが参考になれば(?)幸いです。

クラウド事業本部コンサルティング部のたかくに(@takakuni_)でした!

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.