使用 AWS CloudFormation 通过 CodeDeploy 执行 ECS 蓝/绿部署 - AWS CloudFormation
AWS 文档中描述的 AWS 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅中国的 AWS 服务入门

使用 AWS CloudFormation 通过 CodeDeploy 执行 ECS 蓝/绿部署

您可以使用 CloudFormation 通过 CodeDeploy 来执行 ECS 蓝/绿部署。蓝/绿部署是 AWS CodeDeploy 提供的一种安全部署策略,旨在最大限度地减少因应用程序版本更改而造成的中断。这通过创建新的应用程序环境(称为绿色)以及当前为实时流量提供服务的应用程序(称为蓝色)来实现。这样就可以在您的实时流量从蓝色传送到绿色并随后关闭蓝色资源之前,对绿色环境进行一段时间的监视和测试。

使用 CloudFormation 执行 ECS 蓝/绿部署时,首先创建一个堆栈模板,为蓝色和绿色应用程序环境定义资源,包括指定要使用的流量路由和稳定设置。接下来,基于该模板创建一个堆栈;这会生成蓝色(当前)应用程序。CloudFormation 仅在堆栈创建过程中创建蓝色资源。绿色部署的资源仅在需要时才会创建。

然后,如果在将来的堆栈更新中更新蓝色应用程序中的任务定义或任务集资源,CloudFormation 会执行以下操作:

  • 生成所有必需的绿色应用程序环境资源

  • 根据指定的流量路由参数转移流量

  • 删除蓝色资源

如果在绿色部署成功和完成之前的任何时刻发生错误,CloudFormation 会将堆栈回滚到启动整个绿色部署之前的状态。

要使 CloudFormation 能够在堆栈上执行蓝/绿部署,请在其堆栈模板中包括以下信息:

  • 模板中调用 AWS::CodeDeployBlueGreen 转换的 Transform 部分,以及调用 AWS::CodeDeploy::BlueGreen 挂钩的 Hook 部分。

  • 如果在堆栈更新过程中替换,则至少一个 ECS 资源将触发蓝/绿部署。目前,这些资源是 AWS::ECS::TaskDefinitionAWS::ECS::TaskSet

然后,如果您启动堆栈更新来更新上述资源的任何属性,而且该更新需要 CloudFormation 替换资源,则 CloudFormation 会执行如上所述的蓝/绿部署。有关资源更新行为的更多信息,请参阅堆栈资源的更新行为

在许多情况下,您可能想要在创建堆栈之前将堆栈模板设置为启用蓝/绿部署。但是,您也可以增加功能,以便让 CloudFormation 对现有堆栈执行蓝/绿部署。为此,请将必要的信息添加到堆栈的现有模板中。

此外,我们建议您在启动堆栈更新之前让 CloudFormation 为绿色部署生成更改集。这样您就可以查看将对堆栈进行的实际更改。

使用 CloudFormation 资源对蓝/绿部署建模

为了使用 CodeDeploy 通过 CloudFormation 执行 ECS 蓝/绿部署,您的模板需要包含为部署建模所需的资源,例如 Amazon ECS 服务和负载均衡器。如需更详细地了解这些资源代表了什么,请参阅 AWS CodeDeploy 用户指南中的开始 Amazon ECS 部署之前

要求 资源 必需/可选 如果替换,则触发蓝/绿部署
Amazon ECS 集群 AWS::ECS::Cluster 可选。可以使用默认集群。
Amazon ECS 服务 AWS::ECS::Service 必填项。
应用程序或网络负载均衡器 AWS::ECS::Service LoadBalancer 必填项。
生产侦听器 AWS::ElasticLoadBalancingV2::Listener 必填项。
测试侦听器 AWS::ElasticLoadBalancingV2::Listener 可选。
两个目标组 AWS::ElasticLoadBalancingV2::TargetGroup 必填项。
Amazon ECS 任务定义 AWS::ECS::TaskDefinition 必填项。
您的 Amazon ECS 应用程序的容器 AWS::ECS::TaskDefinition ContainerDefinition Name 必填项。
您的替换任务集的端口 AWS::ECS::TaskDefinition PortMapping ContainerPort 必填项。

触发绿色部署的资源更新

如果您执行堆栈更新,以便更新需要为以下 ECS 资源替换的任何属性,CloudFormation 将启动绿色部署:

更新不需要进行资源替换的这些资源中的属性不会触发绿色部署。

您不能在同一堆栈更新中包括上述资源的更新以及对其他资源的更新。如果您需要更新上面列表中的资源以及同一堆栈中的其他资源,请执行以下操作之一:

  • 执行两个单独的堆栈更新操作:一个仅包括对上述资源的更新,另一个单独的堆栈更新包括对任何其他资源的更改。

  • 从模板中删除 TransformHook 部分,然后执行堆栈更新。在这种情况下,CloudFormation 不会执行绿色部署。

使用 CloudFormation 管理 ECS 蓝/绿部署时的注意事项

在使用 CloudFormation 定义蓝/绿部署时,应考虑以下事项:

  • 只有对特定资源的更新才会触发绿色部署,如触发 ECS 蓝/绿部署的资源更新中所述。

  • 您不能在同一堆栈更新中包括触发绿色部署的资源更新和对其他资源的更新,如触发 ECS 蓝/绿部署的资源更新中所述。

  • 您只能将单个 ECS 服务指定为部署目标。

  • 在绿色部署期间,CodeDeploy 服务无法更新其值由 CloudFormation 模糊处理的参数,并且会导致错误和堆栈更新失败。包括:

  • 要取消仍在进行中的绿色部署,请在 CloudFormation 中,而不是在 CodeDeploy 或 ECS 中取消堆栈更新。有关更多信息,请参阅取消堆栈更新。(更新完成之后,将无法取消它。但是,您可以通过任何先前的设置再次更新堆栈。)

  • 对于定义了蓝/绿 ECS 部署的模板,当前不支持声明输出值或从其他堆栈导入值

  • 对于定义了蓝/绿 ECS 部署的模板,当前不支持导入现有资源

  • 您不能在包含嵌套堆栈资源的模板中使用 AWS::CodeDeploy::BlueGreen 挂钩。

  • 您不能在嵌套堆栈中使用 AWS::CodeDeploy::BlueGreen 挂钩。

如需了解使用 CloudFormation 通过 CodeDeploy 执行 ECS 蓝/绿部署与仅使用 CodeDeploy 的标准 Amazon ECS 部署有何不同,请参阅 AWS CodeDeploy 用户指南中的通过 AWS CloudFormation 执行的 Amazon ECS 蓝/绿部署与标准 Amazon ECS 部署之间的差异

准备模板以执行 ECS 蓝/绿部署

要在堆栈上启用蓝/绿部署,请在执行堆栈更新之前在堆栈模板中包含以下部分。

  • AWS::CodeDeployBlueGreen 转换的引用添加到模板中:

    "Transform": [ "AWS::CodeDeployBlueGreen" ],
  • 添加调用 AWS::CodeDeploy::BlueGreen 挂钩并指定部署属性的 Hook 部分。使用下面的模板参考作为指导。

  • Resources 部分中,定义部署的蓝色和绿色资源。

您可以在第一次创建模板时(即创建堆栈本身之前)添加这些部分,也可以在执行堆栈更新之前将它们添加到现有模板中。如果是为新堆栈指定蓝/绿部署,则 CloudFormation 只在堆栈创建期间创建蓝色资源 — 仅当在堆栈更新期间需要绿色部署的资源时才会创建它们。

查看蓝/绿部署的更改集

我们强烈建议您在执行会启动绿色部署的堆栈更新之前创建更改集。这使您能够在执行堆栈更新之前查看对堆栈作出的实际更改。请注意,资源更改可能不会按堆栈更新期间的执行顺序列出。有关更多信息,请参阅使用更改集更新堆栈

查看蓝/绿部署的堆栈事件

您可以在 Stack (堆栈) 页面的 Events (事件) 选项卡上,使用 AWS CLI 查看 ECS 部署的每个步骤生成的堆栈事件。有关更多信息,请参阅监控堆栈更新的进度

模板参考

"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": {"Ref": "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

默认配置为基于时间的 canary 流量转移,具有 15% 的步骤百分比和五分钟的稳定时间。

Type

部署配置使用的流量转移类型。

有效值:AllAtOnce | TimeBasedCanary | TimeBasedLinear

Required: Yes

TimeBasedCanary

指定一个配置,以两个增量将流量从部署的一个版本转移到另一个版本。

Required: Conditional:如果指定 TimeBasedCanary 作为流量路由类型,则必须包含 TimeBasedCanary 参数。

StepPercentage

TimeBasedCanary 部署的第一个增量中要转移的流量百分比。步骤百分比必须为 14% 或更大。

Required: No

BakeTimeMins

TimeBasedCanary 部署的第一次和第二次流量转移之间的分钟数。

Required: No

TimeBasedLinear

指定一个配置,以相等的增量将流量从部署的一个版本转移到另一个版本,每个增量之间的分钟数相等。

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 并提供 SucceededFailed 结果。

有关更多信息,请参阅 AWS CodeDeploy 用户指南中的 AppSpec“挂钩”部分

Required: No

BeforeInstall

用于在创建替换任务集之前运行任务的函数。

Required: No

AfterInstall

用于在创建替换任务集并且其中一个目标组与之关联后运行任务的函数。

Required: No

AfterAllowTestTraffic

用于在测试侦听器为替换任务集提供流量后运行任务的函数。

Required: No

BeforeAllowTraffic

用于在第二个目标组与替换任务集关联之后但在流量转移到替换任务集之前运行任务的函数。

Required: No

AfterAllowTraffic

用于在第二个目标组为替换任务集提供流量后运行任务的函数。

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::CodeDeploy::BlueGreen

Required: Yes

蓝/绿部署所需的 IAM 权限

为了使 CloudFormation 成功执行蓝绿部署,您必须具有以下 CodeDeploy 权限:

  • codedeploy:Get*

  • codedeploy:CreateCloudFormationDeployment

有关更多信息,请参阅 AWS Identity and Access Management 用户指南 中的 AWS 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": "AllAtOnce" }, "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.3.0", "Scale": { "Unit": "PERCENT", "Value": 1 }, "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: AllAtOnce 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: !Ref ECSTaskExecutionRole 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.3.0 Scale: Unit: PERCENT Value: 1 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