ECSでCodeDeployを使った線形デプロイとCanaryデプロイを試してみた

AWS CodeDeployがAmazon ECSの線形デプロイとCanaryデプロイをサポートしたためその機能を試してみました。
2020.03.17

はじめに

こんにちは、コンサル部の島川です。

2020年2月の頭くらいに「AWS CodeDeploy が Amazon ECS の線形デプロイと Canary デプロイをサポートするようになりました」というブログがアップされました。ECSは元々Blue/Greenデプロイをサポートしていましたが、一発で切り替える方法のみでした。 そこに新しく二種類のデプロイ方法が追加されました。ずっと気になっていた機能なので試してみました。

何がどう追加されたのか

ECSのBlue/GreenデプロイはCodeDeployとの連携が必要で、追加されたのはCodeDeployのデプロイ設定部分です。

今までは一番左の「CodeDeployDefault.ECSAllAtOnce」の機能だけでしたが、新しくデフォルトで4つデプロイ設定が追加されています。

引用元:GitHub : aws-samples / aws-codedeploy-linear-canary-deployments-blog

Deployment Configuration Description
CodeDeployDefault.ECSCanary10Percent5Minutes 最初の増分でトラフィックの10%をシフトします。残りの90%は5分後に展開されます。
CodeDeployDefault.ECSCanary10Percent15Minutes 最初の増分でトラフィックの10%をシフトします。残りの90%は15分後に展開されます。
CodeDeployDefault.ECSLinear10PercentEvery1Minutes すべてのトラフィックがシフトするまで、トラフィックの10%を毎分シフトします。
CodeDeployDefault.ECSLinear10PercentEvery3Minutes すべてのトラフィックがシフトされるまで、3分ごとにトラフィックの10%をシフトします。

デフォルト設定以外にも自分で設定を作成することも可能です。

今回はデフォルト設定を使用します。

やってみた

下記ドキュメントを参考に進めていきます。サンプルではus-east-2ですが、今回はap-northeast-1にしてます。

GitHubレポジトリをcloneする

CloudFormationテンプレート、サンプルDockerFileやECSサービス作成用のJSONファイルが含まれているのでcloneしておきます。

$ git clone git@github.com:aws-samples/aws-codedeploy-linear-canary-deployments-blog.git

ECRリポジトリを作成する

バージョン切り替えを確認するためにECRレポジトリを事前に作成します。

$ aws ecr create-repository --repository-name ecs-sample-app --region ap-northeast-1

マネジメントコンソール上からの作成でもOKです。

作成されたECRのURIは後で使うので控えておきましょう

$ [AccountID].dkr.ecr.ap-northeast-1.amazonaws.com/ecs-sample-app

コンテナイメージをECRにプッシュする

DockerFileをbuildしてECRにコンテナイメージをプッシュします。

$ cd aws-codedeploy-linear-canary-deployments-blog/Docker

$ docker build -t ecs-sample-app .

$ docker tag ecs-sample-app:latest [AccountID].dkr.ecr.ap-northeast-1.amazonaws.com/ecs-sample-app:v1

$ aws ecr --region ap-northeast-1 get-login --no-include-email --registry-ids [AccountID]

$ docker push [AccountID].dkr.ecr.ap-northeast-1.amazonaws.com/ecs-sample-app:v1

バージョン1がECRにアップロードされたので、続けてバージョン2もアップロードします。gitのbranchを切り替えて同じように作業します。

$ git fetch & git checkout v2

$ docker build -t ecs-sample-app .

$ docker tag ecs-sample-app:latest [AccountID].dkr.ecr.ap-northeast-1.amazonaws.com/ecs-sample-app:v2

$ aws ecr --region ap-northeast-1 get-login --no-include-email --registry-ids [AccountID]

$ docker push [AccountID].dkr.ecr.ap-northeast-1.amazonaws.com/ecs-sample-app:v2

CloudFormationを使ってサンプル用の環境を作成する

マネジメントコンソールでaws-codedeploy-linear-canary-deployments-blog/cloudformation/linear_ecs.yamlを指定して、デフォルトのまま次に進みスタックを作成します。作成された後の出力セクションのValueは後で使用します。

ECSサービスを作成する

pullしたgitのサンプルファイルを元に項目を埋めていきます。

aws-codedeploy-linear-canary-deployments-blog/json_files/create_service.json

{
    "cluster": "[クラスター名(CFn参照)]",
    "serviceName": "[サービス名(任意)]",
    "taskDefinition": "[タスク定義のARN(CFn参照)]",
    "loadBalancers": [
        {
            "targetGroupArn": "[ターゲットグループのARN(CFn参照)]",
            "containerName": "[コンテナ名(任意)]",
            "containerPort": 80
        }
    ],
    "launchType": "FARGATE",
    "schedulingStrategy": "REPLICA",
    "deploymentController": {
        "type": "CODE_DEPLOY"
    },
    "platformVersion": "LATEST",
    "networkConfiguration": {
       "awsvpcConfiguration": {
          "assignPublicIp": "ENABLED",
          "securityGroups": [ "[セキュリティグループID(CFn参照)]" ],
          "subnets": [ "[サブネットID(CFn参照)]", "[サブネットID(CFn参照)" ]
       }
    },
    "desiredCount": 2
}

書き換えたらcliから作成します。

$ aws ecs create-service --cli-input-json file://create_service.json --region ap-northeast-1

CodeDeployのリソースを作成する

cliでCodeDeployのアプリケーションを作成します。

$ aws deploy create-application --application-name ecs-blog-app --compute-platform ECS --region ap-northeast-1

cliでCodeDeployのデプロイグループを作成します。

pullしたgitのサンプルファイルを元に項目を埋めていきます。

aws-codedeploy-linear-canary-deployments-blog/json_files/code_deployment_group.json

{
	"applicationName": "ecs-blog-app",
	"deploymentGroupName": "[デプロイグループ名(任意)]",
	"deploymentConfigName": "CodeDeployDefault.ECSLinear10PercentEvery1Minutes",
	"serviceRoleArn": "[ロールARN(EcsRoleForCodeDeploy/CFn参照)]",

	"deploymentStyle": {
		"deploymentType": "BLUE_GREEN",
		"deploymentOption": "WITH_TRAFFIC_CONTROL"
	},
	"blueGreenDeploymentConfiguration": {
		"terminateBlueInstancesOnDeploymentSuccess": {
			"action": "TERMINATE",
			"terminationWaitTimeInMinutes": 5
		},
		"deploymentReadyOption": {
			"actionOnTimeout": "CONTINUE_DEPLOYMENT"
		}
	},
	"loadBalancerInfo": {
		"targetGroupPairInfoList": [{
			"targetGroups": [{
					"name": "[ターゲットグループ名(CFn参照)]"
				},
				{
					"name": "[ターゲットグループ名(CFn参照)]"
				}
			],
			"prodTrafficRoute": {
				"listenerArns": [
					"[ELBのリスナーARN(CFn参照)]"
				]
			},
			"testTrafficRoute": {
					"listenerArns": [
						"[ELBのリスナーARN(CFn参照)]"
					]
			}
		}]
	},
	"ecsServices": [{
		"serviceName": "[サービス名]",
		"clusterName": "[クラスター名]"
	}]
}

書き換えたら続けてコマンドを実行します

$ aws deploy create-deployment-group --cli-input-json file://code_deployment_group.json  --region ap-northeast-1

以上で準備が完了です!デプロイ設定の確認やALBにアクセスできるか確認しておきましょう。

タスク定義を更新する

v1からv2にバージョンを更新します。ECRにはすでにアップロードしているので数字を変えるだけです。

サービスを更新する

リビジョンを先ほど更新したものに変更します。

deploy設定を確認してサービスを更新します。

デプロイされているか確認する

今回選択したデプロイ方法はCodeDeployDefault.ECSLinear10PercentEvery1Minutesです。毎分10%ずつトラフィックをシフトさせていく方法です。リアルタイムでデプロイ状況を確認することができます。

何度かF5で更新すると緑でアクセスできました。

移行中はこのようにタスクが同時稼働している状態になります。

移行が完了すると今回は5分経過できることが旧タスクが停止するようになっています。5分以上残しておきたい場合はCodeDeployのデプロイグループの設定を変更しましょう。

さいごに

ECSのデプロイ手法が追加されたとのことで試してみました。デフォルトの設定以外にも結構細かく要件に合わせて設定することができます。切り替えスムーズ、設定簡単で初めて触った感触とてもよかったです。ぜひお試しを!

※サンプル環境の削除忘れずに!