소프트 삭제 사용

개요 사용

이 페이지에서는 버킷에서 소프트 삭제 정책의 상태를 사용 설정, 중지, 업데이트, 확인하는 방법을 설명합니다. 소프트 삭제된 객체를 나열하고 복원하는 방법은 소프트 삭제된 객체 사용을 참조하세요. 소프트 삭제에 관한 자세한 내용은 개요를 참조하세요. 이 기능을 사용 중지하려면 소프트 삭제 사용 중지를 참조하세요.

필요한 역할

소프트 삭제 정책을 만들고 관리하는 데 필요한 권한을 얻으려면 관리자에게 버킷 또는 버킷이 포함된 프로젝트에 대해 스토리지 관리자(roles/storage.admin) IAM 역할을 부여해 달라고 요청하세요.

이 사전 정의된 역할에는 소프트 삭제 정책을 만들고 관리하는 데 필요한 권한이 포함되어 있습니다. 필요한 정확한 권한을 보려면 필수 권한 섹션을 펼치세요.

필수 권한

소프트 삭제 정책을 만들고 관리하려면 다음 권한이 필요합니다.

  • storage.buckets.get
  • storage.buckets.update
  • storage.buckets.list(이 권한은 Google Cloud 콘솔을 사용하여 이 페이지의 안내를 수행하려는 경우에만 필요)

역할 부여에 대한 자세한 내용은 버킷에 IAM 사용 또는 프로젝트에 대한 액세스 관리를 참조하세요.

버킷의 소프트 삭제 정책 수정

버킷의 소프트 삭제 정책을 사용 설정, 중지 또는 업데이트하려면 다음 안내를 따르세요.

콘솔

  1. Google Cloud 콘솔에서 Cloud Storage 버킷 페이지로 이동합니다.

    버킷으로 이동

  2. 버킷 목록에서 관리하려는 소프트 삭제 정책이 있는 버킷의 이름을 클릭합니다.

  3. 보호 탭을 클릭합니다.

  4. 소프트 삭제 정책 섹션에서 다음 작업 중 하나를 수행합니다.

    • 버킷에 소프트 삭제 정책이 없으면 수정을 클릭하고 보관 기간의 단위와 기간을 선택한 후 저장을 클릭합니다.

    • 버킷에 소프트 삭제 정책이 있으면 수정을 클릭하여 보관 기간의 단위와 기간을 변경합니다.

Google Cloud 콘솔에서 실패한 Cloud Storage 작업에 대한 자세한 오류 정보를 가져오는 방법은 문제 해결을 참조하세요.

명령줄

버킷에 대한 소프트 삭제 정책을 추가하거나 수정하려면 --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. Authorization 헤더에 대한 액세스 토큰을 생성하려면 gcloud CLI가 설치 및 초기화되어 있어야 합니다.

    또는 OAuth 2.0 Playground를 사용하여 액세스 토큰을 만들고 Authorization 헤더에 포함할 수 있습니다.

  2. 다음 정보를 포함하는 JSON 파일을 만듭니다.

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

    여기서 TIME_IN_SECONDS는 소프트 삭제된 객체를 보관할 기간(초)입니다. 예를 들면 2678400입니다. 자세한 내용은 소프트 삭제 보관 기간을 참조하세요.

  3. cURL을 사용하여 PATCH 버킷 요청으로 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. 보호 탭을 클릭합니다.

  4. 소프트 삭제 정책 섹션에서 사용 중지를 클릭하여 버킷의 소프트 삭제 정책을 삭제합니다.

  5. 확인을 클릭합니다.

Google Cloud 콘솔에서 실패한 Cloud Storage 작업에 대한 자세한 오류 정보를 가져오는 방법은 문제 해결을 참조하세요.

명령줄

버킷에서 소프트 삭제 정책을 삭제하려면 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. Authorization 헤더에 대한 액세스 토큰을 생성하려면 gcloud CLI가 설치 및 초기화되어 있어야 합니다.

    또는 OAuth 2.0 Playground를 사용하여 액세스 토큰을 만들고 Authorization 헤더에 포함할 수 있습니다.

  2. 다음 정보를 포함하는 JSON 파일을 만듭니다.

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

    버킷에 대한 소프트 삭제 정책을 중지하려면 TIME_IN_SECONDS0 값을 사용합니다.

  3. cURL을 사용하여 PATCH 버킷 요청으로 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입니다.

버킷에 소프트 삭제 정책이 사용 설정되어 있는지 확인

Console

  1. Google Cloud 콘솔에서 Cloud Storage 버킷 페이지로 이동합니다.

    버킷으로 이동

  2. 버킷 목록에서 확인하려는 소프트 삭제 정책이 있는 버킷의 이름을 클릭합니다.

  3. 보호 탭을 클릭합니다.

    상태가 소프트 삭제 정책(데이터 복구용) 섹션에 표시됩니다.

보호 탭을 사용하여 버킷에 소프트 삭제 정책이 있는지 확인할 수도 있습니다.

Google Cloud 콘솔에서 실패한 Cloud Storage 작업에 대한 자세한 오류 정보를 가져오는 방법은 문제 해결을 참조하세요.

명령줄

버킷의 소프트 삭제 정책 상태를 확인하려면 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. Authorization 헤더에 대한 액세스 토큰을 생성하려면 gcloud CLI가 설치 및 초기화되어 있어야 합니다.

    또는 OAuth 2.0 Playground를 사용하여 액세스 토큰을 만들고 Authorization 헤더에 포함할 수 있습니다.

  2. cURL을 사용하여 GET 버킷 요청으로 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. 삭제된 바이트별 상위 버킷 목록에서 소프트 삭제를 사용 중지할 버킷을 선택합니다.

  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()

다음 단계