Close

搭配使用 Launch Darkly 功能标记与 Bitbucket pipelines

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 来管理源控制,使用 Bitbucket pipelines 来管理 CI/CD 功能。

如何搭配使用 Launch Darkly 功能标记与 Bitbucket pipelines

让您的本地 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 密钥添加为存储库变量后,SDK 密钥值可以作为存储库的 bitbucket-pipeline.yml 文件中的引用。在下面的代码段中,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}"
    }]'

将带字符串类型 LaunchDarklySDKKey 的“参数”部分添加到存储库的 template.yml 文件的“参数”部分中。此参数接收 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

在 Bitbucket pipelines 部署到环境中后,LaunchDarklySDKKey 环境变量将在 AWS Lambda 控制台中可见。此密钥的值对环境来说都是独有的。例如,测试中的 LaunchDarklySDKKey 环境变量将与 Production-us-west-2 中的环境变量不同。

环境配置屏幕截图

SubmitImage 是用 Go 编写的 AWS Lambda。如需搭配使用 Launch Darkly 与 Go,请导入以下依赖关系。

"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 调用期间,这些 api 密钥会被发送回 split,以获得正确的 split 版本。本指南使用每个环境的服务器端密钥。

管理员设置

转到您的 Bitbucket 存储库,再转到“存储库设置”,然后转到“存储库变量”,为每个 API 密钥添加变量。

存储库设置中的存储库变量

编辑 bitbucket-pipelines.yml 文件,然后将 STACK_PARAMETERS 添加到 AWS SAM 部署步骤中。这是根据每个环境完成的。下面的 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