השבתת מחיקה עם יכולת שחזור

סקירה כללית שימוש

בדף הזה מוסבר איך להשבית את התכונה 'מחיקה עם יכולת שחזור' בקטגוריות חדשות וקיימות בארגון.

מחיקה עם יכולת שחזור מופעלת כברירת מחדל בדליים חדשים כדי למנוע אובדן נתונים. במקרה הצורך, אפשר להשבית את המחיקה עם יכולת שחזור עבור דליים קיימים על ידי שינוי מדיניות המחיקה עם יכולת שחזור, ואפשר להשבית את המחיקה עם יכולת שחזור כברירת מחדל עבור דליים חדשים על ידי הגדרת תג ברירת מחדל ברמת הארגון. חשוב לדעת: אחרי שמבטלים את המחיקה עם יכולת שחזור, אי אפשר לשחזר את הנתונים שנמחקו, כולל מחיקות מקריות או זדוניות.

התפקידים הנדרשים

כדי לקבל את ההרשאות שדרושות להשבתת מחיקה עם יכולת שחזור, אתם צריכים לבקש מהאדמין לתת לכם את תפקידי ה-IAM הבאים ברמת הארגון:

התפקידים המוגדרים מראש האלה כוללים את ההרשאות שנדרשות להשבתת מחיקה עם יכולת שחזור. כדי לראות בדיוק אילו הרשאות נדרשות, אפשר להרחיב את הקטע ההרשאות הנדרשות:

ההרשאות הנדרשות

כדי להשבית את המחיקה עם יכולת שחזור, נדרשות ההרשאות הבאות:

  • storage.buckets.get
  • storage.buckets.update
  • storage.buckets.list (ההרשאה הזו נדרשת רק אם מתכננים להשתמש במסוף Google Cloud כדי לבצע את ההוראות בדף הזה)

    כאן מפורטות ההרשאות הנדרשות שכלולות בתפקיד Tag Admin(roles/resourcemanager.tagAdmin).

מידע על הקצאת תפקידים מופיע במאמרים הגדרה וניהול של מדיניות IAM בדליים או ניהול הגישה לפרויקטים.

השבתת מחיקה עם יכולת שחזור בקטגוריה ספציפית

לפני שמתחילים, חשוב לזכור:

  • אם משביתים מדיניות מחיקה עם יכולת שחזור מקטגוריה שיש בה אובייקטים שנמחקו עם יכולת שחזור בזמן ההשבתה, האובייקטים הקיימים שנמחקו עם יכולת שחזור יישמרו עד שתוקף תקופת השמירה שהוחלה בעבר יפוג.

  • אחרי שמשביתים מדיניות של מחיקה עם יכולת שחזור בקטגוריה, מערכת Cloud Storage לא שומרת אובייקטים שנמחקו לאחרונה.

  • כשמשביתים מדיניות של מחיקה עם יכולת שחזור בקטגוריה, השינוי לא מתבצע באופן מיידי ב-Cloud Storage בגלל שמטא-נתונים נשמרים במטמון. לכן, מומלץ להמתין לפחות 30 שניות לפני שמתחילים פעולות מחיקה אחרות, כמו מחיקה בכמות גדולה, אחרי שמשביתים מדיניות של מחיקה עם יכולת שחזור. כך תבטיחו שהנתונים יימחקו באופן סופי ולא יימחקו באופן זמני. מידע נוסף על עקביות בפעולות של Cloud Storage מופיע במאמר עקביות ב-Cloud Storage.

השתמש בהוראות הבאות כדי להשבית מחיקה עם יכולת שחזור בדלי ספציפי:

המסוף

  1. במסוף Google Cloud , נכנסים לדף Buckets של Cloud Storage.

    כניסה לדף Buckets

  2. ברשימת הקטגוריות, לוחצים על שם הקטגוריה שרוצים להשבית את מדיניות המחיקה עם יכולת שחזור שלה.

  3. לוחצים על הכרטיסייה הגנה.

  4. בקטע Soft delete policy, לוחצים על Disable כדי להשבית את המדיניות של מחיקה עם יכולת שחזור.

  5. לוחצים על אישור.

במאמר פתרון בעיות מוסבר איך מקבלים מידע מפורט על שגיאות בנושא פעולות ב-Cloud Storage שנכשלו במסוף Google Cloud .

שורת הפקודה

מריצים את הפקודה gcloud storage buckets update עם הדגל --clear-soft-delete:

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

כאשר:

  • BUCKET_NAME הוא שם הקטגוריה. לדוגמה, my-bucket.

ממשקי API ל-REST

API ל-JSON

  1. התקנה והפעלה של ה-CLI של gcloud, שמאפשרות ליצור אסימון גישה לכותרת Authorization.

  2. יוצרים קובץ JSON שמכיל את הפרטים הבאים:

    {
      "softDeletePolicy": {
        "retentionDurationSeconds": "0"
      }
    }
  3. משתמשים ב- cURL כדי לשלוח קריאה ל-API בפורמט JSON באמצעות בקשה של קטגוריית PATCH:

    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 הוא הנתיב לקובץ JSON שיצרתם בשלב 2.
    • BUCKET_NAME הוא שם הקטגוריה הרלוונטית. לדוגמה, my-bucket.

השבתת מחיקה עם יכולת שחזור ב-100 הקטגוריות הגדולות ביותר בפרויקט

באמצעות המסוף Google Cloud , אפשר להשבית מחיקה עם יכולת שחזור של עד 100 דליים בו-זמנית. הדליים ממוינים לפי מספר הבייטים שנמחקו עם יכולת שחזור או לפי היחס הגבוה ביותר בין מספר הבייטים שנמחקו עם יכולת שחזור למספר הבייטים הפעילים. כך אפשר לנהל את הדליים עם ההשפעה הגדולה ביותר על עלויות המחיקה עם יכולת שחזור.

  1. במסוף Google Cloud , נכנסים לדף Buckets של Cloud Storage.

    כניסה לדף Buckets

  2. בדף Cloud Storage, לוחצים על הגדרות.

  3. לוחצים על הכרטיסייה מחיקה עם יכולת שחזור.

  4. ברשימה Top buckets by deleted bytes (המאגרים המובילים לפי בייטים שנמחקו), בוחרים את המאגרים שרוצים להשבית בהם את האפשרות למחיקה עם יכולת שחזור.

  5. לוחצים על השבתת מחיקה עם יכולת שחזור.

    מחיקה עם יכולת שחזור מושבתת בדליים שבחרתם.

השבתה של מחיקה עם יכולת שחזור בכמה קטגוריות או בכל הקטגוריות בפרויקט

כדי להשבית את המחיקה עם יכולת שחזור בכמה קטגוריות או בכל הקטגוריות בפרויקט, מריצים את הפקודה gcloud storage buckets update עם הדגל --project והתו הכללי לחיפוש * ב-Google Cloud CLI:

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

כאשר:

  • PROJECT_ID הוא מזהה הפרויקט. לדוגמה, my-project.

השבתת מחיקה עם יכולת שחזור בכל הדליים בתיקייה

באמצעות Google Cloud CLI, מריצים את הפקודות gcloud projects list ו-gcloud storage buckets update כדי להשבית מחיקה עם יכולת שחזור בקטגוריות בכל הפרויקטים בתיקייה שצוינה.

מריצים את הפקודות gcloud projects list ו-gcloud storage buckets update כדי ליצור רשימה של כל הקטגוריות בתיקייה שצוינה, ואז משביתים את המחיקה עם יכולת שחזור של כל הקטגוריות בתיקייה:

gcloud projects list --filter="parent.id: FOLDER_ID" --format="value(projectId)" | while read project
do
  gcloud storage buckets update --project=$project --clear-soft-delete gs://*
done

כאשר:

  • FOLDER_ID הוא שם התיקייה. לדוגמה, 123456.

השבתת מחיקה עם יכולת שחזור ברמת הארגון

כדי להשבית את המחיקה עם יכולת שחזור ברמת הארגון, מריצים את הפקודה gcloud storage buckets update של Google Cloud CLI עם הדגל --clear-soft-delete והתו הכללי לחיפוש *:

מריצים את הפקודה gcloud storage buckets update עם הדגל --clear-soft-delete והתו הכללי לחיפוש * כדי להשבית את המחיקה עם יכולת שחזור בכל הקטגוריות בארגון:

gcloud projects list --format="value(projectId)" | while read project
do
  gcloud storage buckets update --project=$project --clear-soft-delete gs://*
done

ב-Cloud Storage, מחיקה עם יכולת שחזור מושבתת בקטגוריות קיימות. אובייקטים שכבר נמחקו עם יכולת שחזור יישארו בקטגוריות עד שתקופת השמירה שלהם תסתיים, ואז הם יימחקו באופן סופי.

השבתת מחיקה עם יכולת שחזור בדליים חדשים

אמנם מחיקה עם יכולת שחזור מופעלת כברירת מחדל בקטגוריות חדשות, אבל אפשר להשתמש בתגים כדי למנוע את ההפעלה שלה כברירת מחדל. התגים משתמשים במפתח storage.defaultSoftDeletePolicy כדי להחיל מדיניות של 0d (אפס ימים) מחיקה עם יכולת שחזור ברמת הארגון, שמשביתה את התכונה ומונעת שמירה עתידית של נתונים שנמחקו.

כדי להשבית את המחיקה עם יכולת שחזור כברירת מחדל כשיוצרים קטגוריות חדשות, פועלים לפי ההוראות הבאות. שימו לב שההוראות הבאות לא שוות להגדרה של מדיניות הארגון שמחייבת מדיניות מסוימת של מחיקה עם יכולת שחזור. כלומר, עדיין אפשר להפעיל מחיקה עם יכולת שחזור בדליים ספציפיים על ידי ציון מדיניות, אם צריך.

  1. באמצעות Google Cloud CLI, יוצרים את התג storage.defaultSoftDeletePolicy שמשמש לשינוי משך השמירה של מחיקה עם יכולת שחזור שמוגדר כברירת מחדל בקטגוריות חדשות. שימו לב שרק storage.defaultSoftDeletePolicy שם התג מעדכן את משך השמירה של מחיקה עם יכולת שחזור שמוגדר כברירת מחדל.

    יוצרים מפתח תג באמצעות הפקודה gcloud resource-manager tags keys create:

    gcloud resource-manager tags keys create storage.defaultSoftDeletePolicy \
     --parent=organizations/ORGANIZATION_ID \
     --description="Configures the default softDeletePolicy for new Storage buckets."
    

    כאשר:

    • ORGANIZATION_ID הוא המזהה המספרי של הארגון שרוצים להגדיר עבורו את משך הזמן של ברירת המחדל לשמירה לאחר מחיקה עם יכולת שחזור. לדוגמה, 12345678901. איך מוצאים את מזהה משאב הארגון
  2. כדי להשבית כברירת מחדל את תקופת השמירה של מחיקה עם יכולת שחזור בדליים חדשים, יוצרים ערך תג 0d (אפס ימים) באמצעות הפקודה gcloud resource-manager tags values create:

    gcloud resource-manager tags values create 0d \
      --parent=ORGANIZATION_ID/storage.defaultSoftDeletePolicy \
      --description="Disables soft delete for new Storage buckets."
    

    כאשר:

    • ORGANIZATION_ID הוא המזהה המספרי של הארגון שרוצים להגדיר עבורו את משך הזמן של ברירת המחדל לשמירה לאחר מחיקה עם יכולת שחזור. לדוגמה, 12345678901.
  3. מצרפים את התג למשאב באמצעות הפקודה gcloud resource-manager tags bindings create:

    gcloud resource-manager tags bindings create \
     --tag-value=ORGANIZATION_ID/storage.defaultSoftDeletePolicy/0d \
     --parent=RESOURCE_ID
    

    כאשר:

    • ORGANIZATION_ID: מספר הארגון שבו נוצר התג. לדוגמה, 12345678901.

    • RESOURCE_ID הוא השם המלא של הארגון שרוצים ליצור בשבילו את הקישור לתג. לדוגמה, כדי לצרף תג ל-organizations/7890123456, מזינים //cloudresourcemanager.googleapis.com/organizations/7890123456.

השבתת מחיקה עם יכולת שחזור של דליים שחורגים מסף עלות שצוין

באמצעות ספריות הלקוח של Cloud לשימוש ב-Python, אפשר להשבית מחיקה עם יכולת שחזור של קטגוריות שעוברות סף עלות יחסי שצוין, באמצעות דוגמה של ספריית לקוח של Python. הדוגמה מבצעת את הפעולות הבאות:

  1. הפונקציה מחשבת את העלות היחסית של האחסון לכל סוג אחסון (storage class).

  2. הכלי מעריך את העלות של מחיקה עם יכולת שחזור שהצטברה בדליים.

  3. האפשרות הזו מאפשרת להגדיר סף עלות לשימוש במחיקה עם יכולת שחזור, לראות את דלי המידע שחורגים מהסף שהגדרתם ולהשבית את המחיקה עם יכולת שחזור בדלי המידע שחורגים מהסף.

מידע נוסף על הגדרת ספריית הלקוח של Python ועל השימוש בדוגמה מופיע בדף כלי לניתוח עלויות של מחיקה עם יכולת שחזור ב-Cloud Storage README.md.

הדוגמה הבאה משביתה מחיקה עם יכולת שחזור בקטגוריות שחורגות מסף עלות שצוין:

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

המאמרים הבאים