Go로 App Lifecycle Manager 기능 플래그 사용

이 가이드에서는 Go 애플리케이션을 App Lifecycle Manager 기능 플래그와 통합하는 방법을 설명합니다. App Lifecycle Manager에서 관리하는 플래그를 평가하기 위해 flagd 프로바이더와 함께 OpenFeature SDK를 사용하는 방법을 알아봅니다. 이를 통해 Go 서비스에서 기능 가용성 및 동작을 동적으로 제어할 수 있습니다.

이 가이드에서는 다음 빠른 시작 중 하나를 완료하여 필요한 App Lifecycle Manager 리소스 (예: SaaS 제품, 단위 종류, 단위, 플래그, 출시)를 이미 설정했다고 가정합니다.

기본 요건

시작하기 전에 다음 사항을 확인하세요.

  1. Go 설치됨: 버전 1.23 이상
  2. App Lifecycle Manager 기능 플래그 빠른 시작 완료:
  3. 애플리케이션 기본 사용자 인증 정보 (ADC)에 대해 인증된 gcloud: Go 애플리케이션은 ADC를 사용하여 Google Cloud 서비스에 인증합니다. 로컬에서 실행하는 경우 (독립형 사용 또는 로컬 Docker 테스트) 환경에서 인증했는지 확인합니다.

    gcloud auth application-default login
    
  4. IAM 권한: Go 애플리케이션을 실행하는 ID에는 플래그 구성을 읽기 위해프로젝트에 대한 roles/saasconfig.viewer Identity and Access Management 역할이 필요합니다. Google Cloud

    gcloud projects add-iam-policy-binding YOUR_PROJECT_ID \
        --member="user:YOUR_EMAIL_ADDRESS" \
        --role="roles/saasconfig.viewer"
    

    YOUR_PROJECT_IDYOUR_EMAIL_ADDRESS를 적절하게 바꿉니다.

주요 환경 변수 이해

Go 애플리케이션은 환경 변수를 사용하여 올바른 플래그 구성에 연결하고 원하는 플래그를 평가합니다.

  • FLAGD_SOURCE_PROVIDER_ID: App Lifecycle Manager 서비스에서 가져올 기능 플래그 구성을 지정합니다. 단위와 연결된 FeatureFlagConfig의 전체 리소스 이름이어야 합니다.
    • 형식: projects/PROJECT_ID/locations/LOCATION/featureFlagsConfigs/UNIT_ID
    • : projects/my-gcp-project/locations/us-central1/featureFlagsConfigs/my-app-instance-01

Go 프로젝트 설정

애플리케이션을 실행하기 전에 모듈을 초기화하고 필요한 초기화 및 평가 로직을 추가합니다.

  1. 프로젝트 디렉터리 만들기:

    mkdir go-featureflag-app
    cd go-featureflag-app
    go mod init go-featureflag-app
    
  2. 애플리케이션 코드 만들기: 다음 콘텐츠로 main.go라는 파일을 추가하여 프로바이더를 초기화하고 플래그를 평가합니다.

    package main
    
    import (
        "context"
        "fmt"
        "log"
        "os"
    
        flagd "github.com/open-feature/go-sdk-contrib/providers/flagd/pkg"
        "github.com/open-feature/go-sdk/openfeature"
        "google.golang.org/grpc"
        "google.golang.org/grpc/credentials"
        "google.golang.org/grpc/credentials/oauth"
        "google.golang.org/grpc/metadata"
    )
    
    // GetNewFlagdProvider initializes a flagd provider configured for  App Lifecycle Manager
    func GetNewFlagdProvider(ctx context.Context) (*flagd.Provider, error) {
        providerID := os.Getenv("FLAGD_SOURCE_PROVIDER_ID")
        if providerID == "" {
            return nil, fmt.Errorf("FLAGD_SOURCE_PROVIDER_ID environment variable is not set")
        }
    
        creds, err := oauth.NewApplicationDefault(ctx)
        if err != nil {
            return nil, err
        }
    
        // Interceptor to inject the Unit name into headers for regional routing
        routingInterceptor := func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
            md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("name=%s", providerID))
            ctx = metadata.NewOutgoingContext(ctx, md)
            return streamer(ctx, desc, cc, method, opts...)
        }
    
        options := []flagd.ProviderOption{
            flagd.WithHost("saasconfig.googleapis.com"),
            flagd.WithPort(443),
            flagd.WithInProcessResolver(),
            flagd.WithProviderID(providerID),
            flagd.WithGrpcDialOptionsOverride([]grpc.DialOption{
                grpc.WithTransportCredentials(credentials.NewTLS(nil)),
                grpc.WithPerRPCCredentials(creds),
                grpc.WithStreamInterceptor(routingInterceptor),
            }),
        }
    
        return flagd.NewProvider(options...)
    }
    
    // InitializeFeatureManagement registers the provider globally
    func InitializeFeatureManagement(ctx context.Context) error {
        provider, err := GetNewFlagdProvider(ctx)
        if err != nil {
            return err
        }
        openfeature.SetProvider(provider)
        return nil
    }
    
    func main() {
    ctx := context.Background()
    InitializeFeatureManagement(ctx);
    
    // 1. Get Client
    client := openfeature.NewClient("simple-api")
    
    // 2. Create Evaluation Context
    evalCtx := openfeature.NewEvaluationContext()
    
    // 3. Evaluate Flag
    // Default to false if evaluation fails
    isEnhanced, err := client.BooleanValue(context.Background(), "enhanced-search", false, evalCtx)
    if err != nil {
    log.Printf("Flag evaluation failed: %v", err)
    }
    
    // 4. Return response
    if isEnhanced {
    fmt.Println("Enhanced search feature is enabled.")
    } else {
    fmt.Println("Enhanced search feature is disabled.")
    }
    
    }
    
  3. go.mod 만들기: 특정 버전을 사용하면 안정성이 보장됩니다.

    module go-featureflag-app
    
    go 1.23
    
    require (
        github.com/open-feature/go-sdk v1.15.1
        github.com/open-feature/go-sdk-contrib/providers/flagd v0.3.0
        google.golang.org/grpc v1.74.2
        golang.org/x/oauth2 v0.22.0
    )
    
  4. 종속 항목 가져오기:

    go mod tidy
    go mod download
    

Go 애플리케이션 실행

독립형 구성을 사용하여 로컬에서 또는 컨테이너로 배포하여 애플리케이션을 실행할 수 있습니다.

독립형 (로컬) 실행

  1. 환경 변수 설정:

    export PROJECT_ID="your-gcp-project-id"
    export LOCATION_1="us-central1"
    export UNIT_ID="my-app-instance-01"
    export FLAGD_SOURCE_PROVIDER_ID="projects/${PROJECT_ID}/locations/${LOCATION_1}/featureFlagsConfigs/${UNIT_ID}"
    
  2. 애플리케이션 실행:

    go run main.go
    

통합 (컨테이너화됨) 실행

  1. Dockerfile 만들기:

    FROM golang:1.23 as builder
    WORKDIR /app
    COPY go.mod go.sum ./
    RUN go mod download
    COPY . .
    RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /go-featureflag-app .
    
    FROM gcr.io/distroless/static-debian11
    WORKDIR /
    COPY --from=builder /go-featureflag-app /go-featureflag-app
    ENTRYPOINT ["/go-featureflag-app"]
    
  2. 이미지를 빌드하고 푸시합니다.

    export PROJECT_ID="your-gcp-project-id"
    docker build -t gcr.io/${PROJECT_ID}/my-go-app:latest .
    docker push gcr.io/${PROJECT_ID}/my-go-app:latest
    
  3. 서비스 배포: 단위 청사진을 업데이트하여 gcr.io/${PROJECT_ID}/my-go-app:latest를 참조하고 App Lifecycle Manager 출시를 사용하여 새 출시 버전을 배포합니다.

플래그 변경사항 확인

애플리케이션이 실행된 후 플래그를 올바르게 평가하는지 확인합니다.

  1. 초기 값 관찰: 출력에서 초기 상태에 해당하는 값을 로깅하는지 확인합니다.
  2. App Lifecycle Manager에서 플래그 업데이트: Google Cloud 콘솔 또는 gcloud을 사용하여 플래그 동작을 수정합니다.
  3. 업데이트된 값 확인: 로컬 컴파일을 다시 실행하거나 컨테이너 로그를 관찰하여 업데이트된 결과를 확인합니다.

다음 단계