AWS CloudFormation を使用して CodeDeploy を通じて ECS ブルー/グリーンデプロイを実行する - AWS CloudFormation

AWS CloudFormation を使用して CodeDeploy を通じて ECS ブルー/グリーンデプロイを実行する

CloudFormation を使用して、AWS CodeDeploy を通じて ECS ブルー/グリーンデプロイを実行できます。ブルー/グリーンデプロイは、CodeDeploy が提供する安全なデプロイ戦略で、アプリケーションバージョンの変更による中断を最小限に抑えます。これは、新しいアプリケーション環境 (グリーン) を作成し、ライブトラフィックを処理している現在のアプリケーション (ブルー) とともに作成します。これにより、ライブトラフィックがブルーからグリーンにルーティングされ、その後、ブルーのリソースをオフにする前に、グリーン環境の監視とテストに一定の時間がかかります。

CloudFormation を使用して ECS ブルー/グリーンデプロイを実行する場合は、まず、使用するトラフィックルーティングとスタビライズ設定を指定するなど、ブルーとグリーンの両方のアプリケーション環境のリソースを定義するスタックテンプレートを作成します。次に、そのテンプレートからスタックを作成します。これにより、ブルー (現在の) アプリケーションが生成されます。CloudFormation はスタックの作成中にブルーのリソースのみを作成します。グリーンデプロイのリソースは、必要になるまで作成されません。

その後、将来のスタックの更新で、ブルーのアプリケーションでタスク定義またはタスクセットリソースを更新した場合、CloudFormation は次のことを行います。

  • 必要なグリーンアプリケーション環境リソースをすべて生成する

  • 指定されたトラフィックルーティングパラメータに基づいてトラフィックをシフトする

  • 青色のリソースを削除する

グリーンデプロイが成功してファイナライズされる前の任意の時点でエラーが発生した場合、CloudFormation は、グリーンデプロイ全体が開始される前の状態にスタックをロールバックします。

CloudFormation がスタックでブルー/グリーンデプロイを実行できるようにするには、スタックテンプレートに次の情報を含めます。

  • テンプレート内の AWS::CodeDeployBlueGreen トランスフォームを呼び出す Transform セクションと、AWS::CodeDeploy::BlueGreen フックを呼び出す Hook セクション。

  • スタックの更新中に置き換えられた場合に、ブルー/グリーンデプロイをトリガーする ECS リソースの少なくとも 1 つ。現在、これらのリソースは AWS::ECS::TaskDefinition および AWS::ECS::TaskSet です。

その後、CloudFormation でリソースを置き換える必要がある上記のリソースのプロパティを更新するスタック更新を開始すると、CloudFormation は上記のようにブルー/グリーンデプロイを実行します。リソースの更新動作の詳細については、「スタックリソースの更新動作」を参照してください。

場合によっては、スタックを作成する前に、ブルー/グリーンデプロイを有効にするためにスタックテンプレートを設定することをお勧めします。ただし、CloudFormation でブルー/グリーンデプロイを既存のスタックに実行させる機能を追加することもできます。これを行うには、スタックの既存のテンプレートに必要な情報を追加します。

さらに、スタックの更新を開始する前に、CloudFormation でグリーンデプロイの変更セットを生成することをお勧めします。これにより、スタックに加えられる実際の変更を確認できます。

CloudFormation リソースを使用したブルー/グリーンデプロイのモデリング

CloudFormation を通じて CodeDeploy を使用して ECS ブルー/グリーンデプロイを実行するには、テンプレートに Amazon ECS サービスとロードバランサーなど、デプロイをモデル化するリソースを含める必要があります。これらのリソースが表す内容の詳細については、AWS CodeDeploy ユーザーガイドの「Amazon ECS デプロイを開始する前に」をご参照ください。

要件 リソース 必須/オプション 置き換えられた場合、ブルー/グリーンデプロイをトリガーします。
Amazon ECS クラスター AWS::ECS::Cluster 省略可能。デフォルトのクラスターを使用できます。 いいえ
Amazon ECS サービス AWS::ECS::Service 必須。 いいえ
アプリケーションまたは Network Load Balancer AWS::ECS::Service LoadBalancer 必須。 いいえ
本稼働リスナー AWS::ElasticLoadBalancingV2::Listener 必須。 いいえ
テストリスナー AWS::ElasticLoadBalancingV2::Listener 省略可能。 いいえ
2 つのターゲットグループ AWS::ElasticLoadBalancingV2::TargetGroup 必須。 いいえ
Amazon ECS タスク定義 AWS::ECS::TaskDefinition 必須。 はい
Amazon ECS アプリケーション用のコンテナ AWS::ECS::TaskDefinition ContainerDefinition Name 必須。 いいえ
置き換えタスクセット用のポート AWS::ECS::TaskDefinition PortMapping ContainerPort 必須。 いいえ

グリーンデプロイをトリガーするリソースの更新

次の ECS リソースの置き換えが必要なプロパティを更新するスタック更新を実行すると、CloudFormation はグリーンデプロイを開始します。

リソースの置き換えを必要としないこれらのリソースのプロパティを更新しても、グリーンデプロイはトリガーされません。

上記のリソースに対する更新と、同じスタック更新内の他のリソースに対する更新を含めることはできません。上記のリスト内のリソースと、同じスタック内の他のリソースを更新する必要がある場合は、次のいずれかの操作を行います。

  • 2 つの別々のスタック更新操作を実行します。1 つは上記のリソースに対する更新のみを含み、もう 1 つは他のリソースに対する変更を含む別のスタック更新操作です。

  • テンプレートから Transform および Hook セクションを削除し、スタックの更新を実行します。この場合、CloudFormation はグリーンデプロイを実行しません。

CloudFormation を使用して ECS ブルー/グリーンデプロイメントを管理する際の考慮事項

CloudFormation を使用してブルー/グリーンデプロイを定義する場合は、次の点を考慮する必要があります。

  • ECS ブルー/グリーンデプロイをトリガーするリソースの更新」で説明されているように、特定のリソースに対する更新のみがグリーンデプロイを開始します。

  • ECS ブルー/グリーンデプロイをトリガーするリソースの更新」で説明されているように、グリーンデプロイを開始するリソースへの更新と他のリソースへの更新を同じスタック更新に含めることはできません。

  • デプロイターゲットとして指定できる ECS サービスは、1 つだけです。

  • CloudFormation によって難読化された値を持つパラメータは、グリーンデプロイ時に CodeDeploy サービスによって更新できず、エラーやスタックの更新に失敗します。具体的には次のとおりです。

  • まだ進行中のグリーンデプロイをキャンセルするには、CodeDeploy または ECS ではなく、CloudFormation でスタックの更新をキャンセルします。詳細については、「スタック更新のキャンセル」を参照してください。(更新が完了した後にキャンセルすることはできません。ただし、以前の設定を使用してスタックを再度更新することはできます。

  • ブルー/グリーン ECS デプロイを定義するテンプレートでは、出力値の宣言や他のスタックからの値のインポートは現在サポートされていません。

  • ブルー/グリーン ECS デプロイを定義するテンプレートでは、既存のリソースのインポートは現在サポートされていません。

  • ネストされたスタックリソースを含むテンプレートでは、AWS::CodeDeploy::BlueGreen フックを使用できません。

  • ネストされたスタックでは AWS::CodeDeploy::BlueGreen フックを使用できません。

CloudFormation を使用して CodeDeploy による ECS ブルー/グリーンデプロイを実行する方法の詳細については、CodeDeploy のみを使用した標準の Amazon ECS デプロイとは異なります。AWS CodeDeploy ユーザーガイドDifferences between Amazon ECS Blue/Green deployments through CloudFormation and standard Amazon ECS deployments を参照してください。

ECS ブルー/グリーンデプロイを実行するためのテンプレートの準備

スタックでブルー/グリーンデプロイメントを有効にするには、スタックの更新を実行する前に、以下のセクションをスタックテンプレートに含めます。

  • AWS::CodeDeployBlueGreen 変換への参照をテンプレートに追加します。

    "Transform": [ "AWS::CodeDeployBlueGreen" ],
  • AWS::CodeDeploy::BlueGreen フックを呼び出し、デプロイのプロパティを指定する Hook セクションを追加します。以下のテンプレートリファレンスを参考にしてください。

  • Resources セクションで、デプロイのブルーとグリーンのリソースを定義します。

これらのセクションは、最初にテンプレートを作成するときに (つまり、スタック自体を作成する前に) 追加することも、スタック更新を実行する前に既存のテンプレートに追加することもできます。新しいスタックにブルー/グリーンデプロイを指定した場合、CloudFormation では、スタック作成時にブルーのリソースのみが作成されます— グリーンデプロイ用のリソースは、スタックの更新中に必要になるまで作成されません。

ブルー/グリーンデプロイの変更セットの確認

グリーンデプロイを開始するスタックの更新を実行する前に、変更セットを作成することを強くお勧めします。これにより、スタックの更新を実行する前にスタックに加えられる実際の変更を確認できます。リソースの変更は、スタックの更新中に実行される順序で表示されない場合があることに注意してください。詳細については、「変更セットを使用したスタックの更新」を参照してください。

ブルー/グリーンデプロイのスタックイベントの表示

ECS デプロイの各ステップで生成されたスタックイベントは、スタックページのEventsタブ、およびAWS CLIを使用して表示できます。詳細については、「スタック更新の進行状況の監視」を参照してください。

テンプレートリファレンス

"Hooks": { "Logical ID": { "Properties": { "TrafficRoutingConfig": { "Type": "Traffic routing type" "TimeBasedCanary": { "StepPercentage": Integer, "BakeTimeMins": Integer } "TimeBasedLinear": { "StepPercentage": Integer, "BakeTimeMins": Integer } }, "AdditionalOptions": { "TerminationWaitTimeInMinutes": Integer }, "LifecycleEventHooks": { "BeforeInstall": "FunctionName", "AfterInstall": "FunctionName", "AfterAllowTestTraffic": "FunctionName", "BeforeAllowTraffic": "FunctionName", "AfterAllowTraffic": "FunctionName" }, "ServiceRole": "CodeDeployServiceRoleName", "Applications": [{ "Target": { "Type": "AWS::ECS::Service", "LogicalID": "Resource Logical ID" }, "ECSAttributes": { "TaskDefinitions": ["AWS::ECS::TaskDefinition Resource Logical ID (Blue)", "AWS::ECS::TaskDefinition Resource Logical ID (Green)"], "TaskSets": ["AWS::ECS::TaskSet Resource Logical ID (Blue)", "AWS::ECS::TaskSet Resource Logical ID (Green)"], "TrafficRouting": { "ProdTrafficRoute": { "Type": "AWS::ElasticLoadBalancingV2::Listener", "LogicalID": "Resource Logical ID (Production)" }, "TestTrafficRoute": { "Type": "AWS::ElasticLoadBalancingV2::Listener", "LogicalID": "Resource Logical ID (Test)" }, "TargetGroups": [ "AWS::ElasticLoadBalancingV2::TargetGroup Resource Logical ID (Blue)", "AWS::ElasticLoadBalancingV2::TargetGroup Resource Logical ID (Green)" ] } } } ] }, "Type": "AWS::CodeDeploy::BlueGreen" }

プロパティ

Logical ID

論理 ID は英数字(A-Za-z0-9)とし、テンプレート内で一意である必要があります。

Required: Yes

Properties

フックのプロパティ。

Required: Yes

TrafficRoutingConfig

トラフィックルーティングの設定値。

Required: No

デフォルトの設定は、時間ベースのカナリアトラフィックシフトで、15% のステップ率と 5 分間のベイク時間があります。

Type

デプロイ設定で使用されるトラフィックシフトのタイプ。

有効値: AllAtOnce | TimeBasedCanary | TimeBasedLinear

Required: Yes

TimeBasedCanary

デプロイの 1 つのバージョンから別のバージョンにトラフィックを 2 つずつシフトする設定を指定します。

Required: Conditional: トラフィックルーティングタイプとして TimeBasedCanary を指定する場合は、TimeBasedCanary パラメータを含める必要があります。

StepPercentage

TimeBasedCanary デプロイの最初の増分でシフトするトラフィックの割合。ステップの割合は 14% 以上である必要があります。

Required: No

BakeTimeMins

TimeBasedCanary デプロイの 1 番目と 2 番目のトラフィックシフトの間の分数。

Required: No

TimeBasedLinear

1 つのデプロイバージョンから別のデプロイバージョンにトラフィックを同じ増分で、各増分間隔を分単位でシフトする設定を指定します。

Required: Conditional: トラフィックルーティングタイプとして TimeBasedLinear を指定する場合は、TimeBasedLinear パラメータを含める必要があります。

StepPercentage

TimeBasedLinear デプロイの各増分開始時にシフトされるトラフィックの割合。ステップの割合は 14% 以上である必要があります。

Required: No

BakeTimeMins

TimeBasedLinear デプロイの各増分トラフィックシフト間隔 (分)。

Required: No

AdditionalOptions

ブルー/グリーンデプロイの追加オプション。

Required: No

TerminationWaitTimeInMinutes

ブルーリソースを終了するまでの待機時間を分単位で指定します。

Required: No

LifecycleEventHooks

ライフサイクルイベントフックを使用して、CodeDeploy がデプロイを検証するために呼び出すことができる Lambda 関数を指定します。デプロイライフサイクルイベントに対して、同じ関数または別の関数を使用することもできます。検証テストが完了すると、Lambda AfterAllowTraffic 関数は CodeDeploy を呼び戻し、Succeeded または Failed の結果を配信します。

詳細については、AWS CodeDeploy ユーザーガイドの「AppSpec の「フック」セクション」をご参照ください。

Required: No

BeforeInstall

置き換えタスクセットが作成される前にタスクを実行するために使用する関数。

Required: No

AfterInstall

置き換えタスクセットが作成され、ターゲットグループの 1 つがそれに関連付けられた後、タスクを実行するために使用する関数。

Required: No

AfterAllowTestTraffic

テストリスナーが置き換えタスクセットにトラフィックを提供した後、タスクを実行するために使用する関数。

Required: No

BeforeAllowTraffic

2 番目のターゲットグループが置き換えタスクセットに関連付けられた後、トラフィックが置き換えタスクセットに移行される前に、タスクを実行するために使用する関数。

Required: No

AfterAllowTraffic

2 番目のターゲットグループが置き換えタスクセットにトラフィックを提供した後、タスクを実行するために使用する関数。

Required: No

ServiceRole

ブルー/グリーンデプロイの実行に使用する CloudFormation の実行ロール。必要なアクセス許可のリストについては、「ブルー/グリーンデプロイに必要な IAM アクセス許可」を参照してください。

Required: Yes

Applications

Amazon ECS アプリケーションのプロパティを指定します。

Required: Yes

Target

Required: Yes

Type

リソースのタイプ。

Required: Yes

LogicalID

リソースの論理 ID。

Required: Yes

ECSAttributes

Amazon ECS アプリケーションデプロイのさまざまな要件を表すリソース。

Required: Yes

TaskDefinitions

Amazon ECS アプリケーションを含む Docker コンテナを実行するための AWS::ECS::TaskDefinition リソースの論理 ID。

Required: Yes

TaskSets

アプリケーションのタスクセットとして使用する AWS::ECS::TaskSet リソースの論理 ID。

Required: Yes

TrafficRouting

トラフィックルーティングに使用するリソースを指定します。

Required: Yes

ProdTrafficRoute

ターゲットグループにトラフィックを指示するためのロードバランサーにより使用されるリスナー。

Required: Yes

Type

リソースのタイプAWS::ElasticLoadBalancingV2::Listener

Required: Yes

LogicalID

リソースの論理 ID。

Required: Yes

TestTrafficRoute

ターゲットグループにトラフィックを指示するためのロードバランサーにより使用されるリスナー。

Required: Yes

Type

リソースのタイプAWS::ElasticLoadBalancingV2::Listener

Required: Yes

LogicalID

リソースの論理 ID。

Required: No

TargetGroups

登録されたターゲットにトラフィックをルーティングするためのターゲットグループとして使用するリソースの論理 ID。

Required: Yes

Type

フックのタイプ。AWS::ElasticLoadBalancingV2::Listener

Required: Yes

ブルー/グリーンデプロイに必要な IAM アクセス許可

CloudFormation がブルー/グリーンデプロイを正常に実行するには、次の CodeDeploy アクセス権限が必要です。

  • codedeploy:Get*

  • codedeploy:CreateCloudFormationDeployment

詳細については、AWS Identity and Access Management ユーザーガイドCodeDeploy のアクション、リソース、および条件キーを参照してください。

テンプレートの例

次の例では、CodeDeploy を使用して ECS ブルー/グリーンデプロイを設定します。トラフィックルーティングの進行状況は、ステップごとに 15% で、各ステップ間で 5 分の安定化期間を設定します。テンプレートを使用してスタックを作成すると、デプロイの初期設定がプロビジョニングされます。

その後、リソースの置き換えを必要とする "BlueTaskSet" リソースのプロパティーに変更を加えた場合、CloudFormation はスタックの更新の一部としてグリーンのデプロイを開始します。

JSON

{ "Parameters": { "Vpc": { "Type": "AWS::EC2::VPC::Id" }, "Subnet1": { "Type": "AWS::EC2::Subnet::Id" }, "Subnet2": { "Type": "AWS::EC2::Subnet::Id" } }, "Transform": [ "AWS::CodeDeployBlueGreen" ], "Hooks": { "CodeDeployBlueGreenHook": { "Properties": { "TrafficRoutingConfig": { "Type": "TimeBasedCanary", "TimeBasedCanary": { "StepPercentage": 15, "BakeTimeMins": 5 } }, "Applications": [ { "Target": { "Type": "AWS::ECS::Service", "LogicalID": "ECSDemoService" }, "ECSAttributes": { "TaskDefinitions": [ "BlueTaskDefinition", "GreenTaskDefinition" ], "TaskSets": [ "BlueTaskSet", "GreenTaskSet" ], "TrafficRouting": { "ProdTrafficRoute": { "Type": "AWS::ElasticLoadBalancingV2::Listener", "LogicalID": "ALBListenerProdTraffic" }, "TargetGroups": [ "ALBTargetGroupBlue", "ALBTargetGroupGreen" ] } } } ] }, "Type": "AWS::CodeDeploy::BlueGreen" } }, "Resources": { "ExampleSecurityGroup": { "Type": "AWS::EC2::SecurityGroup", "Properties": { "GroupDescription": "Security group for ec2 access", "VpcId": { "Ref": "Vpc" }, "SecurityGroupIngress": [ { "IpProtocol": "tcp", "FromPort": 80, "ToPort": 80, "CidrIp": "0.0.0.0/0" }, { "IpProtocol": "tcp", "FromPort": 8080, "ToPort": 8080, "CidrIp": "0.0.0.0/0" }, { "IpProtocol": "tcp", "FromPort": 22, "ToPort": 22, "CidrIp": "0.0.0.0/0" } ] } }, "ALBTargetGroupBlue": { "Type": "AWS::ElasticLoadBalancingV2::TargetGroup", "Properties": { "HealthCheckIntervalSeconds": 5, "HealthCheckPath": "/", "HealthCheckPort": "80", "HealthCheckProtocol": "HTTP", "HealthCheckTimeoutSeconds": 2, "HealthyThresholdCount": 2, "Matcher": { "HttpCode": "200" }, "Port": 80, "Protocol": "HTTP", "Tags": [ { "Key": "Group", "Value": "Example" } ], "TargetType": "ip", "UnhealthyThresholdCount": 4, "VpcId": { "Ref": "Vpc" } } }, "ALBTargetGroupGreen": { "Type": "AWS::ElasticLoadBalancingV2::TargetGroup", "Properties": { "HealthCheckIntervalSeconds": 5, "HealthCheckPath": "/", "HealthCheckPort": "80", "HealthCheckProtocol": "HTTP", "HealthCheckTimeoutSeconds": 2, "HealthyThresholdCount": 2, "Matcher": { "HttpCode": "200" }, "Port": 80, "Protocol": "HTTP", "Tags": [ { "Key": "Group", "Value": "Example" } ], "TargetType": "ip", "UnhealthyThresholdCount": 4, "VpcId": { "Ref": "Vpc" } } }, "ExampleALB": { "Type": "AWS::ElasticLoadBalancingV2::LoadBalancer", "Properties": { "Scheme": "internet-facing", "SecurityGroups": [ { "Ref": "ExampleSecurityGroup" } ], "Subnets": [ { "Ref": "Subnet1" }, { "Ref": "Subnet2" } ], "Tags": [ { "Key": "Group", "Value": "Example" } ], "Type": "application", "IpAddressType": "ipv4" } }, "ALBListenerProdTraffic": { "Type": "AWS::ElasticLoadBalancingV2::Listener", "Properties": { "DefaultActions": [ { "Type": "forward", "ForwardConfig": { "TargetGroups": [ { "TargetGroupArn": { "Ref": "ALBTargetGroupBlue" }, "Weight": 1 } ] } } ], "LoadBalancerArn": { "Ref": "ExampleALB" }, "Port": 80, "Protocol": "HTTP" } }, "ALBListenerProdRule": { "Type": "AWS::ElasticLoadBalancingV2::ListenerRule", "Properties": { "Actions": [ { "Type": "forward", "ForwardConfig": { "TargetGroups": [ { "TargetGroupArn": { "Ref": "ALBTargetGroupBlue" }, "Weight": 1 } ] } } ], "Conditions": [ { "Field": "http-header", "HttpHeaderConfig": { "HttpHeaderName": "User-Agent", "Values": [ "Mozilla" ] } } ], "ListenerArn": { "Ref": "ALBListenerProdTraffic" }, "Priority": 1 } }, "ECSTaskExecutionRole": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Sid": "", "Effect": "Allow", "Principal": { "Service": "ecs-tasks.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }, "ManagedPolicyArns": [ "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" ] } }, "BlueTaskDefinition": { "Type": "AWS::ECS::TaskDefinition", "Properties": { "ExecutionRoleArn": { "Fn::GetAtt": [ "ECSTaskExecutionRole", "Arn" ] }, "ContainerDefinitions": [ { "Name": "DemoApp", "Image": "nginxdemos/hello:latest", "Essential": true, "PortMappings": [ { "HostPort": 80, "Protocol": "tcp", "ContainerPort": 80 } ] } ], "RequiresCompatibilities": [ "FARGATE" ], "NetworkMode": "awsvpc", "Cpu": "256", "Memory": "512", "Family": "ecs-demo" } }, "ECSDemoCluster": { "Type": "AWS::ECS::Cluster", "Properties": {} }, "ECSDemoService": { "Type": "AWS::ECS::Service", "Properties": { "Cluster": { "Ref": "ECSDemoCluster" }, "DesiredCount": 1, "DeploymentController": { "Type": "EXTERNAL" } } }, "BlueTaskSet": { "Type": "AWS::ECS::TaskSet", "Properties": { "Cluster": { "Ref": "ECSDemoCluster" }, "LaunchType": "FARGATE", "NetworkConfiguration": { "AwsVpcConfiguration": { "AssignPublicIp": "ENABLED", "SecurityGroups": [ { "Ref": "ExampleSecurityGroup" } ], "Subnets": [ { "Ref": "Subnet1" }, { "Ref": "Subnet2" } ] } }, "PlatformVersion": "1.4.0", "Scale": { "Unit": "PERCENT", "Value": 100 }, "Service": { "Ref": "ECSDemoService" }, "TaskDefinition": { "Ref": "BlueTaskDefinition" }, "LoadBalancers": [ { "ContainerName": "DemoApp", "ContainerPort": 80, "TargetGroupArn": { "Ref": "ALBTargetGroupBlue" } } ] } }, "PrimaryTaskSet": { "Type": "AWS::ECS::PrimaryTaskSet", "Properties": { "Cluster": { "Ref": "ECSDemoCluster" }, "Service": { "Ref": "ECSDemoService" }, "TaskSetId": { "Fn::GetAtt": [ "BlueTaskSet", "Id" ] } } } } }

YAML

Parameters: Vpc: Type: 'AWS::EC2::VPC::Id' Subnet1: Type: 'AWS::EC2::Subnet::Id' Subnet2: Type: 'AWS::EC2::Subnet::Id' Transform: - 'AWS::CodeDeployBlueGreen' Hooks: CodeDeployBlueGreenHook: Properties: TrafficRoutingConfig: Type: TimeBasedCanary TimeBasedCanary: StepPercentage: 15 BakeTimeMins: 5 Applications: - Target: Type: 'AWS::ECS::Service' LogicalID: ECSDemoService ECSAttributes: TaskDefinitions: - BlueTaskDefinition - GreenTaskDefinition TaskSets: - BlueTaskSet - GreenTaskSet TrafficRouting: ProdTrafficRoute: Type: 'AWS::ElasticLoadBalancingV2::Listener' LogicalID: ALBListenerProdTraffic TargetGroups: - ALBTargetGroupBlue - ALBTargetGroupGreen Type: 'AWS::CodeDeploy::BlueGreen' Resources: ExampleSecurityGroup: Type: 'AWS::EC2::SecurityGroup' Properties: GroupDescription: Security group for ec2 access VpcId: !Ref Vpc SecurityGroupIngress: - IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: 0.0.0.0/0 - IpProtocol: tcp FromPort: 8080 ToPort: 8080 CidrIp: 0.0.0.0/0 - IpProtocol: tcp FromPort: 22 ToPort: 22 CidrIp: 0.0.0.0/0 ALBTargetGroupBlue: Type: 'AWS::ElasticLoadBalancingV2::TargetGroup' Properties: HealthCheckIntervalSeconds: 5 HealthCheckPath: / HealthCheckPort: '80' HealthCheckProtocol: HTTP HealthCheckTimeoutSeconds: 2 HealthyThresholdCount: 2 Matcher: HttpCode: '200' Port: 80 Protocol: HTTP Tags: - Key: Group Value: Example TargetType: ip UnhealthyThresholdCount: 4 VpcId: !Ref Vpc ALBTargetGroupGreen: Type: 'AWS::ElasticLoadBalancingV2::TargetGroup' Properties: HealthCheckIntervalSeconds: 5 HealthCheckPath: / HealthCheckPort: '80' HealthCheckProtocol: HTTP HealthCheckTimeoutSeconds: 2 HealthyThresholdCount: 2 Matcher: HttpCode: '200' Port: 80 Protocol: HTTP Tags: - Key: Group Value: Example TargetType: ip UnhealthyThresholdCount: 4 VpcId: !Ref Vpc ExampleALB: Type: 'AWS::ElasticLoadBalancingV2::LoadBalancer' Properties: Scheme: internet-facing SecurityGroups: - !Ref ExampleSecurityGroup Subnets: - !Ref Subnet1 - !Ref Subnet2 Tags: - Key: Group Value: Example Type: application IpAddressType: ipv4 ALBListenerProdTraffic: Type: 'AWS::ElasticLoadBalancingV2::Listener' Properties: DefaultActions: - Type: forward ForwardConfig: TargetGroups: - TargetGroupArn: !Ref ALBTargetGroupBlue Weight: 1 LoadBalancerArn: !Ref ExampleALB Port: 80 Protocol: HTTP ALBListenerProdRule: Type: 'AWS::ElasticLoadBalancingV2::ListenerRule' Properties: Actions: - Type: forward ForwardConfig: TargetGroups: - TargetGroupArn: !Ref ALBTargetGroupBlue Weight: 1 Conditions: - Field: http-header HttpHeaderConfig: HttpHeaderName: User-Agent Values: - Mozilla ListenerArn: !Ref ALBListenerProdTraffic Priority: 1 ECSTaskExecutionRole: Type: 'AWS::IAM::Role' Properties: AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Sid: '' Effect: Allow Principal: Service: ecs-tasks.amazonaws.com Action: 'sts:AssumeRole' ManagedPolicyArns: - 'arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy' BlueTaskDefinition: Type: 'AWS::ECS::TaskDefinition' Properties: ExecutionRoleArn: !GetAtt - ECSTaskExecutionRole - Arn ContainerDefinitions: - Name: DemoApp Image: 'nginxdemos/hello:latest' Essential: true PortMappings: - HostPort: 80 Protocol: tcp ContainerPort: 80 RequiresCompatibilities: - FARGATE NetworkMode: awsvpc Cpu: '256' Memory: '512' Family: ecs-demo ECSDemoCluster: Type: 'AWS::ECS::Cluster' Properties: {} ECSDemoService: Type: 'AWS::ECS::Service' Properties: Cluster: !Ref ECSDemoCluster DesiredCount: 1 DeploymentController: Type: EXTERNAL BlueTaskSet: Type: 'AWS::ECS::TaskSet' Properties: Cluster: !Ref ECSDemoCluster LaunchType: FARGATE NetworkConfiguration: AwsVpcConfiguration: AssignPublicIp: ENABLED SecurityGroups: - !Ref ExampleSecurityGroup Subnets: - !Ref Subnet1 - !Ref Subnet2 PlatformVersion: 1.4.0 Scale: Unit: PERCENT Value: 100 Service: !Ref ECSDemoService TaskDefinition: !Ref BlueTaskDefinition LoadBalancers: - ContainerName: DemoApp ContainerPort: 80 TargetGroupArn: !Ref ALBTargetGroupBlue PrimaryTaskSet: Type: 'AWS::ECS::PrimaryTaskSet' Properties: Cluster: !Ref ECSDemoCluster Service: !Ref ECSDemoService TaskSetId: !GetAtt - BlueTaskSet - Id