기능 플래그 독립형 사용

App Lifecycle Manager에서 배포하거나 관리하지 않는 애플리케이션에서도 App Lifecycle Manager 기능 플래그를 독립형 서비스로 사용하여 애플리케이션의 기능 가용성을 관리하는 방법을 알아봅니다.

소개

이 빠른 시작에서는 Terraform 블루프린트를 사용하여 VM 또는 Cloud Run 서비스를 배포하는 것과 같은 인프라 프로비저닝에 App Lifecycle Manager를 사용할 필요 없이 App Lifecycle Manager의 강력한 기능 플래그 지정 및 제어된 출시 기능을 사용하는 방법을 보여줍니다. 이 접근 방식은 애플리케이션 인프라를 독립적으로 관리하지만 안전한 기능 플래그 관리를 위해 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. 애플리케이션과 통합: 애플리케이션 코드 (로컬, 온프레미스, 자체 관리형 클라우드 등 어디에서나 실행)에서 flagd 프로바이더와 함께 OpenFeature SDK를 사용합니다. 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 제공, 단위 종류, 단위)를 만듭니다.
  • 단위 종류와 연결된 기능 플래그 리소스를 정의합니다.
  • 배포 전략을 정의하는 플래그 출시 메커니즘 (출시 종류)을 만듭니다.
  • App Lifecycle Manager 출시를 사용하여 초기 플래그 구성을 배포 합니다.
  • App Lifecycle Manager 플래그 서비스에 연결하고 모델링된 단위의 플래그를 평가하는 샘플 Python 애플리케이션을 로컬에서 실행합니다.
  • 플래그 값을 업데이트하고, 새 플래그 출시를 만들고, 변경사항을 배포 합니다.
  • 애플리케이션이 업데이트된 플래그 값을 가져오는지 확인합니다.

시작하기 전에

  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. 외부 ID 공급업체(IdP)를 사용하는 경우 먼저 제휴 ID로 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. 외부 ID 공급업체(IdP)를 사용하는 경우 먼저 제휴 ID로 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. 애플리케이션 ID 권한 부여: 애플리케이션에는 SaaS Config 서비스에서 플래그 구성을 읽을 수 있는 권한이 필요합니다. 애플리케이션에서 사용할 ID에 `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. 단위 종류 만들기: 모델링하는 구성요소의 유형을 정의합니다. 중요한 점은 인프라를 관리하지 않으므로 블루프린트를 제공하지 않는다는 것입니다.

    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를 구성 동기화에만 사용합니다. 애플리케이션 코드는 배포 전반에서 표준으로 유지됩니다. SaaS Config Service에 연결하려면 런타임에 FLAGD_SOURCE_PROVIDER_ID 환경 변수만 있으면 됩니다.

생성된 경로를 환경 변수로 전달하여 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"
    

샘플 애플리케이션 통합 및 실행

모델링된 단위 구성을 사용하여 App Lifecycle Manager 플래그 서비스에 연결하려면 로컬에서 Python 애플리케이션을 실행합니다.

  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
    

다음 단계