Parameters: ProjectName: Type: String Description: Project name to link stacks AppMeshXdsEndpoint: Type: String Description: App Mesh XDS Endpoint Override Default: "" EnvoyImage: Type: String Description: Envoy container image FrontAppImage: Type: String Description: Front app container image ColorAppImage: Type: String Description: Color app container image Resources: Mesh: Type: AWS::AppMesh::Mesh Properties: MeshName: !Ref ProjectName ColorRouter: Type: AWS::AppMesh::VirtualRouter DependsOn: - Mesh Properties: MeshName: !Ref ProjectName VirtualRouterName: 'color-router' Spec: Listeners: - PortMapping: Port: 8080 Protocol: http ColorService: Type: AWS::AppMesh::VirtualService DependsOn: - ColorRouter Properties: MeshName: !Ref ProjectName VirtualServiceName: !Sub 'color.${ProjectName}.local' Spec: Provider: VirtualRouter: VirtualRouterName: 'color-router' FrontNode: Type: AWS::AppMesh::VirtualNode DependsOn: - ColorRouter Properties: MeshName: !Ref ProjectName VirtualNodeName: 'front-node' Spec: Backends: - VirtualService: VirtualServiceName: !Sub 'color.${ProjectName}.local' Listeners: - PortMapping: Port: 8080 Protocol: http HealthCheck: Protocol: http Path: '/ping' HealthyThreshold: 2 UnhealthyThreshold: 2 TimeoutMillis: 2000 IntervalMillis: 5000 ServiceDiscovery: AWSCloudMap: NamespaceName: !Sub '${ProjectName}.local' ServiceName: 'front' BlueNode: Type: AWS::AppMesh::VirtualNode DependsOn: - Mesh Properties: MeshName: !Ref ProjectName VirtualNodeName: 'blue-node' Spec: Listeners: - PortMapping: Port: 8080 Protocol: http HealthCheck: Protocol: http Path: '/ping' HealthyThreshold: 2 UnhealthyThreshold: 2 TimeoutMillis: 2000 IntervalMillis: 5000 ServiceDiscovery: AWSCloudMap: NamespaceName: !Sub '${ProjectName}.local' ServiceName: 'color' Attributes: - Key: 'ECS_TASK_DEFINITION_FAMILY' Value: 'blue' ColorRouteBlue: Type: AWS::AppMesh::Route DependsOn: - ColorRouter - BlueNode Properties: MeshName: !Ref ProjectName VirtualRouterName: 'color-router' RouteName: 'color-route-blue' Spec: Priority: 900 HttpRoute: Match: Prefix: '/' Headers: - Name: "color_header" Match: Range: Start: 100 End: 150 Action: WeightedTargets: - VirtualNode: 'blue-node' Weight: 100 GreenNode: Type: AWS::AppMesh::VirtualNode DependsOn: - Mesh Properties: MeshName: !Ref ProjectName VirtualNodeName: 'green-node' Spec: Listeners: - PortMapping: Port: 8080 Protocol: http HealthCheck: Protocol: http Path: '/ping' HealthyThreshold: 2 UnhealthyThreshold: 2 TimeoutMillis: 2000 IntervalMillis: 5000 ServiceDiscovery: AWSCloudMap: NamespaceName: !Sub '${ProjectName}.local' ServiceName: 'color' Attributes: - Key: 'ECS_TASK_DEFINITION_FAMILY' Value: 'green' ColorRouteGreen: Type: AWS::AppMesh::Route DependsOn: - ColorRouter - GreenNode Properties: MeshName: !Ref ProjectName VirtualRouterName: 'color-router' RouteName: 'color-route-green' Spec: Priority: 2 HttpRoute: Match: Prefix: '/' Headers: - Name: "color_header" Match: Regex: "redor.*" Action: WeightedTargets: - VirtualNode: 'green-node' Weight: 100 RedNode: Type: AWS::AppMesh::VirtualNode DependsOn: - Mesh Properties: MeshName: !Ref ProjectName VirtualNodeName: 'red-node' Spec: Listeners: - PortMapping: Port: 8080 Protocol: http HealthCheck: Protocol: http Path: '/ping' HealthyThreshold: 2 UnhealthyThreshold: 2 TimeoutMillis: 2000 IntervalMillis: 5000 ServiceDiscovery: AWSCloudMap: NamespaceName: !Sub '${ProjectName}.local' ServiceName: 'color' Attributes: - Key: 'ECS_TASK_DEFINITION_FAMILY' Value: 'red' ColorRouteRed: Type: AWS::AppMesh::Route DependsOn: - ColorRouter - RedNode Properties: MeshName: !Ref ProjectName VirtualRouterName: 'color-router' RouteName: 'color-route-red' Spec: Priority: 5 HttpRoute: Match: Prefix: '/' Headers: - Name: "color_header" Match: Prefix: "redorgreen" Action: WeightedTargets: - VirtualNode: 'red-node' Weight: 100 YellowNode: Type: AWS::AppMesh::VirtualNode DependsOn: - Mesh Properties: MeshName: !Ref ProjectName VirtualNodeName: 'yellow-node' Spec: Listeners: - PortMapping: Port: 8080 Protocol: http HealthCheck: Protocol: http Path: '/ping' HealthyThreshold: 2 UnhealthyThreshold: 2 TimeoutMillis: 2000 IntervalMillis: 5000 ServiceDiscovery: AWSCloudMap: NamespaceName: !Sub '${ProjectName}.local' ServiceName: 'color' Attributes: - Key: 'ECS_TASK_DEFINITION_FAMILY' Value: 'yellow' ColorRouteYellow: Type: AWS::AppMesh::Route DependsOn: - ColorRouter - YellowNode Properties: MeshName: !Ref ProjectName VirtualRouterName: 'color-router' RouteName: 'color-route-yellow' Spec: Priority: 990 HttpRoute: Match: Prefix: '/' Headers: - Name: "color_header" Action: WeightedTargets: - VirtualNode: 'yellow-node' Weight: 100 PurpleNode: Type: AWS::AppMesh::VirtualNode DependsOn: - Mesh Properties: MeshName: !Ref ProjectName VirtualNodeName: 'purple-node' Spec: Listeners: - PortMapping: Port: 8080 Protocol: http HealthCheck: Protocol: http Path: '/ping' HealthyThreshold: 2 UnhealthyThreshold: 2 TimeoutMillis: 2000 IntervalMillis: 5000 ServiceDiscovery: AWSCloudMap: NamespaceName: !Sub '${ProjectName}.local' ServiceName: 'color' Attributes: - Key: 'ECS_TASK_DEFINITION_FAMILY' Value: 'purple' ColorRoutePurple: Type: AWS::AppMesh::Route DependsOn: - ColorRouter - PurpleNode Properties: MeshName: !Ref ProjectName VirtualRouterName: 'color-router' RouteName: 'color-route-purple' Spec: Priority: 1000 HttpRoute: Match: Prefix: '/' Headers: - Name: "color_header" Invert: true Action: WeightedTargets: - VirtualNode: 'purple-node' Weight: 100 WhiteNode: Type: AWS::AppMesh::VirtualNode DependsOn: - Mesh Properties: MeshName: !Ref ProjectName VirtualNodeName: 'white-node' Spec: Listeners: - PortMapping: Port: 8080 Protocol: http HealthCheck: Protocol: http Path: '/ping' HealthyThreshold: 2 UnhealthyThreshold: 2 TimeoutMillis: 2000 IntervalMillis: 5000 ServiceDiscovery: AWSCloudMap: NamespaceName: !Sub '${ProjectName}.local' ServiceName: 'color' Attributes: - Key: 'ECS_TASK_DEFINITION_FAMILY' Value: 'white' ColorRouteWhite: Type: AWS::AppMesh::Route DependsOn: - ColorRouter - WhiteNode Properties: MeshName: !Ref ProjectName VirtualRouterName: 'color-router' RouteName: 'color-route-white' Spec: Priority: 995 HttpRoute: Match: Prefix: '/' Method: "GET" Action: WeightedTargets: - VirtualNode: 'white-node' Weight: 100 BlackNode: Type: AWS::AppMesh::VirtualNode DependsOn: - Mesh Properties: MeshName: !Ref ProjectName VirtualNodeName: 'black-node' Spec: Listeners: - PortMapping: Port: 8080 Protocol: http HealthCheck: Protocol: http Path: '/ping' HealthyThreshold: 2 UnhealthyThreshold: 2 TimeoutMillis: 2000 IntervalMillis: 5000 ServiceDiscovery: AWSCloudMap: NamespaceName: !Sub '${ProjectName}.local' ServiceName: 'color' Attributes: - Key: 'ECS_TASK_DEFINITION_FAMILY' Value: 'black' ColorRouteBlack: Type: AWS::AppMesh::Route DependsOn: - ColorRouter - BlackNode Properties: MeshName: !Ref ProjectName VirtualRouterName: 'color-router' RouteName: 'color-route-black' Spec: Priority: 300 HttpRoute: Match: Prefix: '/' Action: WeightedTargets: - VirtualNode: 'black-node' Weight: 100 Cluster: Type: AWS::ECS::Cluster Properties: ClusterName: !Ref ProjectName SecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: "Security group for the instances" VpcId: Fn::ImportValue: !Sub '${ProjectName}:VPC' SecurityGroupIngress: - CidrIp: Fn::ImportValue: !Sub '${ProjectName}:VpcCIDR' IpProtocol: -1 LogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: !Sub '${ProjectName}-log-group' RetentionInDays: 30 TaskIamRole: Type: AWS::IAM::Role Properties: Path: / AssumeRolePolicyDocument: | { "Statement": [{ "Effect": "Allow", "Principal": { "Service": [ "ecs-tasks.amazonaws.com" ]}, "Action": [ "sts:AssumeRole" ] }] } ManagedPolicyArns: - arn:aws:iam::aws:policy/CloudWatchFullAccess - arn:aws:iam::aws:policy/AWSAppMeshEnvoyAccess TaskExecutionIamRole: Type: AWS::IAM::Role Properties: Path: / AssumeRolePolicyDocument: | { "Statement": [{ "Effect": "Allow", "Principal": { "Service": [ "ecs-tasks.amazonaws.com" ]}, "Action": [ "sts:AssumeRole" ] }] } ManagedPolicyArns: - arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly - arn:aws:iam::aws:policy/CloudWatchLogsFullAccess CloudMapNamespace: Type: AWS::ServiceDiscovery::PrivateDnsNamespace Properties: Name: !Sub '${ProjectName}.local' Vpc: Fn::ImportValue: !Sub "${ProjectName}:VPC" ColorServiceRegistry: Type: AWS::ServiceDiscovery::Service Properties: Name: 'color' DnsConfig: NamespaceId: !GetAtt 'CloudMapNamespace.Id' DnsRecords: - Type: A TTL: 300 HealthCheckCustomConfig: FailureThreshold: 1 FrontServiceRegistry: Type: AWS::ServiceDiscovery::Service Properties: Name: 'front' DnsConfig: NamespaceId: !GetAtt 'CloudMapNamespace.Id' DnsRecords: - Type: A TTL: 300 HealthCheckCustomConfig: FailureThreshold: 1 PublicLoadBalancerSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: 'Access to the public facing load balancer' VpcId: Fn::ImportValue: !Sub "${ProjectName}:VPC" SecurityGroupIngress: - CidrIp: 0.0.0.0/0 IpProtocol: tcp FromPort: 80 ToPort: 80 PublicLoadBalancer: Type: AWS::ElasticLoadBalancingV2::LoadBalancer Properties: Scheme: internet-facing LoadBalancerAttributes: - Key: idle_timeout.timeout_seconds Value: '30' Subnets: - Fn::ImportValue: !Sub '${ProjectName}:PublicSubnet1' - Fn::ImportValue: !Sub '${ProjectName}:PublicSubnet2' SecurityGroups: - !Ref PublicLoadBalancerSecurityGroup WebTargetGroup: Type: AWS::ElasticLoadBalancingV2::TargetGroup Properties: HealthCheckIntervalSeconds: 6 HealthCheckPath: '/ping' HealthCheckProtocol: HTTP HealthCheckTimeoutSeconds: 5 HealthyThresholdCount: 2 TargetType: ip Name: !Sub '${ProjectName}-webtarget' Port: 80 Protocol: HTTP UnhealthyThresholdCount: 2 TargetGroupAttributes: - Key: deregistration_delay.timeout_seconds Value: 120 VpcId: Fn::ImportValue: !Sub "${ProjectName}:VPC" PublicLoadBalancerListener: DependsOn: - PublicLoadBalancer Type: AWS::ElasticLoadBalancingV2::Listener Properties: DefaultActions: - TargetGroupArn: !Ref WebTargetGroup Type: 'forward' LoadBalancerArn: !Ref PublicLoadBalancer Port: 80 Protocol: HTTP WebLoadBalancerRule: Type: AWS::ElasticLoadBalancingV2::ListenerRule Properties: Actions: - TargetGroupArn: !Ref WebTargetGroup Type: 'forward' Conditions: - Field: path-pattern Values: - '*' ListenerArn: !Ref PublicLoadBalancerListener Priority: 1 FrontTaskDef: Type: AWS::ECS::TaskDefinition Properties: RequiresCompatibilities: - 'FARGATE' Family: 'front' NetworkMode: 'awsvpc' Cpu: 256 Memory: 512 TaskRoleArn: !Ref TaskIamRole ExecutionRoleArn: !Ref TaskExecutionIamRole ProxyConfiguration: Type: 'APPMESH' ContainerName: 'envoy' ProxyConfigurationProperties: - Name: 'IgnoredUID' Value: '1337' - Name: 'ProxyIngressPort' Value: '15000' - Name: 'ProxyEgressPort' Value: '15001' - Name: 'AppPorts' Value: '8080' - Name: 'EgressIgnoredIPs' Value: '169.254.170.2,169.254.169.254' ContainerDefinitions: - Name: 'app' Image: !Ref FrontAppImage Essential: true LogConfiguration: LogDriver: 'awslogs' Options: awslogs-group: !Sub '${ProjectName}-log-group' awslogs-region: !Ref AWS::Region awslogs-stream-prefix: 'feapp' PortMappings: - ContainerPort: 8080 Protocol: 'tcp' Environment: - Name: 'COLOR_HOST' Value: !Sub 'color.${ProjectName}.local:8080' - Name: envoy Image: !Ref EnvoyImage Essential: true User: '1337' Ulimits: - Name: "nofile" HardLimit: 15000 SoftLimit: 15000 PortMappings: - ContainerPort: 9901 Protocol: 'tcp' - ContainerPort: 15000 Protocol: 'tcp' - ContainerPort: 15001 Protocol: 'tcp' HealthCheck: Command: - 'CMD-SHELL' - 'curl -s http://localhost:9901/server_info | grep state | grep -q LIVE' Interval: 5 Timeout: 2 Retries: 3 LogConfiguration: LogDriver: 'awslogs' Options: awslogs-group: !Sub '${ProjectName}-log-group' awslogs-region: !Ref AWS::Region awslogs-stream-prefix: 'feapp-envoy' Environment: - Name: 'APPMESH_RESOURCE_ARN' Value: !Sub 'mesh/${ProjectName}/virtualNode/front-node' - Name: 'ENVOY_LOG_LEVEL' Value: 'debug' BlueTaskDef: Type: AWS::ECS::TaskDefinition Properties: RequiresCompatibilities: - 'FARGATE' Family: 'blue' NetworkMode: 'awsvpc' Cpu: 256 Memory: 512 TaskRoleArn: !Ref TaskIamRole ExecutionRoleArn: !Ref TaskExecutionIamRole ProxyConfiguration: Type: 'APPMESH' ContainerName: 'envoy' ProxyConfigurationProperties: - Name: 'IgnoredUID' Value: '1337' - Name: 'ProxyIngressPort' Value: '15000' - Name: 'ProxyEgressPort' Value: '15001' - Name: 'AppPorts' Value: '8080' - Name: 'EgressIgnoredIPs' Value: '169.254.170.2,169.254.169.254' ContainerDefinitions: - Name: 'app' Image: !Ref ColorAppImage Essential: true DependsOn: - ContainerName: 'envoy' Condition: 'HEALTHY' LogConfiguration: LogDriver: 'awslogs' Options: awslogs-group: !Sub ${ProjectName}-log-group awslogs-region: !Ref AWS::Region awslogs-stream-prefix: 'blue' PortMappings: - ContainerPort: 8080 Protocol: 'tcp' Environment: - Name: 'COLOR' Value: 'blue' - Name: envoy Image: !Ref EnvoyImage Essential: true User: '1337' Ulimits: - Name: "nofile" HardLimit: 15000 SoftLimit: 15000 PortMappings: - ContainerPort: 9901 Protocol: 'tcp' - ContainerPort: 15000 Protocol: 'tcp' - ContainerPort: 15001 Protocol: 'tcp' HealthCheck: Command: - 'CMD-SHELL' - 'curl -s http://localhost:9901/server_info | grep state | grep -q LIVE' Interval: 5 Timeout: 2 Retries: 3 LogConfiguration: LogDriver: 'awslogs' Options: awslogs-group: !Sub '${ProjectName}-log-group' awslogs-region: !Ref AWS::Region awslogs-stream-prefix: 'blue-envoy' Environment: - Name: 'APPMESH_RESOURCE_ARN' Value: !Sub 'mesh/${ProjectName}/virtualNode/blue-node' GreenTaskDef: Type: AWS::ECS::TaskDefinition Properties: RequiresCompatibilities: - 'FARGATE' Family: 'green' NetworkMode: 'awsvpc' Cpu: 256 Memory: 512 TaskRoleArn: !Ref TaskIamRole ExecutionRoleArn: !Ref TaskExecutionIamRole ProxyConfiguration: Type: 'APPMESH' ContainerName: 'envoy' ProxyConfigurationProperties: - Name: 'IgnoredUID' Value: '1337' - Name: 'ProxyIngressPort' Value: '15000' - Name: 'ProxyEgressPort' Value: '15001' - Name: 'AppPorts' Value: '8080' - Name: 'EgressIgnoredIPs' Value: '169.254.170.2,169.254.169.254' ContainerDefinitions: - Name: 'app' Image: !Ref ColorAppImage Essential: true DependsOn: - ContainerName: 'envoy' Condition: 'HEALTHY' LogConfiguration: LogDriver: 'awslogs' Options: awslogs-group: !Sub '${ProjectName}-log-group' awslogs-region: !Ref AWS::Region awslogs-stream-prefix: 'green' PortMappings: - ContainerPort: 8080 Protocol: 'tcp' Environment: - Name: 'COLOR' Value: 'green' - Name: envoy Image: !Ref EnvoyImage Essential: true User: '1337' Ulimits: - Name: "nofile" HardLimit: 15000 SoftLimit: 15000 PortMappings: - ContainerPort: 9901 Protocol: 'tcp' - ContainerPort: 15000 Protocol: 'tcp' - ContainerPort: 15001 Protocol: 'tcp' HealthCheck: Command: - 'CMD-SHELL' - 'curl -s http://localhost:9901/server_info | grep state | grep -q LIVE' Interval: 5 Timeout: 2 Retries: 3 LogConfiguration: LogDriver: 'awslogs' Options: awslogs-group: !Sub '${ProjectName}-log-group' awslogs-region: !Ref AWS::Region awslogs-stream-prefix: 'green-envoy' Environment: - Name: 'APPMESH_RESOURCE_ARN' Value: !Sub 'mesh/${ProjectName}/virtualNode/green-node' RedTaskDef: Type: AWS::ECS::TaskDefinition Properties: RequiresCompatibilities: - 'FARGATE' Family: 'red' NetworkMode: 'awsvpc' Cpu: 256 Memory: 512 TaskRoleArn: !Ref TaskIamRole ExecutionRoleArn: !Ref TaskExecutionIamRole ProxyConfiguration: Type: 'APPMESH' ContainerName: 'envoy' ProxyConfigurationProperties: - Name: 'IgnoredUID' Value: '1337' - Name: 'ProxyIngressPort' Value: '15000' - Name: 'ProxyEgressPort' Value: '15001' - Name: 'AppPorts' Value: '8080' - Name: 'EgressIgnoredIPs' Value: '169.254.170.2,169.254.169.254' ContainerDefinitions: - Name: 'app' Image: !Ref ColorAppImage Essential: true DependsOn: - ContainerName: 'envoy' Condition: 'HEALTHY' LogConfiguration: LogDriver: 'awslogs' Options: awslogs-group: !Sub '${ProjectName}-log-group' awslogs-region: !Ref AWS::Region awslogs-stream-prefix: 'red' PortMappings: - ContainerPort: 8080 Protocol: 'tcp' Environment: - Name: 'COLOR' Value: 'red' - Name: envoy Image: !Ref EnvoyImage Essential: true User: '1337' Ulimits: - Name: "nofile" HardLimit: 15000 SoftLimit: 15000 PortMappings: - ContainerPort: 9901 Protocol: 'tcp' - ContainerPort: 15000 Protocol: 'tcp' - ContainerPort: 15001 Protocol: 'tcp' HealthCheck: Command: - 'CMD-SHELL' - 'curl -s http://localhost:9901/server_info | grep state | grep -q LIVE' Interval: 5 Timeout: 2 Retries: 3 LogConfiguration: LogDriver: 'awslogs' Options: awslogs-group: !Sub '${ProjectName}-log-group' awslogs-region: !Ref AWS::Region awslogs-stream-prefix: 'red-envoy' Environment: - Name: 'APPMESH_RESOURCE_ARN' Value: !Sub 'mesh/${ProjectName}/virtualNode/red-node' YellowTaskDef: Type: AWS::ECS::TaskDefinition Properties: RequiresCompatibilities: - 'FARGATE' Family: 'yellow' NetworkMode: 'awsvpc' Cpu: 256 Memory: 512 TaskRoleArn: !Ref TaskIamRole ExecutionRoleArn: !Ref TaskExecutionIamRole ProxyConfiguration: Type: 'APPMESH' ContainerName: 'envoy' ProxyConfigurationProperties: - Name: 'IgnoredUID' Value: '1337' - Name: 'ProxyIngressPort' Value: '15000' - Name: 'ProxyEgressPort' Value: '15001' - Name: 'AppPorts' Value: '8080' - Name: 'EgressIgnoredIPs' Value: '169.254.170.2,169.254.169.254' ContainerDefinitions: - Name: 'app' Image: !Ref ColorAppImage Essential: true DependsOn: - ContainerName: 'envoy' Condition: 'HEALTHY' LogConfiguration: LogDriver: 'awslogs' Options: awslogs-group: !Sub '${ProjectName}-log-group' awslogs-region: !Ref AWS::Region awslogs-stream-prefix: 'yellow' PortMappings: - ContainerPort: 8080 Protocol: 'tcp' Environment: - Name: 'COLOR' Value: 'yellow' - Name: envoy Image: !Ref EnvoyImage Essential: true User: '1337' Ulimits: - Name: "nofile" HardLimit: 15000 SoftLimit: 15000 PortMappings: - ContainerPort: 9901 Protocol: 'tcp' - ContainerPort: 15000 Protocol: 'tcp' - ContainerPort: 15001 Protocol: 'tcp' HealthCheck: Command: - 'CMD-SHELL' - 'curl -s http://localhost:9901/server_info | grep state | grep -q LIVE' Interval: 5 Timeout: 2 Retries: 3 LogConfiguration: LogDriver: 'awslogs' Options: awslogs-group: !Sub '${ProjectName}-log-group' awslogs-region: !Ref AWS::Region awslogs-stream-prefix: 'yellow-envoy' Environment: - Name: 'APPMESH_RESOURCE_ARN' Value: !Sub 'mesh/${ProjectName}/virtualNode/yellow-node' PurpleTaskDef: Type: AWS::ECS::TaskDefinition Properties: RequiresCompatibilities: - 'FARGATE' Family: 'purple' NetworkMode: 'awsvpc' Cpu: 256 Memory: 512 TaskRoleArn: !Ref TaskIamRole ExecutionRoleArn: !Ref TaskExecutionIamRole ProxyConfiguration: Type: 'APPMESH' ContainerName: 'envoy' ProxyConfigurationProperties: - Name: 'IgnoredUID' Value: '1337' - Name: 'ProxyIngressPort' Value: '15000' - Name: 'ProxyEgressPort' Value: '15001' - Name: 'AppPorts' Value: '8080' - Name: 'EgressIgnoredIPs' Value: '169.254.170.2,169.254.169.254' ContainerDefinitions: - Name: 'app' Image: !Ref ColorAppImage Essential: true DependsOn: - ContainerName: 'envoy' Condition: 'HEALTHY' LogConfiguration: LogDriver: 'awslogs' Options: awslogs-group: !Sub '${ProjectName}-log-group' awslogs-region: !Ref AWS::Region awslogs-stream-prefix: 'purple' PortMappings: - ContainerPort: 8080 Protocol: 'tcp' Environment: - Name: 'COLOR' Value: 'purple' - Name: envoy Image: !Ref EnvoyImage Essential: true User: '1337' Ulimits: - Name: "nofile" HardLimit: 15000 SoftLimit: 15000 PortMappings: - ContainerPort: 9901 Protocol: 'tcp' - ContainerPort: 15000 Protocol: 'tcp' - ContainerPort: 15001 Protocol: 'tcp' HealthCheck: Command: - 'CMD-SHELL' - 'curl -s http://localhost:9901/server_info | grep state | grep -q LIVE' Interval: 5 Timeout: 2 Retries: 3 LogConfiguration: LogDriver: 'awslogs' Options: awslogs-group: !Sub '${ProjectName}-log-group' awslogs-region: !Ref AWS::Region awslogs-stream-prefix: 'purple-envoy' Environment: - Name: 'APPMESH_RESOURCE_ARN' Value: !Sub 'mesh/${ProjectName}/virtualNode/purple-node' WhiteTaskDef: Type: AWS::ECS::TaskDefinition Properties: RequiresCompatibilities: - 'FARGATE' Family: 'white' NetworkMode: 'awsvpc' Cpu: 256 Memory: 512 TaskRoleArn: !Ref TaskIamRole ExecutionRoleArn: !Ref TaskExecutionIamRole ProxyConfiguration: Type: 'APPMESH' ContainerName: 'envoy' ProxyConfigurationProperties: - Name: 'IgnoredUID' Value: '1337' - Name: 'ProxyIngressPort' Value: '15000' - Name: 'ProxyEgressPort' Value: '15001' - Name: 'AppPorts' Value: '8080' - Name: 'EgressIgnoredIPs' Value: '169.254.170.2,169.254.169.254' ContainerDefinitions: - Name: 'app' Image: !Ref ColorAppImage Essential: true DependsOn: - ContainerName: 'envoy' Condition: 'HEALTHY' LogConfiguration: LogDriver: 'awslogs' Options: awslogs-group: !Sub '${ProjectName}-log-group' awslogs-region: !Ref AWS::Region awslogs-stream-prefix: 'white' PortMappings: - ContainerPort: 8080 Protocol: 'tcp' Environment: - Name: 'COLOR' Value: 'white' - Name: envoy Image: !Ref EnvoyImage Essential: true User: '1337' Ulimits: - Name: "nofile" HardLimit: 15000 SoftLimit: 15000 PortMappings: - ContainerPort: 9901 Protocol: 'tcp' - ContainerPort: 15000 Protocol: 'tcp' - ContainerPort: 15001 Protocol: 'tcp' HealthCheck: Command: - 'CMD-SHELL' - 'curl -s http://localhost:9901/server_info | grep state | grep -q LIVE' Interval: 5 Timeout: 2 Retries: 3 LogConfiguration: LogDriver: 'awslogs' Options: awslogs-group: !Sub '${ProjectName}-log-group' awslogs-region: !Ref AWS::Region awslogs-stream-prefix: 'white-envoy' Environment: - Name: 'APPMESH_RESOURCE_ARN' Value: !Sub 'mesh/${ProjectName}/virtualNode/white-node' BlackTaskDef: Type: AWS::ECS::TaskDefinition Properties: RequiresCompatibilities: - 'FARGATE' Family: 'black' NetworkMode: 'awsvpc' Cpu: 256 Memory: 512 TaskRoleArn: !Ref TaskIamRole ExecutionRoleArn: !Ref TaskExecutionIamRole ProxyConfiguration: Type: 'APPMESH' ContainerName: 'envoy' ProxyConfigurationProperties: - Name: 'IgnoredUID' Value: '1337' - Name: 'ProxyIngressPort' Value: '15000' - Name: 'ProxyEgressPort' Value: '15001' - Name: 'AppPorts' Value: '8080' - Name: 'EgressIgnoredIPs' Value: '169.254.170.2,169.254.169.254' ContainerDefinitions: - Name: 'app' Image: !Ref ColorAppImage Essential: true DependsOn: - ContainerName: 'envoy' Condition: 'HEALTHY' LogConfiguration: LogDriver: 'awslogs' Options: awslogs-group: !Sub '${ProjectName}-log-group' awslogs-region: !Ref AWS::Region awslogs-stream-prefix: 'black' PortMappings: - ContainerPort: 8080 Protocol: 'tcp' Environment: - Name: 'COLOR' Value: 'black' - Name: envoy Image: !Ref EnvoyImage Essential: true User: '1337' Ulimits: - Name: "nofile" HardLimit: 15000 SoftLimit: 15000 PortMappings: - ContainerPort: 9901 Protocol: 'tcp' - ContainerPort: 15000 Protocol: 'tcp' - ContainerPort: 15001 Protocol: 'tcp' HealthCheck: Command: - 'CMD-SHELL' - 'curl -s http://localhost:9901/server_info | grep state | grep -q LIVE' Interval: 5 Timeout: 2 Retries: 3 LogConfiguration: LogDriver: 'awslogs' Options: awslogs-group: !Sub '${ProjectName}-log-group' awslogs-region: !Ref AWS::Region awslogs-stream-prefix: 'black-envoy' Environment: - Name: 'APPMESH_RESOURCE_ARN' Value: !Sub 'mesh/${ProjectName}/virtualNode/black-node' FrontService: Type: AWS::ECS::Service DependsOn: - WebLoadBalancerRule Properties: Cluster: !Ref Cluster DeploymentConfiguration: MaximumPercent: 200 MinimumHealthyPercent: 100 DesiredCount: 1 LaunchType: 'FARGATE' ServiceRegistries: - RegistryArn: !GetAtt 'FrontServiceRegistry.Arn' NetworkConfiguration: AwsvpcConfiguration: AssignPublicIp: DISABLED SecurityGroups: - !Ref SecurityGroup Subnets: - Fn::ImportValue: !Sub '${ProjectName}:PrivateSubnet1' - Fn::ImportValue: !Sub '${ProjectName}:PrivateSubnet2' TaskDefinition: !Ref FrontTaskDef LoadBalancers: - ContainerName: app ContainerPort: 8080 TargetGroupArn: !Ref WebTargetGroup BlueService: Type: AWS::ECS::Service Properties: Cluster: !Ref Cluster DeploymentConfiguration: MaximumPercent: 200 MinimumHealthyPercent: 100 DesiredCount: 1 LaunchType: 'FARGATE' ServiceRegistries: - RegistryArn: !GetAtt 'ColorServiceRegistry.Arn' NetworkConfiguration: AwsvpcConfiguration: AssignPublicIp: DISABLED SecurityGroups: - !Ref SecurityGroup Subnets: - Fn::ImportValue: !Sub '${ProjectName}:PrivateSubnet1' - Fn::ImportValue: !Sub '${ProjectName}:PrivateSubnet2' TaskDefinition: !Ref BlueTaskDef GreenService: Type: AWS::ECS::Service Properties: Cluster: !Ref Cluster DeploymentConfiguration: MaximumPercent: 200 MinimumHealthyPercent: 100 DesiredCount: 1 LaunchType: 'FARGATE' ServiceRegistries: - RegistryArn: !GetAtt 'ColorServiceRegistry.Arn' NetworkConfiguration: AwsvpcConfiguration: AssignPublicIp: DISABLED SecurityGroups: - !Ref SecurityGroup Subnets: - Fn::ImportValue: !Sub '${ProjectName}:PrivateSubnet1' - Fn::ImportValue: !Sub '${ProjectName}:PrivateSubnet2' TaskDefinition: !Ref GreenTaskDef RedService: Type: AWS::ECS::Service Properties: Cluster: !Ref Cluster DeploymentConfiguration: MaximumPercent: 200 MinimumHealthyPercent: 100 DesiredCount: 1 LaunchType: 'FARGATE' ServiceRegistries: - RegistryArn: !GetAtt 'ColorServiceRegistry.Arn' NetworkConfiguration: AwsvpcConfiguration: AssignPublicIp: DISABLED SecurityGroups: - !Ref SecurityGroup Subnets: - Fn::ImportValue: !Sub '${ProjectName}:PrivateSubnet1' - Fn::ImportValue: !Sub '${ProjectName}:PrivateSubnet2' TaskDefinition: !Ref RedTaskDef YellowService: Type: AWS::ECS::Service Properties: Cluster: !Ref Cluster DeploymentConfiguration: MaximumPercent: 200 MinimumHealthyPercent: 100 DesiredCount: 1 LaunchType: 'FARGATE' ServiceRegistries: - RegistryArn: !GetAtt 'ColorServiceRegistry.Arn' NetworkConfiguration: AwsvpcConfiguration: AssignPublicIp: DISABLED SecurityGroups: - !Ref SecurityGroup Subnets: - Fn::ImportValue: !Sub '${ProjectName}:PrivateSubnet1' - Fn::ImportValue: !Sub '${ProjectName}:PrivateSubnet2' TaskDefinition: !Ref YellowTaskDef PurpleService: Type: AWS::ECS::Service Properties: Cluster: !Ref Cluster DeploymentConfiguration: MaximumPercent: 200 MinimumHealthyPercent: 100 DesiredCount: 1 LaunchType: 'FARGATE' ServiceRegistries: - RegistryArn: !GetAtt 'ColorServiceRegistry.Arn' NetworkConfiguration: AwsvpcConfiguration: AssignPublicIp: DISABLED SecurityGroups: - !Ref SecurityGroup Subnets: - Fn::ImportValue: !Sub '${ProjectName}:PrivateSubnet1' - Fn::ImportValue: !Sub '${ProjectName}:PrivateSubnet2' TaskDefinition: !Ref PurpleTaskDef WhiteService: Type: AWS::ECS::Service Properties: Cluster: !Ref Cluster DeploymentConfiguration: MaximumPercent: 200 MinimumHealthyPercent: 100 DesiredCount: 1 LaunchType: 'FARGATE' ServiceRegistries: - RegistryArn: !GetAtt 'ColorServiceRegistry.Arn' NetworkConfiguration: AwsvpcConfiguration: AssignPublicIp: DISABLED SecurityGroups: - !Ref SecurityGroup Subnets: - Fn::ImportValue: !Sub '${ProjectName}:PrivateSubnet1' - Fn::ImportValue: !Sub '${ProjectName}:PrivateSubnet2' TaskDefinition: !Ref WhiteTaskDef BlackService: Type: AWS::ECS::Service Properties: Cluster: !Ref Cluster DeploymentConfiguration: MaximumPercent: 200 MinimumHealthyPercent: 100 DesiredCount: 1 LaunchType: 'FARGATE' ServiceRegistries: - RegistryArn: !GetAtt 'ColorServiceRegistry.Arn' NetworkConfiguration: AwsvpcConfiguration: AssignPublicIp: DISABLED SecurityGroups: - !Ref SecurityGroup Subnets: - Fn::ImportValue: !Sub '${ProjectName}:PrivateSubnet1' - Fn::ImportValue: !Sub '${ProjectName}:PrivateSubnet2' TaskDefinition: !Ref BlackTaskDef Outputs: FrontendEndpoint: Description: 'Public endpoint for Frontend service' Value: !Join ['', ['http://', !GetAtt 'PublicLoadBalancer.DNSName']] Export: Name: !Sub '${ProjectName}:FrontendEndpoint'