削除(復元可能)を使用する

概要 用途

このページでは、バケットの削除(復元可能)ポリシーの有効化、無効化、更新、ステータスの確認を行う方法について説明します。削除済み(復元可能)オブジェクトを一覧表示して復元する方法については、削除済み(復元可能)オブジェクトを使用するをご覧ください。削除(復元可能)の詳細については、概要をご覧ください。この機能を無効にするには、削除(復元可能)を無効にするをご覧ください。

必要なロール

削除(復元可能)ポリシーの作成と管理に必要な権限を取得するには、バケットまたはバケットを含むプロジェクトに対するストレージ管理者roles/storage.admin)IAM ロールを付与するよう管理者に依頼してください。

この事前定義ロールには、削除(復元可能)ポリシーの作成と管理に必要な権限が含まれています。必要とされる正確な権限については、「必要な権限」セクションを開いてご確認ください。

必要な権限

削除(復元可能)ポリシーの作成と管理には、次の権限が必要です。

  • storage.buckets.get
  • storage.buckets.update
  • storage.buckets.list(この権限は、このページで説明する操作を Google Cloud コンソールを使用して行う場合に必要です)

ロールの付与については、バケットで IAM を使用するまたはプロジェクトへのアクセスを管理するをご覧ください。

バケットの削除(復元可能)ポリシーを編集する

バケットの削除(復元可能)ポリシーを有効化、無効化、更新するには:

コンソール

  1. Google Cloud コンソールで、Cloud Storage の [バケット] ページに移動します。

    [バケット] に移動

  2. バケットのリストで、削除(復元可能)ポリシーを管理するバケットの名前をクリックします。

  3. [保護] タブをクリックします。

  4. [削除(復元可能)ポリシー] セクションで、次のいずれかの操作を行います。

    • バケットに削除(復元可能)ポリシーがない場合は、[編集] をクリックして時間の単位と保持期間を選択し、[保存] をクリックします。

    • バケットに削除(復元可能)ポリシーがある場合は、[編集] をクリックして時間の単位と保持期間を変更します。

失敗した Cloud Storage オペレーションの詳細なエラー情報を Google Cloud コンソールで確認する方法については、トラブルシューティングをご覧ください。

コマンドライン

バケットで削除(復元可能)ポリシーを追加または変更するには、--soft-delete-duration フラグを指定して gcloud storage buckets update コマンドを使用します。

  gcloud storage buckets update gs://BUCKET_NAME --soft-delete-duration=SOFT_DELETE_DURATION

ここで

  • BUCKET_NAME はバケットの名前です。例: my-bucket
  • SOFT_DELETE_DURATION には、削除済み(復元可能)オブジェクトを保持する期間を指定します。たとえば、2w1d は 2 週間と 1 日です。詳細については、削除(復元可能)の保持期間をご覧ください。

REST API

JSON API

  1. gcloud CLI のインストールと初期化を行います。これにより、Authorization ヘッダーのアクセス トークンを生成できます。

  2. 次の情報が含まれる JSON ファイルを作成します。

    {
      "softDeletePolicy": {
        "retentionDurationSeconds": "TIME_IN_SECONDS"
      }
    }

    ここで、TIME_IN_SECONDS は、削除済み(復元可能)オブジェクトを保持する期間(秒単位)です。例: 2678400。詳細については、削除(復元可能)の保持期間をご覧ください。

  3. cURL を使用して JSON API を呼び出し、PATCH Bucket リクエストを行います。

    curl -X PATCH --data-binary @JSON_FILE_NAME \
      -H "Authorization: Bearer $(gcloud auth print-access-token)" \
      -H "Content-Type: application/json" \
      "https://storage.googleapis.com/storage/v1/b/BUCKET_NAME"

    ここで

    • JSON_FILE_NAME は、手順 2 で作成した JSON ファイルのパスです。
    • BUCKET_NAME は、関連するバケットの名前です。例: my-bucket

バケットの削除(復元可能)ポリシーを無効にする

コンソール

  1. Google Cloud コンソールで、Cloud Storage の [バケット] ページに移動します。

    [バケット] に移動

  2. バケットのリストで、削除(復元可能)ポリシーを無効にするバケットの名前をクリックします。

  3. [保護] タブをクリックします。

  4. [削除(復元可能)ポリシー] セクションで [無効にする] をクリックして、削除(復元可能)ポリシーを無効にします。

  5. [確認] をクリックします。

失敗した Cloud Storage オペレーションの詳細なエラー情報を Google Cloud コンソールで確認する方法については、トラブルシューティングをご覧ください。

コマンドライン

バケットから削除(復元可能)ポリシーを削除するには、gcloud storage buckets update コマンドを使用して --clear-soft-delete フラグを指定します。

  gcloud storage buckets update gs://BUCKET_NAME --clear-soft-delete

ここで

  • BUCKET_NAME はバケットの名前です。例: my-bucket

REST API

JSON API

  1. gcloud CLI のインストールと初期化を行います。これにより、Authorization ヘッダーのアクセス トークンを生成できます。

  2. 次の情報が含まれる JSON ファイルを作成します。

    {
      "softDeletePolicy": {
        "retentionDurationSeconds": "TIME_IN_SECONDS"
      }
    }

    バケットの削除(復元可能)ポリシーを無効にするには、TIME_IN_SECONDS に値 0 を使用します。

  3. cURL を使用して、PATCH Bucket リクエストで JSON API を呼び出します。

    curl -X PATCH --data-binary @JSON_FILE_NAME \
      -H "Authorization: Bearer $(gcloud auth print-access-token)" \
      -H "Content-Type: application/json" \
      "https://storage.googleapis.com/storage/v1/b/BUCKET_NAME"

    ここで

    • JSON_FILE_NAME は、手順 2 で作成した JSON ファイルのパスです。
    • BUCKET_NAME は、関連するバケットの名前です。例: my-bucket

バケットで削除(復元可能)ポリシーが有効になっているかどうかを確認する

コンソール

  1. Google Cloud コンソールで、Cloud Storage の [バケット] ページに移動します。

    [バケット] に移動

  2. バケットのリストで、削除(復元可能)ポリシーを確認するバケットの名前をクリックします。

  3. [保護] タブをクリックします。

    [削除(復元可能)ポリシー(データ復旧用)] セクションにステータスが表示されます。

バケットにソフト削除ポリシーがあるかどうかは、[保護] タブで確認することもできます。

失敗した Cloud Storage オペレーションの詳細なエラー情報を Google Cloud コンソールで確認する方法については、トラブルシューティングをご覧ください。

コマンドライン

バケットの削除(復元可能)ポリシーのステータスを確認するには、gcloud storage buckets describe コマンドを使用します。

  gcloud storage buckets describe gs://BUCKET_NAME \
      --format="default(soft_delete_policy)"

ここで

  • BUCKET_NAME はバケットの名前です。例: my-bucket

REST API

JSON API

  1. gcloud CLI のインストールと初期化を行います。これにより、Authorization ヘッダーのアクセス トークンを生成できます。

  2. cURL を使用して、GET Bucket リクエストで JSON API を呼び出します。

    curl -X GET \
      -H "Authorization: Bearer $(gcloud auth print-access-token)" \
      -H "Content-Type: application/json" \
      "https://storage.googleapis.com/storage/v1/b/BUCKET_NAME?fields=softDeletePolicy"

    BUCKET_NAME は、該当するバケットの名前です。例: my-bucket

プロジェクト内の複数のバケットまたはすべてのバケットで削除(復元可能)を無効にする

次の手順では、プロジェクト内の複数のバケットまたはすべてのバケットで削除(復元可能)を無効にする方法について説明します。組織レベルで削除(復元可能)を無効にする場合は、削除(復元可能)を無効にするをご覧ください。

コンソール

Google Cloud コンソールで、削除(復元可能)されたバイト数が多いバケットまたはライブバイト数に対して削除(復元可能)バイト数の比率が高いバケットの削除(復元可能)を無効にできます。これにより、削除(復元可能)の使用に伴う費用を軽減できます。

  1. Google Cloud コンソールで、Cloud Storage の [バケット] ページに移動します。

    [バケット] に移動

  2. [Cloud Storage] のページで [設定] をクリックします。

  3. [削除(復元可能)] タブをクリックします。

  4. [過去 30 日間に削除(復元可能)された平均バイト数が多いバケット] のリストから、削除(復元可能)を無効にするバケットを選択します。

  5. [削除(復元可能)をオフにする] をクリックします。

    選択したバケットで削除(復元可能)が無効になります。

コマンドライン

プロジェクト内のすべてのバケットで削除(復元可能)を無効にするには、--clear-soft-delete フラグとワイルドカード(*を指定して gcloud storage buckets update コマンドを実行します。

gcloud storage buckets update --clear-soft-delete gs://*

クライアント ライブラリ

Python

詳細については、Cloud Storage Python API のリファレンス ドキュメントをご覧ください。

Cloud Storage に対する認証を行うには、アプリケーションのデフォルト認証情報を設定します。詳細については、ローカル開発環境の認証の設定をご覧ください。

from __future__ import annotations

import argparse
import json
import google.cloud.monitoring_v3 as monitoring_client


def get_relative_cost(storage_class: str) -> float:
    """Retrieves the relative cost for a given storage class and location.

    Args:
        storage_class: The storage class (e.g., 'standard', 'nearline').

    Returns:
        The price per GB from the https://cloud.google.com/storage/pricing,
        divided by the standard storage class.
    """
    relative_cost = {
        "STANDARD": 0.023 / 0.023,
        "NEARLINE": 0.013 / 0.023,
        "COLDLINE": 0.007 / 0.023,
        "ARCHIVE": 0.0025 / 0.023,
    }

    return relative_cost.get(storage_class, 1.0)


def get_soft_delete_cost(
    project_name: str,
    soft_delete_window: float,
    agg_days: int,
    lookback_days: int,
) -> dict[str, list[dict[str, float]]]:
    """Calculates soft delete costs for buckets in a Google Cloud project.

    Args:
        project_name: The name of the Google Cloud project.
        soft_delete_window: The time window in seconds for considering
          soft-deleted objects (default is 7 days).
        agg_days: Aggregate results over a time period, defaults to 30-day period
        lookback_days: Look back up to upto days, defaults to 360 days

    Returns:
        A dictionary with bucket names as keys and cost data for each bucket,
        broken down by storage class.
    """

    query_client = monitoring_client.QueryServiceClient()

    # Step 1: Get storage class ratios for each bucket.
    storage_ratios_by_bucket = get_storage_class_ratio(
        project_name, query_client, agg_days, lookback_days
    )

    # Step 2: Fetch soft-deleted bytes and calculate costs using Monitoring API.
    soft_deleted_costs = calculate_soft_delete_costs(
        project_name,
        query_client,
        soft_delete_window,
        storage_ratios_by_bucket,
        agg_days,
        lookback_days,
    )

    return soft_deleted_costs


def calculate_soft_delete_costs(
    project_name: str,
    query_client: monitoring_client.QueryServiceClient,
    soft_delete_window: float,
    storage_ratios_by_bucket: dict[str, float],
    agg_days: int,
    lookback_days: int,
) -> dict[str, list[dict[str, float]]]:
    """Calculates the relative cost of enabling soft delete for each bucket in a
       project for certain time frame in secs.

    Args:
        project_name: The name of the Google Cloud project.
        query_client: A Monitoring API query client.
        soft_delete_window: The time window in seconds for considering
          soft-deleted objects (default is 7 days).
        storage_ratios_by_bucket: A dictionary of storage class ratios per bucket.
        agg_days: Aggregate results over a time period, defaults to 30-day period
        lookback_days: Look back up to upto days, defaults to 360 days

    Returns:
        A dictionary with bucket names as keys and a list of cost data
        dictionaries
        for each bucket, broken down by storage class.
    """
    soft_deleted_bytes_time = query_client.query_time_series(
        monitoring_client.QueryTimeSeriesRequest(
            name=f"projects/{project_name}",
            query=f"""
                    {{  # Fetch 1: Soft-deleted (bytes seconds)
                        fetch gcs_bucket :: storage.googleapis.com/storage/v2/deleted_bytes
                        | value val(0) * {soft_delete_window}\'s\'  # Multiply by soft delete window
                        | group_by [resource.bucket_name, metric.storage_class], window(), .sum;

                        # Fetch 2: Total byte-seconds (active objects)
                        fetch gcs_bucket :: storage.googleapis.com/storage/v2/total_byte_seconds
                        | filter metric.type != 'soft-deleted-object'
                        | group_by [resource.bucket_name, metric.storage_class], window(1d), .mean  # Daily average
                        | group_by [resource.bucket_name, metric.storage_class], window(), .sum  # Total over window

                    }}  # End query definition
                    | every {agg_days}d  # Aggregate over larger time intervals
                    | within {lookback_days}d  # Limit data range for analysis
                    | ratio  # Calculate ratio (soft-deleted (bytes seconds)/ total (bytes seconds))
                    """,
        )
    )

    buckets: dict[str, list[dict[str, float]]] = {}
    missing_distribution_storage_class = []
    for data_point in soft_deleted_bytes_time.time_series_data:
        bucket_name = data_point.label_values[0].string_value
        storage_class = data_point.label_values[1].string_value
        # To include location-based cost analysis:
        # 1. Uncomment the line below:
        # location = data_point.label_values[2].string_value
        # 2. Update how you calculate 'relative_storage_class_cost' to factor in location
        soft_delete_ratio = data_point.point_data[0].values[0].double_value
        distribution_storage_class = bucket_name + " - " + storage_class
        storage_class_ratio = storage_ratios_by_bucket.get(
            distribution_storage_class
        )
        if storage_class_ratio is None:
            missing_distribution_storage_class.append(
                distribution_storage_class)
        buckets.setdefault(bucket_name, []).append({
            # Include storage class and location data for additional plotting dimensions.
            # "storage_class": storage_class,
            # 'location': location,
            "soft_delete_ratio": soft_delete_ratio,
            "storage_class_ratio": storage_class_ratio,
            "relative_storage_class_cost": get_relative_cost(storage_class),
        })

    if missing_distribution_storage_class:
        print(
            "Missing storage class for following buckets:",
            missing_distribution_storage_class,
        )
        raise ValueError("Cannot proceed with missing storage class ratios.")

    return buckets


def get_storage_class_ratio(
    project_name: str,
    query_client: monitoring_client.QueryServiceClient,
    agg_days: int,
    lookback_days: int,
) -> dict[str, float]:
    """Calculates storage class ratios for each bucket in a project.

    This information helps determine the relative cost contribution of each
    storage class to the overall soft-delete cost.

    Args:
        project_name: The Google Cloud project name.
        query_client: Google Cloud's Monitoring Client's QueryServiceClient.
        agg_days: Aggregate results over a time period, defaults to 30-day period
        lookback_days: Look back up to upto days, defaults to 360 days

    Returns:
        Ratio of Storage classes within a bucket.
    """
    request = monitoring_client.QueryTimeSeriesRequest(
        name=f"projects/{project_name}",
        query=f"""
            {{
            # Fetch total byte-seconds for each bucket and storage class
            fetch gcs_bucket :: storage.googleapis.com/storage/v2/total_byte_seconds
            | group_by [resource.bucket_name, metric.storage_class], window(), .sum;
            # Fetch total byte-seconds for each bucket (regardless of class)
            fetch gcs_bucket :: storage.googleapis.com/storage/v2/total_byte_seconds
            | group_by [resource.bucket_name], window(), .sum
            }}
            | ratio  # Calculate ratios of storage class size to total size
            | every {agg_days}d
            | within {lookback_days}d
            """,
    )

    storage_class_ratio = query_client.query_time_series(request)

    storage_ratios_by_bucket = {}
    for time_series in storage_class_ratio.time_series_data:
        bucket_name = time_series.label_values[0].string_value
        storage_class = time_series.label_values[1].string_value
        ratio = time_series.point_data[0].values[0].double_value

        # Create a descriptive key for the dictionary
        key = f"{bucket_name} - {storage_class}"
        storage_ratios_by_bucket[key] = ratio

    return storage_ratios_by_bucket


def soft_delete_relative_cost_analyzer(
    project_name: str,
    cost_threshold: float = 0.0,
    soft_delete_window: float = 604800,
    agg_days: int = 30,
    lookback_days: int = 360,
    list_buckets: bool = False,
    ) -> str | dict[str, float]: # Note potential string output
    """Identifies buckets exceeding the relative cost threshold for enabling soft delete.

    Args:
        project_name: The Google Cloud project name.
        cost_threshold: Threshold above which to consider removing soft delete.
        soft_delete_window: Time window for calculating soft-delete costs (in
          seconds).
        agg_days: Aggregate results over this time period (in days).
        lookback_days: Look back up to this many days.
        list_buckets: Return a list of bucket names (True) or JSON (False,
          default).

    Returns:
        JSON formatted results of buckets exceeding the threshold and costs
        *or* a space-separated string of bucket names.
    """

    buckets: dict[str, float] = {}
    for bucket_name, storage_sources in get_soft_delete_cost(
        project_name, soft_delete_window, agg_days, lookback_days
    ).items():
        bucket_cost = 0.0
        for storage_source in storage_sources:
            bucket_cost += (
                storage_source["soft_delete_ratio"]
                * storage_source["storage_class_ratio"]
                * storage_source["relative_storage_class_cost"]
            )
        if bucket_cost > cost_threshold:
            buckets[bucket_name] = round(bucket_cost, 4)

    if list_buckets:
        return " ".join(buckets.keys())  # Space-separated bucket names
    else:
        return json.dumps(buckets, indent=2)  # JSON output


def soft_delete_relative_cost_analyzer_main() -> None:
    # Sample run: python storage_soft_delete_relative_cost_analyzer.py <Project Name>
    parser = argparse.ArgumentParser(
        description="Analyze and manage Google Cloud Storage soft-delete costs."
    )
    parser.add_argument(
        "project_name", help="The name of the Google Cloud project to analyze."
    )
    parser.add_argument(
        "--cost_threshold",
        type=float,
        default=0.0,
        help="Relative Cost threshold.",
    )
    parser.add_argument(
        "--soft_delete_window",
        type=float,
        default=604800.0,
        help="Time window (in seconds) for considering soft-deleted objects.",
    )
    parser.add_argument(
        "--agg_days",
        type=int,
        default=30,
        help=(
            "Time window (in days) for aggregating results over a time period,"
            " defaults to 30-day period"
        ),
    )
    parser.add_argument(
        "--lookback_days",
        type=int,
        default=360,
        help=(
            "Time window (in days) for considering the how old the bucket to be."
        ),
    )
    parser.add_argument(
        "--list",
        type=bool,
        default=False,
        help="Return the list of bucketnames seperated by space.",
    )

    args = parser.parse_args()

    response = soft_delete_relative_cost_analyzer(
        args.project_name,
        args.cost_threshold,
        args.soft_delete_window,
        args.agg_days,
        args.lookback_days,
        args.list,
    )
    if not args.list:
        print(
            "To remove soft-delete policy from the listed buckets run:\n"
            # Capture output
            "python storage_soft_delete_relative_cost_analyzer.py"
            " [your-project-name] --[OTHER_OPTIONS] --list > list_of_buckets.txt \n"
            "cat list_of_buckets.txt | gcloud storage buckets update -I "
            "--clear-soft-delete",
            response,
        )
        return
    print(response)


if __name__ == "__main__":
    soft_delete_relative_cost_analyzer_main()

次のステップ