Close

Bitbucket Pipelines에서 Launch Darkly 기능 플래그 사용

Warren Marusiak 얼굴 사진
Warren Marusiak

선임 기술 에반젤리스트

프로덕션 환경에 새 코드를 배포하는 것은 위험합니다. 코드가 단위 테스트, 통합 테스트, 테스트 및 스테이징 환경에서의 시스템 테스트를 거친 후에도 프로덕션에서 버그가 발생할 수 있습니다. 기존에는 프로덕션에서 버그가 발생하고 사용자가 영향을 받으면 개발자가 두 가지 선택을 할 수 있었습니다. 버그가 있는 코드를 롤백하거나 수정을 배포할 수 있었으며 두 해결책 모두 시간이 걸렸습니다. 이제는 개발자가 관련 코드 변경을 기능 플래그에 래핑하여 버튼을 한 번만 클릭하면 환경에서 기능을 활성화 또는 비활성화할 수 있습니다. 버그가 발생한 코드가 사용자에게 미치는 영향을 즉시 완화할 수 있으며 수정을 개발하고 안전하게 적용할 수 있습니다. 이 문서는 Bitbucket Pipelines 및 ImageLabeller 데모 애플리케이션의 Launch Darkly 기능 플래그를 사용하여 이것을 보여줍니다.

ImageLabeller 기능 플래그 데모

ImageLabeller는 머신러닝을 사용하여 이미지에 레이블을 지정하는 작은 애플리케이션입니다. ImageLabeller는 테스트, 스테이징, Production-us-west-2, Production-us-east-1, Production-ca-central-1이라는 다섯 가지 환경에 배포되었습니다. 이 문서에서는 기능 플래그를 사용하여 ImageLabeller의 SubmitImage 컴포넌트에 대한 변경 사항을 관리하는 방법을 보여줍니다. SubmitImage는 Go로 작성된 AWS Lambda입니다. 기능 플래그 관리에 Launch Darkly를, 소스 제어에 Bitbucket을, CI/CD 기능 관리에 Bitbucket Pipelines를 사용하게 됩니다.

Bitbucket Pipelines에서 Launch Darkly 기능 플래그를 사용하는 방법

로컬 Launch Darkly 관리자가 프로젝트 및 환경을 만들도록 합니다. 아래 스크린샷에는 다섯 가지 환경으로 구성된 PMMImageLabellerDemo라는 프로젝트가 있습니다. 테스트 및 스테이징은 프로덕션 전 환경입니다. 각 환경의 SDK 키를 기록해 두세요. 나중에 SDK 키가 Bitbucket에 리포지토리 변수로 추가될 것입니다.

이 예에서 Bitbucket Pipelines는 코드가 기능 브랜치에 커밋될 때 해당 환경에 배포됩니다. Production-us-west-2, Production-us-east-1, Production-ca-central-1은 AWS 환경에 해당하는 프로덕션 환경입니다. Bitbucket Pipelines는 코드가 기능 브랜치에서 풀리퀘스트를 통해 메인 라인에 병합될 때 해당 환경에 배포됩니다.

Bitbucket Pipelines 스크린샷

Launch Darkly에서 프로젝트의 기능 플래그를 만듭니다. 테스트 환경을 선택하고 기능 플래그 설정을 조정합니다. 아래 스크린샷에서 테스트 리전에서는 기능 플래그가 기본적으로 true를 반환하도록 설정되어 있습니다. AtlassianTestUser@atlassian.com이라는 특정 사용자가 요청을 하면 기능 플래그가 false를 반환합니다. 이런 방식으로 시스템 테스트 스위트의 테스트 사용자와 같은 특정 기명 사용자는 코드를 어떤 방법으로 실행할 수 있고 같은 환경의 일반 사용자는 코드를 다른 방법으로 실행할 수 있습니다.

이 동작은 환경별로 조정될 수 있습니다. 기능 플래그를 사용하면 개발자가 모든 리전에 새 코드를 배포할 수 있고 코드는 특정 환경에서만 실행되도록 허용합니다. 이 데모의 경우 스테이징과 세 가지 프로덕션 환경 모두에서 플래그가 false를 반환하도록 설정되어 있습니다. 새 코드는 테스트 환경에서만 실행됩니다.

사용자 타겟팅 스크린샷

Launch Darkly에서 각 환경용 SDK 키를 가져가세요. 그런 다음 Bitbucket으로 이동하여 이 플래그를 사용할 각 리포지토리에 리포지토리 변수를 추가합니다. 아래 스크린샷은 리포지토리 변수 다섯 개가 추가된 것을 보여줍니다. ld_test_env에는 테스트 환경을 위한 Launch Darkly SDK 키가 들어 있습니다. ld_staging_env에는 스테이징 환경을 위한 Launch Darkly SDK 키가 들어 있습니다. 리포지토리 변수는 나중에 bitbucket-pipelines.yml 파일에서 리포지토리에 대한 참조로 사용됩니다.

SDK 키

SDK 키가 리포지토리 변수로 추가된 후에 리포지토리의 bitbucket-pipeline.yml 파일에서 SDK 키 값을 참조할 수 있습니다. STACK_PARAMETERS가 아래 스니펫에서 production-ca-central-1의 배포 단계에 추가됩니다. STACK_PARAMETERS는 해당하는 SDK 키 값을 AWS CloudFormation template.yml 파일에 매개 변수로 보냅니다.

- pipe: atlassian/aws-sam-deploy:1.2.0
  variables:
    AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID}
    AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY}
    AWS_DEFAULT_REGION: 'ca-central-1'
    STACK_NAME: 'OpenDevOpsSubmitImage'
    CAPABILITIES: [ 'CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM', 'CAPABILITY_AUTO_EXPAND' ]
    TEMPLATE: 'https://s3.amazonaws.com/open-devops-code-ca-central-1-${AWS_ACCOUNT_ID}/submit-image-packaged.yml'
    WAIT: 'true'
    DEBUG: 'true'
    S3_BUCKET: 'open-devops-code-ca-central-1-${AWS_ACCOUNT_ID}'
    SAM_TEMPLATE: 'build/template.yaml'
    STACK_PARAMETERS: '[{
      "ParameterKey": "LaunchDarklySDKKey",
      "ParameterValue": "${ld_prod_cac1_env}"
    }]'

리포지토리의 template.yml 파일의 매개 변수 섹션에 문자열 유형의 LaunchDarklySDKKey를 사용하여 매개 변수 섹션을 추가합니다. 이 매개 변수는 bitbucket-pipelines.yml 파일에 설정된 LaunchDarklySDKKey STACK_PARAMETER의 값을 받습니다.

Parameters:
  LaunchDarklySDKKey:
    Type: String

template.yml 파일에 있는 SubmitImage 함수의 AWS Lambda 리소스도 업데이트합니다. LaunchDarklySDKKey를 환경 변수로 추가합니다.

Resources:
  SubmitImageFunction:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      CodeUri: submitImage/
      Handler: submit-image
      Runtime: go1.x
      Tracing: Active # https://docs.aws.amazon.com/lambda/latest/dg/lambda-x-ray.html
      Policies:
        - AmazonDynamoDBFullAccess
        - AmazonS3FullAccess
      Events:
        CatchAll:
          Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
          Properties:
            Path: /submit-image
            Method: GET
      Environment:
        Variables:
          LaunchDarklySDKKey:
            Ref: LaunchDarklySDKKey

LaunchDarklySDKKey 환경 변수는 Bitbucket Pipelines가 환경에 배포되고 나면 AWS Lambda 콘솔에 보일 것입니다. 이 키의 값은 환경에 고유합니다. 예를 들어 테스트의 LaunchDarklySDKKey 환경 변수는 Production-us-west-2의 환경 변수와 다릅니다.

환경 구성 스크린샷

SubmitImage는 Go로 작성된 AWS Lambda입니다. Go와 함께 Launch Darkly를 사용하려면 다음 종속성을 가져와야 합니다.

"gopkg.in/launchdarkly/go-sdk-common.v2/lduser"
ld "gopkg.in/launchdarkly/go-server-sdk.v5"

런타임에 Launch Darkly에서 기능 플래그 값을 가져오는 함수를 추가합니다.

func getLaunchDarklyFlags(username string) (bool, error) {
  client, _ := ld.MakeClient(os.Getenv("LaunchDarklySDKKey"), 5 * time.Second)
  flagKey := "SubmitImageDemoFeature"

  userUuid, uuidErr := uuid.NewRandom()
  if uuidErr != nil {
    return false, uuidErr
  }

  var user lduser.User
  if(username == "") {
    user = lduser.NewAnonymousUser(userUuid.String())
  } else {
    user = lduser.NewUser(username)
  }

  showFeature, _ := client.BoolVariation(flagKey, user, false)

  if showFeature {
    return true, nil
  } else {
    return false, nil
  }
}

기본 플래그 값을 가져오려면 빈 문자열로 함수를 호출하고 대상 값을 가져오려면 사용자 이메일로 함수를 호출합니다. 위에 표시된 설정은 익명 사용자에 대해 true, AtlasianTestUser@atlassian.com 사용자애 대해 false를 가져와야 합니다.

flagVal, flagErr  := getLaunchDarklyFlags("")
  if flagErr != nil {
    return "", flagErr
  }
  fmt.Println("DEMO flagVal for anonymous user: ", flagVal)

  flagVal, flagErr  = getLaunchDarklyFlags("AtlassianTestUser@atlassian.com")
  if flagErr != nil {
    return "", flagErr
  }
  fmt.Println("DEMO flagVal for AtlassianTestUser@atlassian.com: ", flagVal)

코드를 실행한 후 AWS CloudWatch 로그로 이동하여 올바른 플래그 값을 가져오는지 확인합니다.

이벤트 로그 스크린샷
대상 규칙 복사 중

관리자 설정으로 이동한 후 API 키로 이동하여 각 환경의 API 키 목록을 받습니다. API 키는 올바른 버전의 Split을 가져오기 위해 코드에서 API 호출 중에 Split으로 다시 전송됩니다. 이 가이드에서는 각 환경에 서버 측 키를 사용합니다.

관리자 설정

Bitbucket 리포지토리로 이동한 다음 리포지토리 설정, 리포지토리 변수로 이동하여 각 API 키에 변수를 추가합니다.

리포지토리 설정의 리포지토리 변수

bitbucket-pipelines.yml 파일을 편집하고 AWS SAM 배포 단계에 STACK_PARAMETERS를 추가합니다. 이 작업은 환경별로 수행됩니다. 아래의 YAML 스니펫은 AWS US-WEST-1 테스트 리전의 배포 단계를 보여줍니다. 따라서 이 단계는 위의 split_test_env 리포지토리 변수 설정을 참조합니다. 각 환경에 적합한 리포지토리 변수를 사용하세요.

- pipe: atlassian/aws-sam-deploy:1.2.0
  variables:
    AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID}
    AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY}
    AWS_DEFAULT_REGION: 'us-west-1'
    STACK_NAME: 'OpenDevOpsSubmitImage'
    CAPABILITIES: [ 'CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM', 'CAPABILITY_AUTO_EXPAND' ]
    TEMPLATE: 'https://s3.amazonaws.com/open-devops-code-us-west-1-${AWS_ACCOUNT_ID}/submit-image-packaged.yml'
    WAIT: 'true'
    DEBUG: 'true'
    S3_BUCKET: 'open-devops-code-us-west-1-${AWS_ACCOUNT_ID}'
    SAM_TEMPLATE: 'build/template.yaml'
    STACK_PARAMETERS: '[{
      "ParameterKey": "SplitIOSDKKey",
      "ParameterValue": "${split_test_env}"
    }]'

AWS CloudFormation template.yml 파일을 편집하고 Split SDK 키를 참조하는 매개 변수 섹션을 추가합니다.

Parameters:
  SplitIOSDKKey:
    Type: String

template.yml 파일에서 Split에 액세스하는 데 필요한 각 AWS Lambda 리소스에 환경 섹션을 추가합니다. 이 가이드는 다음을 보여줍니다

Environment:
  Variables:
    SplitIOSDKKey:
      Ref: SplitIOSDKKey

Split SDK를 사용할 Go 파일에 다음 종속성을 가져옵니다.

"github.com/splitio/go-client/v6/splitio/client"
"github.com/splitio/go-client/v6/splitio/conf"

이 함수는 클라이언트를 만들고 Split UI에서 만들어진 “SubmitImageDemoSplit”의 기능 플래그 값을 검색합니다. 사용자 이름이라는 단일 매개 변수가 있어야 합니다.

func getSplitIOFlag(username string) (string, error) {
  splitIOSDKKey := os.Getenv("SplitIOSDKKey")

  cfg := conf.Default()
  factory, err := client.NewSplitFactory(splitIOSDKKey, cfg)
  if err != nil {
    fmt.Printf("SDK init error: %s\n", err)
    return "", err
  }

  splitClient := factory.Client()
  err = splitClient.BlockUntilReady(10)
  if err != nil {
    fmt.Printf("SDK timeout: %s\n", err)
    return "", err
  }

  treatment := splitClient.Treatment(username, "SubmitImageDemoSplit", nil)
  fmt.Printf("SPLIT_DEMO treatment is %s, username is %s\n", treatment, username)

  return treatment, nil
}

이메일 주소로 함수를 호출합니다. 이 경우 someRandomUser@atlassian.com은 기능 플래그와 연결된 허용 목록의 구성원이 아니므로 기능 플래그의 기본값을 가져옵니다. AtlassianTestUser@atlassian.com은 자신이 속한 허용 목록에 연결된 기능 플래그의 값을 가져옵니다.

코드가 실행된 후에 AWS CloudWatch 로그의 출력을 살펴보세요. someRandomUser@atlassian.com이 액세스할 때 기능 플래그가 비활성화되고 AtlassianTestUser@atlassian.com이 액세스할 때 기능 플래그가 다시 활성화되는 것을 살펴보세요.

이벤트 로그

결론...

Launch Darkly 기능 플래그는 Bitbucket Pipelines를 통해 배포된 애플리케이션에 쉽게 통합됩니다. 개발자는 기능 플래그를 사용하여 배포된 코드의 실행을 제어할 수 있습니다. 이렇게 하면 버그가 있는 배포에 더 빠르게 대응하고 사용자에게 미치는 영향을 줄일 수 있습니다. 시간을 내어 Bitbucket 및 Launch Darkly 인스턴스를 시작하고 팀의 역량을 테스트해 보세요.

Warren Marusiak
Warren Marusiak

Warren is a Canadian developer from Vancouver, BC with over 10 years of experience. He came to Atlassian from AWS in January of 2021.


이 문서 공유

여러분께 도움을 드릴 자료를 추천합니다.

이러한 리소스에 책갈피를 지정하여 DevOps 팀의 유형에 대해 알아보거나 Atlassian에서 DevOps에 대한 지속적인 업데이트를 확인하세요.

DevOps 일러스트레이션

DevOps 커뮤니티

DevOps 일러스트레이션

DevOps 학습 경로

맵 일러스트레이션

무료로 사용해보기

DevOps 뉴스레터 신청

Thank you for signing up