使用功能标志(独立)

了解如何将 App Lifecycle Manager 功能标志用作独立服务,以管理应用中的功能可用性,即使这些应用不是由 App Lifecycle Manager 部署或管理的也是如此。

简介

本快速入门介绍如何使用 App Lifecycle Manager 的强大功能标志和受控发布功能,而无需使用 App Lifecycle Manager 进行基础架构预配(例如使用 Terraform 蓝图部署虚拟机或 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 资源(应用产品、单元种类、单元),以对应用组件进行建模,而无需部署基础架构。
  • 定义与单元种类关联的功能标志资源。
  • 创建标志发布机制(发布种类),以定义分发策略。
  • 使用 App Lifecycle Manager 发布分发 初始标志配置。
  • 在本地运行一个示例 Python 应用,该应用连接到 App Lifecycle Manager 标志服务并评估建模单元的标志。
  • 更新标志值,创建新的标志发布,并分发 更改。
  • 验证应用是否提取了更新后的标志值。

准备工作

  1. 登录您的 Google 账号。

    如果您还没有 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. 为应用默认凭据 (ADC) 验证 gcloud:本地 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 项目 ID,并将 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} 
    

定义和发布功能标志

现在,创建实际的功能标志,并使用 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 进行 Config Sync。应用代码在部署中保持标准;它只需要在运行时存在 FLAGD_SOURCE_PROVIDER_ID 环境变量,即可连接到 SaaS Config 服务。

您可以通过将构造的路径作为环境变量传递,将 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
    

后续步骤