使用功能标志 - 独立

了解如何将 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 提供程序。将其配置为连接到应用生命周期管理器标志服务 (saasconfig.googleapis.com)、进行身份验证,并使用其对应的 Unit 资源名称来标识自身,以获取正确的标志值。

借助此方法,您无需更改现有的部署流水线或基础架构管理工具,即可受益于受管理的、安全的标志分发。

App Lifecycle Manager 功能标志目前为非公开预览版。访问需要列入许可名单。如需为您的组织或项目申请访问权限,请填写此表单

本快速入门使用在本地运行的基本 Python 应用来演示如何访问标志,模拟现有应用的集成方式。

目标

  • 设置新 Google Cloud 项目或使用现有项目。
  • 启用必需的 API(App Lifecycle Manager 和 SaaS Config)。
  • 授予资源创建和标志读取所需的 Identity and Access Management 权限。
  • 创建最少的应用生命周期管理器资源(SaaS 产品、单元种类、单元),以对应用组件进行建模,而无需部署基础架构。
  • 定义与单元种类关联的功能标志资源。
  • 创建标志发布机制(发布种类),用于定义分发策略。
  • 使用 App Lifecycle Manager 推出功能分发初始标志配置。
  • 在本地运行一个连接到 App Lifecycle Manager Flag 服务的示例 Python 应用,并评估所建模单元的标志。
  • 更新标志值,创建新的标志版本,然后分发更改。
  • 验证应用是否会采用更新后的标志值。

准备工作

  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.

    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 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. 创建 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. 创建单元种类:用于定义您要建模的组件类型。至关重要的是,我们不提供蓝图,因为我们不管理基础设施。

    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
    

后续步骤