שימוש בהתראות על תכונות עצמאיות

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

מבוא

במדריך הזה נסביר איך להשתמש ביכולות החזקות של App Lifecycle Manager לניהול תכונות והשקה מבוקרת, בלי שתצטרכו להשתמש ב-App Lifecycle Manager להקצאת משאבים (למשל, פריסת מכונות וירטואליות או שירותי Cloud Run באמצעות תוכניות Terraform). הגישה הזו מתאימה אם אתם מנהלים את תשתית האפליקציה באופן עצמאי, אבל רוצים להשתמש ב-App Lifecycle Manager כדי לנהל את ה-feature flags בצורה בטוחה.

בגישה העצמאית הזו:

  1. יצירת מודל של המערכת באמצעות משאבי App Lifecycle Manager קלי משקל: יוצרים App Lifecycle Manager Units כדי לייצג רכיבים של התשתית הקיימת (למשל, פריסה ספציפית של מיקרו-שירות, סביבת דייר, מופע בינארי יחיד). היחידות האלה משמשות רק כיעדים להגדרות של דגלים, ולא כוללות פריסה של תשתית באמצעות תוכניות בסיסיות של App Lifecycle Manager.
  2. הגדרה והפצה של דגלים: משתמשים ב-App Lifecycle Manager API או במסוף Google Cloud כדי ליצור דגלי תכונות. כדי לוודא שהשינויים בהגדרות מופצים בצורה בטוחה והדרגתית ל-Units הממודל, אפשר לנהל את מחזור החיים שלו באמצעות App Lifecycle Manager‏ Rollouts. כך אפשר לשמור על עקביות ובטיחות תפעולית, גם כשמנהלים רק דגלים.
  3. שילוב עם האפליקציה: משתמשים ב-OpenFeature SDK עם הספק flagd בקוד האפליקציה (שפועל בכל מקום – באופן מקומי, on-premises, בענן בניהול עצמי). מגדירים את האפליקציה כך שתתחבר לשירות הדגלים של App Lifecycle Manager‏ (saasconfig.googleapis.com), תבצע אימות ותזהה את עצמה באמצעות שם המשאב התואם של Unit כדי לאחזר את ערכי הדגלים הנכונים.

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

ה-feature flags של App Lifecycle Manager נמצאים בגרסת טרום-השקה פרטית. כדי לקבל גישה, צריך להוסיף את המשתמש לרשימת ההיתרים. כדי לבקש גישה לארגון או לפרויקט, מלאו את הטופס הזה.

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

מטרות

  • מגדירים פרויקט חדש Google Cloud או משתמשים בפרויקט קיים.
  • מפעילים את ממשקי ה-API הנדרשים (App Lifecycle Manager ו-SaaS Config).
  • לתת את ההרשאות הנדרשות לניהול זהויות והרשאות גישה (IAM) כדי ליצור משאבים ולקרוא דגלים.
  • יוצרים משאבים מינימליים של App Lifecycle Manager (הצעת אפליקציה, סוג יחידה, יחידה) כדי ליצור מודל של רכיב באפליקציה בלי לפרוס תשתית.
  • מגדירים משאב של feature flag שמשויך לסוג היחידה.
  • יוצרים מנגנון להשקת תכונות (סוג ההשקה) שמגדיר את אסטרטגיית ההפצה.
  • מפיצים את ההגדרה הראשונית של הדגל באמצעות השקת גרסה של App Lifecycle Manager.
  • מריצים באופן מקומי אפליקציית Python לדוגמה שמתחברת לשירות הדגלים של App Lifecycle Manager ומעריכה את הדגל עבור היחידה הממוּדלת.
  • מעדכנים את ערך הדגל, יוצרים גרסה חדשה של הדגל ומפיצים את השינוי.
  • מוודאים שהאפליקציה משתמשת בערך המעודכן של הדגל.

לפני שמתחילים

  1. נכנסים לחשבון Google.

    אם עדיין אין חשבון, יוצרים חשבון חדש.

  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  3. Verify that billing is enabled for your Google Cloud project.

  4. Enable the App Lifecycle Manager and SaaS Config APIs, if any are not already enabled.

    Roles required to enable APIs

    To enable APIs, you need the serviceusage.services.enable permission. If you created the project, then you likely already have this permission through the Owner role (roles/owner). Otherwise, you can get this permission through the Service Usage Admin role (roles/serviceusage.serviceUsageAdmin). Learn how to grant roles.

    Enable the APIs

  5. Install the Google Cloud CLI.

  6. If you're using an external identity provider (IdP), you must first sign in to the gcloud CLI with your federated identity.

  7. To initialize the gcloud CLI, run the following command:

    gcloud init
  8. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  9. Verify that billing is enabled for your Google Cloud project.

  10. Enable the App Lifecycle Manager and SaaS Config APIs, if any are not already enabled.

    Roles required to enable APIs

    To enable APIs, you need the serviceusage.services.enable permission. If you created the project, then you likely already have this permission through the Owner role (roles/owner). Otherwise, you can get this permission through the Service Usage Admin role (roles/serviceusage.serviceUsageAdmin). Learn how to grant roles.

    Enable the APIs

  11. Install the Google Cloud CLI.

  12. If you're using an external identity provider (IdP), you must first sign in to the gcloud CLI with your federated identity.

  13. To initialize the gcloud CLI, run the following command:

    gcloud init
  14. מתקינים את Python: מוודאים שגרסה Python 3.7 ואילך מותקנת במחשב שבו תפעילו את האפליקציה לדוגמה. צריך גם את pip כדי להתקין יחסי תלות.
    python --version
    pip --version
  15. אימות של gcloud ל-Application Default Credentials ‏ (ADC): סקריפט Python המקומי משתמש ב-ADC כדי לאמת את שירותי Google Cloud . נכנסים באמצעות חשבון המשתמש:
    gcloud auth application-default login
  16. מתן הרשאות לזהות האפליקציה: האפליקציה צריכה הרשאה לקרוא את הגדרות הדגלים משירות SaaS Config. מקצים את התפקיד `roles/saasconfig.viewer` לזהות שבה האפליקציה תשתמש. במדריך למתחילים הזה, שבו משתמשים ב-ADC באופן מקומי עם חשבון המשתמש, מקצים את התפקיד לכתובת האימייל שלכם:
    ‫
        gcloud projects add-iam-policy-binding PROJECT_ID \
            --member="user:YOUR_EMAIL_ADDRESS" \
            --role="roles/saasconfig.viewer"
    מחליפים את PROJECT_ID במזהה הפרויקט Google Cloud ואת YOUR_EMAIL_ADDRESS בכתובת האימייל שמשויכת להתחברות ל-CLI.

יצירת משאבים מינימליים של App Lifecycle Manager

למרות שאנחנו לא פורסים תשתית באמצעות App Lifecycle Manager, אנחנו צריכים כמה משאבים כדי לארגן, לטרגט ולהפיץ את הטירגוטים שלנו בצורה בטוחה. המשאבים האלה מייצגים את רכיבי האפליקציה הקיימים ב-App Lifecycle Manager.

  1. הגדרת משתנים: מגדירים משתני סביבה לשמות ולמיקומים של המשאבים.

    export PROJECT_ID="your-project-id"
    export SAAS_OFFERING_ID="standalone-flags-saas"
    export UNIT_KIND_ID="standalone-app-kind"
    export UNIT_ID="my-app-instance-01"
    export LOCATION_1="us-central1" # Example region where your app instance conceptually resides
    # Add more locations if your app components span multiple regions
    # export LOCATION_2="europe-west1"
    
  2. יצירת הצעה לאפליקציה: ההצעה משמשת כמאגר ברמה העליונה להגדרות של השירות, כולל דגלים.

    gcloud beta app-lifecycle-manager saas create ${SAAS_OFFERING_ID} \
        --project=${PROJECT_ID} \
        --location=global \
        --locations=name=${LOCATION_1} # Add --locations=name=${LOCATION_2} if using more regions
    
     gcloud beta app-lifecycle-manager saas create ${SAAS_OFFERING_ID} \
        --project=${PROJECT_ID} \
        --location=${LOCATION_1} \
        --locations=name=${LOCATION_1} 
    
  3. יצירת סוג יחידה: כאן מגדירים את סוג הרכיב שמבצעים לו מודלינג. חשוב לציין שאנחנו לא מספקים תוכנית, כי אנחנו לא מנהלים את התשתית.

    gcloud beta app-lifecycle-manager unit-kinds create ${UNIT_KIND_ID} \
      --project=${PROJECT_ID} \
      --location=global \
      --saas=${SAAS_OFFERING_ID}
    
    gcloud beta app-lifecycle-manager unit-kinds create ${UNIT_KIND_ID} \
      --project=${PROJECT_ID} \
      --location=${LOCATION_1} \
      --saas=${SAAS_OFFERING_ID}
    
  4. יצירת יחידה: מייצגת מופע ספציפי של האפליקציה.

    gcloud beta app-lifecycle-manager units create ${UNIT_ID} \
      --project=${PROJECT_ID} \
      --unit-kind=${UNIT_KIND_ID} \
      --location=${LOCATION_1} 
    

הגדרה והשקה של ה-feature flag

עכשיו, יוצרים את ה-feature flag בפועל ומשתמשים במנגנון ההשקה של App Lifecycle Manager כדי להפוך את ההגדרות שלו לזמינות ביחידות שיצרתם.

  1. הגדרת משתני דגל:

    export FLAG_ID="standalone-flag-01"
    export FLAG_KEY="enable-beta-feature" 
    
  2. יצירת משאב הדגל, הגרסה וההפצה:

    gcloud beta app-lifecycle-manager flags create ${FLAG_ID} \
      --project=${PROJECT_ID} \
      --key=${FLAG_KEY} \
      --flag-value-type=BOOL \
      --location=global \
      --unit-kind=${UNIT_KIND_ID} \
    
    export FLAG_REVISION_ID_1="${FLAG_ID}-rev1"
    gcloud beta app-lifecycle-manager flags revisions create ${FLAG_REVISION_ID_1} \
      --project=${PROJECT_ID} \
      --flag=${FLAG_ID} \
      --location=global
    
    export FLAG_RELEASE_ID_1="${FLAG_ID}-rel1"
    gcloud beta app-lifecycle-manager flags releases create ${FLAG_RELEASE_ID_1} \
      --project=${PROJECT_ID} \
      --flag-revisions=${FLAG_REVISION_ID_1} \
      --unit-kind=${UNIT_KIND_ID} \
      --location=global
    
  3. יוצרים סוג השקה: מגדירים את האסטרטגיה להפצת השינויים בדגלים.

    export ROLLOUT_KIND_ID="standalone-flags-rollout-kind"
    
    gcloud beta app-lifecycle-manager rollout-kinds create ${ROLLOUT_KIND_ID} \
      --project=${PROJECT_ID} \
      --unit-kind=${UNIT_KIND_ID} \
      --rollout-orchestration-strategy=Google.Cloud.Simple.AllAtOnce \
      --location=global
    
  4. יוצרים את ההשקה: מתחילים את תהליך ההפצה.

    export ROLLOUT_ID_1="${FLAG_ID}-rollout1"
    
    gcloud beta app-lifecycle-manager rollouts create ${ROLLOUT_ID_1} \
      --project=${PROJECT_ID} \
      --flag-release=${FLAG_RELEASE_ID_1} \
      --rollout-kind=${ROLLOUT_KIND_ID} \
      --location=global
    
  5. מעקב אחרי ההשקה: מוודאים שהפריסה הושלמה בהצלחה לפני שממשיכים.

    gcloud beta app-lifecycle-manager rollouts describe ${ROLLOUT_ID_1} \
      --project=${PROJECT_ID} \
      --location=global
    

הגדרת תשתית עצמאית

בהגדרה עצמאית, אתם מנהלים את אירוח האפליקציה בעצמכם (למשל, Cloud Run או GKE) ומשתמשים ב-App Lifecycle Manager רק לסנכרון ההגדרות האישיות. קוד האפליקציה נשאר סטנדרטי בכל הפריסות. צריך רק שמשתנה הסביבה FLAGD_SOURCE_PROVIDER_ID יהיה קיים בזמן הריצה כדי להתחבר ל-SaaS Config Service.

כדי למפות את הגדרות היחידות של App Lifecycle Manager להגדרות הפריסה הרגילות של Terraform, אתם יכולים להעביר את הנתיב שנוצר כמשתנה סביבה.

  1. הגדרת התשתית: ממפים את הנתיב בתבנית הפריסה (למשל, standalone.tf).

    variable "project_id" { type = string }
    variable "region"     { type = string }
    variable "unit_id"    { type = string }
    
    resource "google_cloud_run_v2_service" "standalone_app" {
      name     = "my-standalone-service"
      location = var.region
    
      template {
        containers {
          image = "us-central1-docker.pkg.dev/my-project/my-repo/my-image:latest"
    
          env {
            name  = "FLAGD_SOURCE_PROVIDER_ID"
            value = "projects/${var.project_id}/locations/${var.region}/featureFlagsConfigs/${var.unit_id}"
          }
        }
      }
    }
    
  2. מגדירים את ערכי המשתנים: מספקים את הפרמטרים של יחידת ההגדרה בקובץ משתנים משויך (למשל, terraform.tfvars).

    project_id = "PROJECT_ID"
    region     = "LOCATION_1"
    unit_id    = "UNIT_ID"
    

שילוב והרצה של אפליקציית הדוגמה

מריצים אפליקציית Python לדוגמה באופן מקומי כדי להתחבר לשירות הדגלים של App Lifecycle Manager באמצעות הגדרות היחידה שעוצבו.

  1. יוצרים ספרייה וקבצים לפרויקט:

    mkdir saas_flags_standalone_app
    cd saas_flags_standalone_app
    

    יצירת requirements.txt:

    google-auth
    grpcio>=1.49.1,<2.0.0dev
    openfeature-sdk==0.8.0
    openfeature-provider-flagd==0.2.2
    requests
    typing_extensions
    Flask>=2.0
    

    יחסי תלות של התקנות:

    pip install -r requirements.txt
    
  2. יוצרים את הקובץ app.py:

    import google.auth.transport.grpc
    import google.auth.transport.requests
    import grpc
    import logging
    import time
    import os
    import sys
    from flask import Flask, jsonify
    
    from openfeature import api
    from openfeature.contrib.provider.flagd import FlagdProvider
    from openfeature.contrib.provider.flagd.config import ResolverType
    
    app = Flask(__name__)
    
    logging.basicConfig(stream=sys.stdout, level=logging.INFO,
                        format='%(asctime)s - %(levelname)s - %(message)s')
    log = logging.getLogger(__name__)
    
    FLAG_KEY = os.environ.get("FLAG_KEY", "enable-beta-feature")
    DEFAULT_FLAG_VALUE = False 
    
    # CRITICAL: Read the Unit resource name from environment variable.
    # This identifies the application instance to the flag service.
    provider_id = os.environ.get("FLAGD_SOURCE_PROVIDER_ID")
    if not provider_id:
        log.critical("FATAL: FLAGD_SOURCE_PROVIDER_ID not set.")
        sys.exit("FLAGD_SOURCE_PROVIDER_ID not set")
    
    log.info(f"Initializing OpenFeature provider for Unit: {provider_id}")
    
    def add_x_goog_request_params_header(config_name):
        return lambda context, callback: callback([("x-goog-request-params", f'name={config_name}')], None)
    
    try:
        credentials, detected_project_id = google.auth.default(
            scopes=["https://www.googleapis.com/auth/cloud-platform"] 
        )
        auth_req = google.auth.transport.requests.Request() 
    
        configservice_credentials = grpc.composite_channel_credentials(
            grpc.ssl_channel_credentials(), 
            grpc.metadata_call_credentials( 
                google.auth.transport.grpc.AuthMetadataPlugin(credentials, auth_req)
            ),
            grpc.metadata_call_credentials(
                add_x_goog_request_params_header(provider_id)
            )
        )
    
        provider = FlagdProvider(
            resolver_type=ResolverType.IN_PROCESS,    
            host="saasconfig.googleapis.com",         
            port=443,                                 
            sync_metadata_disabled=True,              
            provider_id=provider_id,                  
            channel_credentials=configservice_credentials 
        )
    
        api.set_provider(provider)
        client = api.get_client()
    
        time.sleep(5)
    
        initial_flag_value = client.get_boolean_value(FLAG_KEY, DEFAULT_FLAG_VALUE)
        log.info(f"***** STARTUP FLAG CHECK ***** Flag '{FLAG_KEY}' evaluated to: {initial_flag_value}")
    
    except Exception as e:
        log.critical(f"FATAL: Failed to initialize OpenFeature provider: {e}", exc_info=True)
        sys.exit(f"Provider initialization failed: {e}")
    
    @app.route('/')
    def home():
        log.info(f"Request received for endpoint '/', evaluating flag: {FLAG_KEY}")
        try:
            flag_value = client.get_boolean_value(FLAG_KEY, DEFAULT_FLAG_VALUE)
            log.info(f"Evaluated flag '{FLAG_KEY}': {flag_value}")
            return jsonify({
                "flag_key": FLAG_KEY,
                "value": flag_value,
                "provider_id": provider_id 
            })
        except Exception as e:
            log.error(f"Error evaluating flag '{FLAG_KEY}': {e}", exc_info=True)
            return jsonify({
                "error": f"Failed to evaluate flag {FLAG_KEY}",
                "details": str(e),
            }), 500
    
    if __name__ == '__main__':
        port = int(os.environ.get('PORT', 8080))
        log.info(f"Starting Flask web server on host 0.0.0.0 port {port}")
        app.run(host='0.0.0.0', port=port)
    
  3. מריצים את האפליקציה:

    export FLAGD_SOURCE_PROVIDER_ID="projects/${PROJECT_ID}/locations/${LOCATION_1}/featureFlagsConfigs/${UNIT_ID}"
    python app.py
    
  4. אימות ערך הדגל הראשוני: מריצים בדיקה מול נקודת הקצה המקומית בטרמינל משני.

    curl http://localhost:8080
    

עדכון הדגל והפצת השינוי

לשנות את מצב זמן הריצה של הגדרת הדגל ולהפיץ את העדכון ליעדים המחוברים.

  1. עדכון המשאב של הדגל:

    gcloud beta app-lifecycle-manager flags create ${FLAG_ID} \
        --project=${PROJECT_ID} \
        --key=${FLAG_KEY} \
        --flag-value-type=BOOL \
        --location=global \
        --unit-kind=${UNIT_KIND_ID}
    
    export FLAG_REVISION_ID_2="${FLAG_ID}-rev2"
    gcloud beta app-lifecycle-manager flags revisions create ${FLAG_REVISION_ID_2} \
      --project=${PROJECT_ID} \
      --flag=${FLAG_ID} \
      --location=global
    
    export FLAG_RELEASE_ID_2="${FLAG_ID}-rel2"
    gcloud beta app-lifecycle-manager flags releases create ${FLAG_RELEASE_ID_2} \
      --project=${PROJECT_ID} \
      --flag-revisions=${FLAG_REVISION_ID_2} \
      --unit-kind=${UNIT_KIND_ID} \
      --location=global
    
  2. יוצרים השקה חדשה לעדכון:

    export ROLLOUT_ID_2="${FLAG_ID}-rollout2"
    
    gcloud beta app-lifecycle-manager rollouts create ${ROLLOUT_ID_2} \
      --project=${PROJECT_ID} \
      --flag-release=${FLAG_RELEASE_ID_2} \
      --rollout-kind=${ROLLOUT_KIND_ID} \
      --location=global
    
  3. מעקב אחרי ההשקה החדשה:

    gcloud beta app-lifecycle-manager rollouts describe ${ROLLOUT_ID_2} \
      --project=${PROJECT_ID} \
      --location=global 
    
  4. מאמתים את השינוי באפליקציה שפועלת:

    curl http://localhost:8080
    

הסרת המשאבים

כדי לא לצבור חיובים בחשבון Google Cloud על המשאבים שבהם השתמשתם בדף הזה, מבצעים את הפעולות הבאות.

  1. לוחצים על Ctrl+C במסוף שבו app.py פועל כדי לעצור את אפליקציית Python המקומית.
  2. מגדירים את מצב הדגל.
  3. כדי להסיר סימונים מיושנים, צריך ליצור השקה חדשה.
  4. אחרי שההשקה מסתיימת והדגלים שיצאו משימוש מוסרים, צריך למחוק את המשאבים של App Lifecycle Manager:

    gcloud beta app-lifecycle-manager rollouts delete ${ROLLOUT_ID_1} --project=${PROJECT_ID} --location=global --quiet 
    gcloud beta app-lifecycle-manager rollouts delete ${ROLLOUT_ID_2} --project=${PROJECT_ID} --location=global --quiet 
    gcloud beta app-lifecycle-manager rollout-kinds delete ${ROLLOUT_KIND_ID} --project=${PROJECT_ID} --location=global --quiet 
    gcloud beta app-lifecycle-manager flags releases delete ${FLAG_RELEASE_ID_1} --project=${PROJECT_ID} --location=global --quiet 
    gcloud beta app-lifecycle-manager flags releases delete ${FLAG_RELEASE_ID_2} --project=${PROJECT_ID} --location=global --quiet 
    gcloud beta app-lifecycle-manager flags delete ${FLAG_ID} --project=${PROJECT_ID} --location=global --quiet 
    gcloud beta app-lifecycle-manager units delete ${UNIT_ID} --project=${PROJECT_ID} --location=${LOCATION_1} --quiet 
    gcloud beta app-lifecycle-manager unit-kinds delete ${UNIT_KIND_ID} --project=${PROJECT_ID} --location=global --quiet 
    gcloud beta app-lifecycle-manager unit-kinds delete ${UNIT_KIND_ID} --project=${PROJECT_ID} --location=${LOCATION_1} --quiet 
    gcloud beta app-lifecycle-manager saas delete ${SAAS_OFFERING_ID} --project=${PROJECT_ID} --location=global --quiet 
    gcloud beta app-lifecycle-manager saas delete ${SAAS_OFFERING_ID} --project=${PROJECT_ID} --location=${LOCATION_1} --quiet 
    
  5. מחיקת הספרייה המקומית:

    cd ..
    rm -rf saas_flags_standalone_app
    

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