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 サービスは、1 つだけです。

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

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

  • 現在、ブルー/グリーン ECS デプロイを定義するテンプレートでは、出力 を宣言したり Fn::ImportValue を使用したりすることで他のスタックから値をインポートすることはできません。

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

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

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

CloudFormation を使用して、CodeDeploy を介して ECS ブルー/グリーンデプロイを実行する方法の情報については、CodeDeploy のみを使用した場合の標準 Amazon ECS デプロイとは異なります。「AWS CodeDeploy ユーザーガイド」の「CloudFormation と標準 Amazon ECS デプロイを介した Amazon ECS ブルー/グリーンデプロイの相違点」を参照してください。

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

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

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

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

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

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

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

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

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

ECS デプロイの各ステップで生成されるスタックイベントは、[Stack] (スタック) ページの [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)とし、テンプレート内で一意である必要があります。

必須: はい

Properties

フックのプロパティ。

必須: はい

TrafficRoutingConfig

トラフィックルーティングの構成値。

必須: いいえ

デフォルトの構成は、時間ベースの canary トラフィックシフトで、15% のステップ率と 5 分間のベイク時間があります。

Type

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

有効値: AllAtOnce | TimeBasedCanary | TimeBasedLinear

必須: はい

TimeBasedCanary

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

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

StepPercentage

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

必須: いいえ

BakeTimeMins

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

必須: いいえ

TimeBasedLinear

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

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

StepPercentage

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

必須: いいえ

BakeTimeMins

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

必須: いいえ

AdditionalOptions

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

必須: いいえ

TerminationWaitTimeInMinutes

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

必須: いいえ

LifecycleEventHooks

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

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

必須: いいえ

BeforeInstall

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

必須: いいえ

AfterInstall

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

必須: いいえ

AfterAllowTestTraffic

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

必須: いいえ

BeforeAllowTraffic

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

必須: いいえ

AfterAllowTraffic

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

必須: いいえ

ServiceRole

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

必須: はい

Applications

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

必須: はい

Target

必須: はい

Type

リソースのタイプ。

必須: はい

LogicalID

リソースの論理 ID。

必須: はい

ECSAttributes

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

必須: はい

TaskDefinitions

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

必須: はい

TaskSets

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

必須: はい

TrafficRouting

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

必須: はい

ProdTrafficRoute

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

必須: はい

Type

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

必須: はい

LogicalID

リソースの論理的な ID。

必須: はい

TestTrafficRoute

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

必須: はい

Type

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

必須: はい

LogicalID

リソースの論理的な ID。

必須: いいえ

TargetGroups

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

必須: はい

Type

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

必須: はい

ブルー/グリーンデプロイに必要な 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