שימוש במתגי feature flag עצמאיים

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

מבוא

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

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

  1. בניית מודל של המערכת באמצעות משאבים קלים של App Lifecycle Manager: יוצרים Units ב-App Lifecycle Manager כדי לייצג רכיבים של התשתית הקיימת (למשל, פריסה ספציפית של מיקרו-שירות, סביבת דייר, מכונה בינארית אחת). היחידות האלה משמשות רק כיעדים להגדרות של דגלים, ולא כוללות פריסת תשתית באמצעות תוכניות של App Lifecycle Manager.
  2. הגדרה והפצה של דגלים: משתמשים ב-App Lifecycle Manager API או במסוף Google Cloud כדי ליצור דגלי תכונות. לנהל את מחזור החיים שלהם באמצעות App Lifecycle Manager Rollouts כדי לוודא שהשינויים בהגדרות מופצים בצורה בטוחה והדרגתית ל-Units שנוצר. כך אפשר לשמור על עקביות ובטיחות תפעולית, גם כשמנהלים רק דגלים.
  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. 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

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

  3. 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

  4. Install the Google Cloud CLI.

  5. Configure the gcloud CLI to use your federated identity.

    For more information, see Sign in to the gcloud CLI with your federated identity.

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

    gcloud init
  7. התקנת Python: ודאו שמותקנת גרסה 3.7 ומעלה של Python במכונה שבה תפעילו את האפליקציה לדוגמה. צריך גם את pip כדי להתקין יחסי תלות.
    python --version
    pip --version
  8. אימות של gcloud ל-Application Default Credentials ‏ (ADC): סקריפט Python המקומי משתמש ב-ADC כדי לאמת את שירותי Google Cloud . נכנסים באמצעות חשבון המשתמש:
    gcloud auth application-default login
  9. מתן הרשאות לזהות האפליקציה: האפליקציה צריכה הרשאה לקרוא את הגדרות הדגלים משירות 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. יוצרים את המשאב, הגרסה והגרסה של ה-flag:

    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. כדי לעצור את אפליקציית Python המקומית, מקישים על Ctrl+C בטרמינל שבו פועל app.py.
  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
    

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