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 サービスによって更新できず、エラーやスタックの更新に失敗します。具体的には次のとおりです。
NoEcho
属性で定義されたパラメータ。動的な参照を使用して外部サービスから値を取得するパラメータ。詳細については、「動的な参照を使用してテンプレート値を指定する」を参照してください。
まだ進行中のグリーンデプロイをキャンセルするには、CodeDeploy または ECS ではなく、CloudFormation でスタックの更新をキャンセルします。詳細については、「スタック更新のキャンセル」を参照してください。(更新が完了した後、操作をキャンセルできません。ただし、前の設定でスタックを再度更新することはできます。)
現在、ブルー/グリーン ECS デプロイを定義するテンプレートでは、出力 を宣言したり Fn::ImportValue を使用したりすることで他のスタックから値をインポートすることはできません。
ブルー/グリーン ECS デプロイを定義するテンプレートでは、既存リソースを CloudFormation 管理に取り込む は現在サポートされていません。
ネストされたスタックリソースを含むテンプレートでは、
AWS::CodeDeploy::BlueGreen
フックを使用できません。ネストされたスタックでは
AWS::CodeDeploy::BlueGreen
フックを使用できません。
CloudFormation を使用して CodeDeploy を介して ECS ブルー/グリーンデプロイを実行する場合、プロセスは CodeDeploy のみを使用する標準の Amazon ECS デプロイとは異なります。詳細については、[AWS CodeDeployユーザーガイド]の[AWS CloudFormationを使用した CodeDeploy による Perform Amazon ECS Blue/Green デプロイの実行]を参照してください。
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