使用獨立功能旗標

瞭解如何將 App Lifecycle Manager 功能旗標做為獨立服務使用,管理應用程式中的功能可用性,即使這些應用程式並非由 App Lifecycle Manager 部署或管理也沒問題。

簡介

本快速入門導覽課程說明如何使用 App Lifecycle Manager 強大的功能旗標和受控推出功能,而不需使用 App Lifecycle Manager 佈建基礎架構 (例如使用 Terraform 藍圖部署 VM 或 Cloud Run 服務)。如果您獨立管理應用程式基礎架構,但想使用 App Lifecycle Manager 安全地管理功能旗標,這個方法就非常適合。

在這個獨立做法中,您將:

  1. 使用輕量級的 App Lifecycle Manager 資源建立系統模型:建立 App Lifecycle Manager Units,代表現有基礎架構的元件 (例如特定微服務部署作業、租戶環境、單一二進位執行個體)。這些單元僅做為旗標設定的目標,不會使用 App Lifecycle Manager 藍圖部署基礎架構。
  2. 定義及發布旗標:使用 App Lifecycle Manager API 或 Google Cloud 控制台建立功能旗標。使用 App Lifecycle Manager Rollouts 管理生命週期,確保設定變更安全地逐步傳播至模型化的 Units。即使只管理旗標,也能確保作業一致性和安全性。
  3. 與應用程式整合:在應用程式程式碼中使用 OpenFeature SDK 和 flagd 供應商 (可在任何位置執行,包括本機、地端部署和自行管理的雲端)。將其設定為連線至 App Lifecycle Manager 旗標服務 (saasconfig.googleapis.com)、驗證,並使用對應的 Unit 資源名稱識別自身,以擷取正確的旗標值。

採用這種做法,您就能享有安全無虞的代管式功能旗標發布機制,不必變更現有的部署管道或基礎架構管理工具。

App Lifecycle Manager 功能旗標為不公開預先發布版。如要存取,必須加入允許清單。如要為機構或專案申請存取權,請填寫這份表單

本快速入門導覽課程會使用在本機執行的基本 Python 應用程式,示範如何存取旗標,模擬現有應用程式的整合方式。

目標

  • 設定新 Google Cloud 專案或使用現有專案。
  • 啟用必要的 API (App Lifecycle Manager 和 SaaS Config)。
  • 授予資源建立和讀取旗標所需的 Identity and Access Management 權限。
  • 建立最少的 App Lifecycle Manager 資源 (SaaS 產品、單元種類、單元),以便對應用程式元件進行建模,不必部署基礎架構。
  • 定義與單元種類相關聯的功能旗標資源。
  • 建立旗標推出機制 (推出作業 Kind),定義發布策略。
  • 使用 App Lifecycle Manager Rollout 發布初始旗標設定。
  • 在本機執行範例 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.

    Roles required to enable APIs

    To enable APIs, you need the Service Usage Admin IAM role (roles/serviceusage.serviceUsageAdmin), which contains the serviceusage.services.enable permission. Learn how to grant roles.

    Enable the APIs

  5. 安裝 Google Cloud CLI。

  6. 若您採用的是外部識別資訊提供者 (IdP),請先使用聯合身分登入 gcloud CLI

  7. 執行下列指令,初始化 gcloud CLI:

    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.

    Roles required to enable APIs

    To enable APIs, you need the Service Usage Admin IAM role (roles/serviceusage.serviceUsageAdmin), which contains the serviceusage.services.enable permission. Learn how to grant roles.

    Enable the APIs

  11. 安裝 Google Cloud CLI。

  12. 若您採用的是外部識別資訊提供者 (IdP),請先使用聯合身分登入 gcloud CLI

  13. 執行下列指令,初始化 gcloud CLI:

    gcloud init
  14. 安裝 Python:請確認您已在執行範例應用程式的機器上安裝 Python 3.7 以上版本。您也需要 pip 才能安裝依附元件。
    python --version
    pip --version
  15. 為應用程式預設憑證 (ADC) 驗證 gcloud:本機 Python 指令碼會使用 ADC 向 Google Cloud 服務驗證。使用使用者帳戶登入:
    gcloud auth application-default login
  16. 授予應用程式身分權限:應用程式需要權限,才能從 SaaS 設定服務讀取旗標設定。將 `roles/saasconfig.viewer` 角色授予應用程式將使用的身分。如要透過使用者帳戶在本機使用 ADC 進行這項快速入門,請將角色授予您的電子郵件地址:
        gcloud projects add-iam-policy-binding PROJECT_ID \
            --member="user:YOUR_EMAIL_ADDRESS" \
            --role="roles/saasconfig.viewer"
    PROJECT_ID 替換為專案 ID,並將 YOUR_EMAIL_ADDRESS 替換為與 CLI 登入作業相關聯的電子郵件地址。 Google Cloud

建立最少的 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. 建立 SaaS 產品:這是服務設定 (包括旗標) 的頂層容器。

    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. 建立單元 kind:定義您要模擬的元件類型。請注意,我們不會提供藍圖,因為我們不會管理基礎架構。

    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} 
    

定義及推出功能旗標

現在,請建立實際的功能旗標,並使用 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 設定服務。

您可以將建構的路徑做為環境變數傳遞,將 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. 在執行 app.py 的終端機中按下 Ctrl+C,停止本機 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
    

後續步驟