mazyu36の日記

某SIer所属のクラウドエンジニアのブログ

2022/10時点のAWS CDKにおけるECSの Blue Green Deploymentについて

はじめに

タイトルの通りだが、2022/10時点におけるAWS CDKにおけるECSの Blue Green Deploymentの対応状況について記載する。

公式ドキュメント的に推奨されていないやり方だが、現状問題なく動作しているのでやったことをまとめておく。

概要

以下のIssueの通りAWS CDKにおいて、正式にはBlue Green Deploymentに対応していない。

github.com

しかし上記のIssueの中でも議論されている通り、CfnDeploymentGroupを使用するとBlue Green Deploymentを実装することができる。

docs.aws.amazon.com

ただし、上記には以下の通りECSのBlue Green Deploymentには使うなという記載がある。そのため使用する際は自己責任で行うこと。

Amazon ECS blue/green deployments through CodeDeploy do not use the AWS::CodeDeploy::DeploymentGroup resource. To perform Amazon ECS blue/green deployments, use the AWS::CodeDeploy::BlueGreen hook. See Perform Amazon ECS blue/green deployments through CodeDeploy using AWS CloudFormation for more information.

※2022/11/2追記

本記事の内容は古い。v2.50.0でL2 Constructが実装されたので基本的にはそちらを使うのが良い。詳細は以下記事を参照。

mazyu36.hatenablog.com

実装方法

Blue Green Deploymentに直接関わる部分以外の実装は省略する(別途全体公開するかも)

1. CodeDeployのロールを作成する。

ECSにデプロイする権限を持つCodeDeployのIAMロールを作成する。

// CodeDeployのロール作成
const codeDeployRole = new iam.Role(this, 'CodeDeployRole', {
    assumedBy: new iam.ServicePrincipal('codedeploy.amazonaws.com'),
    managedPolicies: [
            iam.ManagedPolicy.fromAwsManagedPolicyName('AWSCodeDeployRoleForECS')
    ]
})

2. CodeDeployのECSアプリケーションを作成する。

以下の通り。

// CodeDeployのECSアプリケーションを作成
const ecsApplication = new codedeploy.EcsApplication(this, 'EcsApplication', {
        applicationName: 'ECSServiceApp', // 名称は任意
});

3. デプロイメントグループを作成する。

問題の箇所。

ドキュメント上ではECSのBlue Green Deploymentに使うなと言われているCfnDeploymentGroupを使用してDeployment Groupを作成する。

const deploymentGroup = new codedeploy.CfnDeploymentGroup(this, 'ECSDeploymentGroup', {
        applicationName: ecsApplication.applicationName, // 2で作成したECSアプリケーション名を設定
        serviceRoleArn: codeDeployRole.roleArn,  // 1で作成したIAMロールのARNを設定
        autoRollbackConfiguration: {
                enabled: true,
                events: ['DEPLOYMENT_FAILURE']
        },
        blueGreenDeploymentConfiguration: {
                deploymentReadyOption: {
                        actionOnTimeout: 'CONTINUE_DEPLOYMENT',
                        waitTimeInMinutes: 0
                },
                terminateBlueInstancesOnDeploymentSuccess: { 
                        action: 'TERMINATE',
                        terminationWaitTimeInMinutes: 5 // この辺りの設定はお好きに
                }
        },
        deploymentConfigName: 'CodeDeployDefault.ECSAllAtOnce',
        deploymentGroupName: 'ecsDeployment', // 手順4で使用する。
        deploymentStyle: {
                deploymentOption: 'WITH_TRAFFIC_CONTROL',
                deploymentType: 'BLUE_GREEN'

        },
        loadBalancerInfo: {  // ターゲットグループやリスナーは別途実装したALBのものを参照。
                targetGroupPairInfoList: [{
                        targetGroups: [
                                { name: blueTargetGroup.targetGroupName },  
                                { name: greenTargetGroup.targetGroupName }
                        ],
                        prodTrafficRoute: {
                                listenerArns: [listener.listenerArn]
                        },
                        testTrafficRoute: {
                                listenerArns: [testListener.listenerArn]
                        }
                }]
        },
        ecsServices: [{ // クラスター名、サービス名は別途実装したものを参照。
                clusterName: ecsCluster.clusterName,
                serviceName: ecsService.serviceName,
        }]

})

4. CodePipelineにCodeDeployのステージを追加する

ここも若干癖あり。特にDeploymentGroupは正式なものが用意されていない感満載だが、一応下記の通り実装すれば実現できる。

CodePipelineのDeploy Actionに紐付けるためのDeployment Groupを作成して名称一致で無理やり3で作成したものと紐づけるイメージ。

以下参考。 docs.aws.amazon.com

// CodePipelineを定義
const pipeline = new codepipeline.Pipeline(this, 'CodePipeline', {
    pipelineName: 'CodePipeline'
});


// CodePipelineにDeployステージを追加
const deployStage = pipeline.addStage({
  stageName: 'Deploy'
});


// DeploymentGroupを作成する。ここは名称を参照する形で力づくで作成している感ある。
const deploymentGroup: codedeploy.IEcsDeploymentGroup = codedeploy.EcsDeploymentGroup.fromEcsDeploymentGroupAttributes(this,
    'EcsCodeDeploymentGroup', {
    deploymentGroupName: 'ecsDeployment', // 手順3で作成したDeploymentGroupの名前と一致させる
    application: ecsApplication // 手順2で作成したECSアプリケーションを設定
});

// CodeDeploy用のActionを作成する。
const deployAction = new codepipeline_actions.CodeDeployEcsDeployAction({
    actionName: 'EcsCodeDeploymentAction',
    deploymentGroup,
    taskDefinitionTemplateInput: buildOutput,
    appSpecTemplateInput: buildOutput,
    containerImageInputs: [{
        input: buildOutput,
        taskDefinitionPlaceholder: "IMAGE1_NAME" // taskdef.jsonに設定するアレ
    }]
});

// CodePipelineにデプロイアクションを追加
deployStage.addAction(deployAction);

ここまでの1~4を実装すれば、フルCDKで(一応)ECSのBlue Green Deploymentを実現できる。

普通にできたけどなんで使っちゃいけないんだ・・・?

わかる人教えてください...

注意点

上記でBlue Green Deploymentを実装した後、タスク定義をCDK上で更新(例えばvCPUを変えるなど)して既存のECSリソースに適用しようとすると以下のエラーが出る。

Unable to update task definition on services with a CODE_DEPLOY deployment controller.

要はタスク定義はちゃんとCodeDeployで更新しろということであり、CDKでタスク定義を更新しようとするとInPlaceでの更新扱いになって、エラーになる(つまりリポジトリに置いたtaskdef.jsonを更新してCodePipeline経由で更新するとかしないとダメ)。

これはよく考えておかないと後々取り返しつかなくなる気がする。

サーキットブレーカーがある今、無理にBlue Green Deploymentにこだわる必要ない気がするけどね。

AWS Dev Day 2022において

ECSのCDKに関するセッションやBlue Green Deploymentに関するセッションあるのでCDKでECSやっている人は必見な気がする。

特に気になっているのは以下2つ。

  • C-3: Blue/Green デプロイと安全性と複雑性と
  • C-4: AWS CDKでECS on FargateのCI/CDを実現する際の理想と現実

aws.amazon.com

C-4で登壇される方はBlue Greenの話もするらしい。期待大。