記事
チュートリアル
インタラクティブ ガイド
Bitbucket Pipelines での Launch Darkly 機能フラグの使用
Warren Marusiak
シニア テクニカル エバンジェリスト
新しいコードを本番環境にデプロイするのは危険です。テスト環境やステージング環境でコードのユニット テスト、統合テスト、システム テストを実施した後でも、バグが本番環境に侵入する可能性があります。バグが本番環境に侵入してユーザーが影響を受けると、従来、開発者には 2 つの選択肢がありました。バグのあるコードをロールバックするか、修正をロール フォワードすることです。どちらの解決方法も時間がかかります。今では、開発者は関連するコード変更を機能フラグで囲み、ボタンをクリックするだけで、環境内の機能をオンまたはオフにすることができます。バグのあるコードがユーザーに与える影響をすばやく軽減でき、修正プログラムを開発して安全にロール フォワードできます。この記事では、ImageLabeller デモ アプリケーションで Bitbucket Pipelines と Launch Darkly 機能フラグを使用して、この内容をご説明します。
前提条件
ImageLabeller 機能フラグ デモ
ImageLabeller は、機械学習を使用して画像にラベルを添付する小さなアプリケーションです。ImageLabeller は 5 つの環境にデプロイされます。テスト、ステージング、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 機能に使用します。
Bitbucket Pipelines での Launch Darkly 機能フラグの使用方法
ローカルの Launch Darkly 管理者にプロジェクトと環境を作成してもらいます。下のスクリーンショットには、5 つの環境を含む PMMImageLabellerDemo というプロジェクトがあります。テストとステージングは本番前の環境です。それぞれの環境の SDK キーを記録します。後で、SDK キーはリポジトリ変数として Bitbucket に追加されます。
この例では、コードがフィーチャー ブランチにコミットされると、Bitbucket Pipelines がこれらの環境にデプロイされます。Production-us-west-2、Production-us-east-1、Production-ca-central-1 は本番環境で、AWS 環境に対応しています。プル リクエストによって、コードがフィーチャー ブランチからメインラインにマージされると、Bitbucket Pipelines がこれらの環境にデプロイされます。
Launch Darkly で、プロジェクトの機能フラグを作成します。テスト環境を選択し、機能フラグの設定を調整します。下のスクリーンショットでは、テスト リージョンの機能フラグがデフォルトで true を返すように設定されています。特定のユーザー AtlassianTestUser@atlassian.com がリクエストをしている場合、機能フラグは false を返します。このように、システム テスト スイートのテスト ユーザーなど、特定の名前のユーザーは、コードを一方向に実行することができますが、同じ環境の通常のユーザーはコードを異なる方法で実行できます。
この動作は環境ごとに調整できます。機能フラグによって、開発者は新しいコードをすべての地域にデプロイできますが、コードは特定の環境でのみ実行されます。このデモの場合、フラグはステージング環境と 3 つの本番環境すべてで false を返すように設定されています。新しいコードはテスト環境でのみ実行されます。
Launch Darkly からそれぞれの環境の SDK キーを取得します。次に、Bitbucket に移動し、このフラグを使用する各リポジトリにリポジトリ変数を追加します。下のスクリーンショットでは、5 つのリポジトリ変数が追加されたことを示しています。ld_test_env には、テスト環境の Launch Darkly SDK キーが含まれています。ld_staging_env には、ステージング環境の Launch Darkly SDK キーが含まれています。これらのリポジトリ変数を、後でリポジトリの bitbucket-pipelines.yml ファイルで参照します。
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}"
}]'
リポジトリの 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 です。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 呼び出し時にスプリットに送り返されます。このガイドでは、各環境の Server 側のキーを使用します。
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」の機能フラグ値を取得します。これは username という単一のパラメーターを取得します。
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 をスピンアップして、チームの機能をテストできます。
この記事を共有する
次のトピック
おすすめコンテンツ
次のリソースをブックマークして、DevOps チームのタイプに関する詳細や、アトラシアンの DevOps についての継続的な更新をご覧ください。