I am creating ECS cluster, service and task using cloudformation but it gives an error: Embedded stack arn:aws:cloudformation:us-east-2:0212657325299:stack/Root-Cluster-153O1DKDIKGLV/f1123c5c-d1f9-11ea-1216-2a3e4111fce2 was not successfully created: The following resource(s) failed to create: [Myservice, LoadBalancerListener]. I have created a root stack which runs the vpc stack and Cluster stack. This error occurs when running the Cluster stack. I think the error is in the Load balancer and role in Myservice but I am unable to figure the solution. Any help would be appreciated.
---
AWSTemplateFormatVersion: 2010-09-09
Parameters:
SubnetA:
Type: String
SubnetB:
Type: String
VpcID:
Type: String
Resources:
Albsg:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: alb-sg
VpcId: !Ref VpcID
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 0.0.0.0/0
Description: For traffic from Internet
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
Description: For traffic from Internet
GroupDescription: Security Group for demo server
Alb:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
IpAddressType: ipv4
Name: Alb
Scheme: internet-facing
SecurityGroups:
- !Ref Albsg
Subnets:
- Ref: "SubnetA"
- Ref: "SubnetB"
Type: application
DefaultTargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Name: alb-tg
VpcId: !Ref VpcID
Port: 80
Protocol: HTTP
LoadBalancerListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
LoadBalancerArn: !Ref Alb
Port: 80
Protocol: HTTP
DefaultActions:
- Type: forward
TargetGroupArn: !Ref DefaultTargetGroup
MyCluster:
Type: AWS::ECS::Cluster
Properties:
ClusterName: Flask-redis
Myservice:
Type: AWS::ECS::Service
Properties:
Cluster: !Ref MyCluster
DeploymentController:
Type: ECS
DesiredCount: 2
LaunchType: EC2
LoadBalancers:
- !Ref Alb
# Role: String
SchedulingStrategy: REPLICA
ServiceName: Python-service
TaskDefinition: !Ref Task
Task:
Type: AWS::ECS::TaskDefinition
Properties:
Family: redis-python
ContainerDefinitions:
- Essential: true
Image: redis:latest
Name: redis
Cpu: .5 vCPU
# ExecutionRoleArn: !Ref Role
Memory: 0.5 GB
NetworkMode: bridge
RequiresCompatibilities:
- EC2---
AWSTemplateFormatVersion: 2010-09-09
Parameters:
SubnetA:
Type: String
SubnetB:
Type: String
VpcID:
Type: String
Resources:
Albsg:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: alb-sg
VpcId: !Ref VpcID
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 0.0.0.0/0
Description: For traffic from Internet
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
Description: For traffic from Internet
GroupDescription: Security Group for demo server
Alb:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
IpAddressType: ipv4
Name: Alb
Scheme: internet-facing
SecurityGroups:
- !Ref Albsg
Subnets:
- Ref: "SubnetA"
- Ref: "SubnetB"
Type: application
DefaultTargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Name: alb-tg
VpcId: !Ref VpcID
Port: 80
Protocol: HTTP
LoadBalancerListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
LoadBalancerArn: !Ref Alb
Port: 80
Protocol: HTTP
DefaultActions:
- Type: forward
TargetGroupArn: !Ref DefaultTargetGroup
MyCluster:
Type: AWS::ECS::Cluster
Properties:
ClusterName: Flask-redis
Myservice:
Type: AWS::ECS::Service
Properties:
Cluster: !Ref MyCluster
DeploymentController:
Type: ECS
DesiredCount: 2
LaunchType: EC2
LoadBalancers:
- !Ref Alb
# Role: String
SchedulingStrategy: REPLICA
ServiceName: Python-service
TaskDefinition: !Ref Task
Task:
Type: AWS::ECS::TaskDefinition
Properties:
Family: redis-python
ContainerDefinitions:
- Essential: true
Image: redis:latest
Name: redis
Cpu: .5 vCPU
# ExecutionRoleArn: !Ref Role
Memory: 0.5 GB
NetworkMode: bridge
RequiresCompatibilities:
- EC2
The AWS::ECS::Service LoadBalancer is an object. There were also other issues:
Missing DependsOn
Missing port on container
I used the template in us-east-1 using default VPC. The template will still not work as there are no container instances. But at least your original issue should be addressed.
---
AWSTemplateFormatVersion: 2010-09-09
Parameters:
SubnetA:
Default: subnet-00afd36c5eb1d367b
Type: String
SubnetB:
Default: subnet-0573cd428fe807ebc
Type: String
VpcID:
Default: vpc-040d4c42ee5a159fc
Type: String
Resources:
Albsg:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: alb-sg
VpcId: !Ref VpcID
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 0.0.0.0/0
Description: For traffic from Internet
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
Description: For traffic from Internet
GroupDescription: Security Group for demo server
Alb:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
IpAddressType: ipv4
Name: Alb
Scheme: internet-facing
SecurityGroups:
- !Ref Albsg
Subnets:
- Ref: "SubnetA"
- Ref: "SubnetB"
Type: application
DefaultTargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Name: alb-tg
VpcId: !Ref VpcID
Port: 5000
Protocol: HTTP
LoadBalancerListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
LoadBalancerArn: !Ref Alb
Port: 80
Protocol: HTTP
DefaultActions:
- Type: forward
TargetGroupArn: !Ref DefaultTargetGroup
MyCluster:
Type: AWS::ECS::Cluster
Properties:
ClusterName: Flask-redis
Myservice:
Type: AWS::ECS::Service
DependsOn: LoadBalancerListener
Properties:
Cluster: !Ref MyCluster
DeploymentController:
Type: ECS
DesiredCount: 2
LaunchType: EC2
LoadBalancers:
- ContainerName: redis
ContainerPort: 5000
TargetGroupArn: !Ref DefaultTargetGroup
# Role: String
SchedulingStrategy: REPLICA
ServiceName: Python-service
TaskDefinition: !Ref Task
Task:
Type: AWS::ECS::TaskDefinition
Properties:
Family: redis-python
ContainerDefinitions:
- Essential: true
Image: redis:latest
Name: redis
PortMappings:
- ContainerPort: 5000
#HostPort: Integer
#Protocol: tcp
Cpu: .5 vCPU
# ExecutionRoleArn: !Ref Role
Memory: 0.5 GB
NetworkMode: bridge
RequiresCompatibilities:
- EC2
Related
Having no background with aws/devops and after a lot of efforts and trial/error I succeeded building the here bellow stack for my django app:
The stack relies heavily on celery which consumes a lot of tasks so I migrated to sqs for cost reasons (instead of aws redis). For the same reason I decided to disable the nat gateways because it costs so much and I rely only on security groups and acl for security
Initially I had two services: One with nginx, Django, celery,
pgadmin4, flower, and a second with Celery desired count 0 to scale
up on heavy loads. When celery starts working it tend to consume 100%
CPU and launch a lot of process taking all the available connections
of postgres, new process fail, so I added pgbouncer for connection
pooling
I decided to migrate to 3 services, one admin service with nginx,
pgbouncer and pgadmin4, a second non scalable minimal service with
django and one celery worker, and a third scalable celery service
with desired count to 0 which will be launched and scaled down by an
alarm on sqs queues. (I am also considering a fourth service with
django and a desired count to 0 which will be launched and scaled
down by an alarm on elb target response time).
The actual problem is the following : my django and celery containers
(in the django app service) talk with pgbouncer without a problem,
but the nginx container (in the admin service) can't reach the django
container ?
I tried playing with the securitygroups without success (I'm no
expert), any thoughts guys?
Thank you.
Here's the stack code:
# VPC Template Parameters
CIDRBlockIP:
Description: The first two values of the IP for the VPC
Type: String
MinLength: '3'
MaxLength: '7'
Default: 10.20
AllowedPattern: "(\\d{1,3})\\.(\\d{1,3})"
ConstraintDescription: must be a valid start to an IP range of the form x.x
AvailabilityZone1:
Description: The first availability zone in the region
Default: us-west-2a
Type: AWS::EC2::AvailabilityZone::Name
ConstraintDescription: Must be a valid availability zone
AvailabilityZone2:
Description: The second availability zone in the region
Default: us-west-2b
Type: AWS::EC2::AvailabilityZone::Name
ConstraintDescription: Must be a valid availability zone
ELBIngressPort:
Description: The ELB ingress port used by security groups
Type: Number
MinValue: 0
MaxValue: 65535
ConstraintDescription: TCP ports must be between 0 - 65535
Default: 443
ELBHttpIngressPort:
Description: The ELB ingress port used by security groups
Type: Number
MinValue: 0
MaxValue: 65535
ConstraintDescription: TCP ports must be between 0 - 65535
Default: 80
AppIngressPort:
Description: The application ingress port used by security groups
Type: Number
MinValue: 0
MaxValue: 65535
ConstraintDescription: TCP ports must be between 0 - 65535
Default: 80
DbInVpc:
Type: String
Default: true
AllowedValues:
- true
- false
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: Region Availability Zones
Parameters:
- AvailabilityZone1
- AvailabilityZone2
- Label:
default: Ingress Ports
Parameters:
- ELBIngressPort
- AppIngressPort
ParameterLabels:
AvailabilityZone1:
default: Availability Zone 1
AvailabilityZone2:
default: Availability Zone 2
ELBIngressPort:
default: Load Balancer Port
AppIngressPort:
default: Application Port
Conditions:
CreateDbSecurityGroup: !Equals [ !Ref DbInVpc, true ]
Mappings:
# Django App Mappings
appConfigs:
prod:
loadbalancerCertArn: "xxxx"
# Maps CIDR blocks to VPC and various subnets
CIDRMap:
VPC:
CIDR: ".0.0/16"
Public1:
CIDR: ".2.0/24"
Public2:
CIDR: ".3.0/24"
Private1:
CIDR: ".64.0/19"
Private2:
CIDR: ".96.0/19"
Resources:
###############
# VPC Template#
###############
VPC:
Type: AWS::EC2::VPC
UpdateReplacePolicy: Delete
Properties:
CidrBlock: !Sub
- "${beg_ip}${sec_val}"
- beg_ip: !Ref CIDRBlockIP
sec_val: !FindInMap [ CIDRMap, VPC, CIDR ]
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: Name
Value: !Ref "AWS::StackName"
PublicSubnet1:
Type: AWS::EC2::Subnet
UpdateReplacePolicy: Delete
Properties:
VpcId: !Ref VPC
CidrBlock: !Sub
- "${beg_ip}${sec_val}"
- beg_ip: !Ref CIDRBlockIP
sec_val: !FindInMap [ CIDRMap, Public1, CIDR ]
AvailabilityZone: !Ref AvailabilityZone1
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-PublicSubnet1"
PublicSubnet2:
Type: AWS::EC2::Subnet
UpdateReplacePolicy: Delete
DependsOn:
- PublicSubnet1
- PrivateSubnet1
Properties:
VpcId: !Ref VPC
CidrBlock: !Sub
- "${beg_ip}${sec_val}"
- beg_ip: !Ref CIDRBlockIP
sec_val: !FindInMap [ CIDRMap, Public2, CIDR ]
AvailabilityZone: !Ref AvailabilityZone2
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-PublicSubnet2"
PrivateSubnet1:
Type: AWS::EC2::Subnet
UpdateReplacePolicy: Delete
Properties:
VpcId: !Ref VPC
CidrBlock: !Sub
- "${beg_ip}${sec_val}"
- beg_ip: !Ref CIDRBlockIP
sec_val: !FindInMap [ CIDRMap, Private1, CIDR ]
AvailabilityZone: !Ref AvailabilityZone1
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-PrivateSubnet1"
PrivateSubnet2:
Type: AWS::EC2::Subnet
UpdateReplacePolicy: Delete
DependsOn:
- PublicSubnet1
- PrivateSubnet1
Properties:
VpcId: !Ref VPC
CidrBlock: !Sub
- "${beg_ip}${sec_val}"
- beg_ip: !Ref CIDRBlockIP
sec_val: !FindInMap [ CIDRMap, Private2, CIDR ]
AvailabilityZone: !Ref AvailabilityZone2
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-PrivateSubnet2"
InternetGateway:
Type: AWS::EC2::InternetGateway
UpdateReplacePolicy: Delete
Properties:
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-igw"
VPCGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
UpdateReplacePolicy: Delete
Properties:
VpcId: !Ref VPC
InternetGatewayId: !Ref InternetGateway
PublicRouteTable:
Type: AWS::EC2::RouteTable
UpdateReplacePolicy: Delete
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-public-rt"
PublicRoute:
Type: AWS::EC2::Route
UpdateReplacePolicy: Delete
DependsOn: VPCGatewayAttachment
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
PublicSubnetRouteTableAssociation1:
Type: AWS::EC2::SubnetRouteTableAssociation
UpdateReplacePolicy: Delete
Properties:
SubnetId: !Ref PublicSubnet1
RouteTableId: !Ref PublicRouteTable
PublicSubnetRouteTableAssociation2:
Type: AWS::EC2::SubnetRouteTableAssociation
UpdateReplacePolicy: Delete
Properties:
SubnetId: !Ref PublicSubnet2
RouteTableId: !Ref PublicRouteTable
PublicSubnetNetworkAclAssociation1:
Type: AWS::EC2::SubnetNetworkAclAssociation
UpdateReplacePolicy: Delete
Properties:
SubnetId: !Ref PublicSubnet1
NetworkAclId: !GetAtt VPC.DefaultNetworkAcl
PublicSubnetNetworkAclAssociation2:
Type: AWS::EC2::SubnetNetworkAclAssociation
UpdateReplacePolicy: Delete
Properties:
SubnetId: !Ref PublicSubnet2
NetworkAclId: !GetAtt VPC.DefaultNetworkAcl
ELBSecurityGroup:
Type: AWS::EC2::SecurityGroup
UpdateReplacePolicy: Delete
Properties:
GroupDescription: Enable HTTP/HTTPs ingress
VpcId: !Ref VPC
SecurityGroupIngress:
- CidrIp: 0.0.0.0/0
IpProtocol: tcp
ToPort: !Ref ELBIngressPort
FromPort: !Ref ELBIngressPort
- CidrIp: 0.0.0.0/0
IpProtocol: tcp
ToPort: !Ref ELBHttpIngressPort # ALB should redirect to HTTPS
FromPort: !Ref ELBHttpIngressPort
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-ELBSecurityGroup"
ELBSecurityGroupToAppEgress:
Type: AWS::EC2::SecurityGroupEgress # prevent security group circular references
UpdateReplacePolicy: Delete
Properties:
GroupId: !Ref ELBSecurityGroup
IpProtocol: tcp
ToPort: !Ref AppIngressPort
FromPort: !Ref AppIngressPort
DestinationSecurityGroupId: !Ref AppSecurityGroup
AppSecurityGroup:
Type: AWS::EC2::SecurityGroup
UpdateReplacePolicy: Delete
Properties:
GroupDescription: Enable access from ELB to app
VpcId: !Ref VPC
SecurityGroupIngress:
- SourceSecurityGroupId: !Ref ELBSecurityGroup
IpProtocol: tcp
ToPort: !Ref AppIngressPort
FromPort: !Ref AppIngressPort
- SourceSecurityGroupId: !Ref BastionSecurityGroup
IpProtocol: tcp
ToPort: 22
FromPort: 22
#!!! New, to enable intra vpc communication ??!!
- CidrIp: '0.0.0.0/0'
IpProtocol: tcp
ToPort: 80
FromPort: 80
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-AppSecurityGroup"
AppSecurityGroupFromELBIngress:
Type: AWS::EC2::SecurityGroupIngress # prevent security group circular references
UpdateReplacePolicy: Delete
Properties:
GroupId: !Ref AppSecurityGroup
IpProtocol: tcp
ToPort: !Ref AppIngressPort
FromPort: !Ref AppIngressPort
SourceSecurityGroupId: !Ref ELBSecurityGroup
AppSecurityGroupFromAppSecurityGroupIngressTcp:
Type: AWS::EC2::SecurityGroupIngress
UpdateReplacePolicy: Delete
Properties:
GroupId: !Ref AppSecurityGroup
IpProtocol: tcp
ToPort: 65535
FromPort: 0
SourceSecurityGroupId: !Ref AppSecurityGroup
AppSecurityGroupFromAppSecurityGroupIngressUdp:
Type: AWS::EC2::SecurityGroupIngress
UpdateReplacePolicy: Delete
Properties:
GroupId: !Ref AppSecurityGroup
IpProtocol: udp
ToPort: 65535
FromPort: 0
SourceSecurityGroupId: !Ref AppSecurityGroup
BastionSecurityGroup:
Type: AWS::EC2::SecurityGroup
UpdateReplacePolicy: Delete
Properties:
GroupDescription: Enable access to the bastion host
VpcId: !Ref VPC
SecurityGroupEgress:
- CidrIp: 0.0.0.0/0
IpProtocol: tcp
ToPort: 65535
FromPort: 0
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-BastionSecurityGroup"
BastionSecurityGroupToAppEgress:
Type: AWS::EC2::SecurityGroupEgress # prevent security group circular references
UpdateReplacePolicy: Delete
Properties:
GroupId: !Ref BastionSecurityGroup
IpProtocol: tcp
ToPort: 22
FromPort: 22
DestinationSecurityGroupId: !Ref AppSecurityGroup
#####################################################
# Database security groups, only create if required #
#####################################################
DbSecurityGroup:
Type: AWS::EC2::SecurityGroup
UpdateReplacePolicy: Delete
Condition: CreateDbSecurityGroup
Properties:
GroupDescription: Enable access to the RDS DB
VpcId: !Ref VPC
SecurityGroupEgress:
- CidrIp: 0.0.0.0/0
IpProtocol: tcp
ToPort: 6379
FromPort: 6379
- CidrIp: 0.0.0.0/0
IpProtocol: tcp
ToPort: 5432
FromPort: 5432
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-DbSecurityGroup"
DbSecurityGroupFromBastionIngressRedis:
Type: AWS::EC2::SecurityGroupIngress # prevent security group circular references
UpdateReplacePolicy: Delete
Condition: CreateDbSecurityGroup
Properties:
GroupId: !Ref DbSecurityGroup
IpProtocol: tcp
ToPort: 6379 # Redis
FromPort: 6379
SourceSecurityGroupId: !Ref BastionSecurityGroup
DbSecurityGroupFromBastionIngressPostgres:
Type: AWS::EC2::SecurityGroupIngress # prevent security group circular references
UpdateReplacePolicy: Delete
Condition: CreateDbSecurityGroup
Properties:
GroupId: !Ref DbSecurityGroup
IpProtocol: tcp
ToPort: 5432 # Postgres
FromPort: 5432
SourceSecurityGroupId: !Ref BastionSecurityGroup
DbSecurityGroupFromAppIngressRedis:
Type: AWS::EC2::SecurityGroupIngress # prevent security group circular references
UpdateReplacePolicy: Delete
Condition: CreateDbSecurityGroup
Properties:
GroupId: !Ref DbSecurityGroup
IpProtocol: tcp
ToPort: 6379
FromPort: 6379
SourceSecurityGroupId: !Ref AppSecurityGroup
DbSecurityGroupFromAppIngressPostgres:
Type: AWS::EC2::SecurityGroupIngress # prevent security group circular references
UpdateReplacePolicy: Delete
Condition: CreateDbSecurityGroup
Properties:
GroupId: !Ref DbSecurityGroup
IpProtocol: tcp
ToPort: 5432
FromPort: 5432
SourceSecurityGroupId: !Ref AppSecurityGroup
#################
# Admin Apps Service#
#################
AdminAppsTaskDefinition:
Type: AWS::ECS::TaskDefinition
DependsOn:
- AdminAppsLogGroup
- NginxRepository
- Pgadmin4Repository
- PgbouncerRepository
Properties:
Family: !Join ['-', [!Ref AwsIamUserId, !Ref AdminAppsServiceName, TaskDefinition]]
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
Cpu: 256
Memory: "0.5GB" #512 (0.5GB), 1024 (1GB), 2048 (2GB), 3072 (3GB), 4096 (4GB)
ExecutionRoleArn: !GetAtt ECSExecutionRole.Arn
TaskRoleArn: !GetAtt EcsTaskRole.Arn
ContainerDefinitions:
- Name: !Sub "${AwsIamUserId}_nginx"
Image: !Sub "${NginxRepository.RepositoryUri}:latest"
Essential: true
PortMappings:
- ContainerPort: !Ref AppContainerPort
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-region: !Sub ${AwsRegion}
awslogs-group: !Ref AdminAppsLogGroup
awslogs-stream-prefix: Nginx
- Name: !Sub "${AwsIamUserId}_pgbouncer"
Image: !Sub "${PgbouncerRepository.RepositoryUri}:latest"
PortMappings:
- ContainerPort: 5432
Protocol: tcp
Essential: true
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-region: !Sub ${AwsRegion}
awslogs-group: !Ref AdminAppsLogGroup
awslogs-stream-prefix: Pgbouncer
Secrets:
- Name: DJANGO_ECS_SECRETS
ValueFrom: !Ref DjangoSecrets
- Name: AWS_ACCESS_CREDENTIALS
ValueFrom: !Ref AwsAccessSecrets
Environment:
- Name: !Sub "${AwsIamUserId}_pgadmin4"
Image: !Sub "${Pgadmin4Repository.RepositoryUri}:latest"
Essential: false
PortMappings:
- ContainerPort: 5050
Protocol: tcp
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-region: !Sub ${AwsRegion}
awslogs-group: !Ref AdminAppsLogGroup
awslogs-stream-prefix: Pgadmin4
Command:
- "/generate_servers_json_and_pgpass_conf_files"
Secrets:
- Name: DJANGO_ECS_SECRETS
ValueFrom: !Ref DjangoSecrets
- Name: AWS_ACCESS_CREDENTIALS
ValueFrom: !Ref AwsAccessSecrets
Environment:
AdminAppsService:
Type: AWS::ECS::Service
DependsOn:
- DjangoAppHttpsListener
- GeneralECSCluster
Properties:
ServiceName: !Ref AdminAppsServiceName
Cluster: !Ref GeneralECSCluster
TaskDefinition: !Ref AdminAppsTaskDefinition
DeploymentConfiguration:
MinimumHealthyPercent: 0
MaximumPercent: 200
DesiredCount: 1
LaunchType: FARGATE
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: ENABLED #!! Switch to DISABLED to use nat, if DISABLED uncomment the nat section above of this file
Subnets:
#- !Ref PrivateSubnet1 #!! uncomment this to use nat, if enable uncomment the nat section above of this file
#- !Ref PrivateSubnet2 #!! uncomment this to use nat, if enable uncomment the nat section above of this file
- !Ref PublicSubnet1 #!! uncomment this to use nat, if enable uncomment the nat section above of this file
- !Ref PublicSubnet2 #!! uncomment this to use nat, if enable uncomment the nat section above of this file
SecurityGroups:
- !Ref AppSecurityGroup
LoadBalancers:
- ContainerName: !Sub "${AwsIamUserId}_nginx"
ContainerPort: !Ref AppContainerPort
TargetGroupArn: !Ref DjangoAppTargetGroup
ServiceRegistries:
- RegistryArn: !GetAtt DiscoveryService.Arn
#################
# Django Service#
#################
DjangoAppTaskDefinition:
Type: AWS::ECS::TaskDefinition
DependsOn:
- DjangoAppLogGroup
- DjangoCeleryRepository
Properties:
Family: !Join ['-', [!Ref AwsIamUserId, !Ref DjangoAppServiceName, TaskDefinition]]
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
Cpu: 256
Memory: "0.5GB" #512 (0.5GB), 1024 (1GB), 2048 (2GB), 3072 (3GB), 4096 (4GB)
ExecutionRoleArn: !GetAtt ECSExecutionRole.Arn
TaskRoleArn: !GetAtt EcsTaskRole.Arn
ContainerDefinitions:
- Name: !Sub "${AwsIamUserId}_django_celery"
Image: !Sub "${DjangoCeleryRepository.RepositoryUri}:latest"
Essential: true
#Cpu: 128
#Memory: 256
PortMappings:
- ContainerPort: 8000
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-region: !Sub ${AwsRegion}
awslogs-group: !Ref DjangoAppLogGroup
awslogs-stream-prefix: DjangoAppContainer
EntryPoint:
- "/entrypoint"
Command:
- "/start"
Secrets:
- Name: DJANGO_ECS_SECRETS
ValueFrom: !Ref DjangoSecrets
- Name: AWS_ACCESS_CREDENTIALS
ValueFrom: !Ref AwsAccessSecrets
Environment:
- Name: celery-default-worker
Image: !Sub "${DjangoCeleryRepository.RepositoryUri}:latest"
Essential: false
#Cpu: 128
#MemoryReservation: 256
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-region: !Sub ${AwsRegion}
awslogs-group: !Ref DjangoAppLogGroup
awslogs-stream-prefix: CeleryAppContainer
EntryPoint:
- "/entrypoint"
Command:
- "/start-celery-default-worker"
Secrets:
- Name: DJANGO_ECS_SECRETS
ValueFrom: !Ref DjangoSecrets
- Name: AWS_ACCESS_CREDENTIALS
ValueFrom: !Ref AwsAccessSecrets
Environment:
DjangoAppService:
Type: AWS::ECS::Service
DependsOn:
#- DjangoAppHttpsListener
- GeneralECSCluster
Properties:
ServiceName: !Ref DjangoAppServiceName
Cluster: !Ref GeneralECSCluster
TaskDefinition: !Ref DjangoAppTaskDefinition
DeploymentConfiguration:
MinimumHealthyPercent: 100
MaximumPercent: 200
DesiredCount: 1
LaunchType: FARGATE
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: ENABLED #!! Switch to DISABLED to use nat, if DISABLED uncomment the nat section above of this file
Subnets:
#- !Ref PrivateSubnet1 #!! uncomment this to use nat, if enable uncomment the nat section above of this file
#- !Ref PrivateSubnet2 #!! uncomment this to use nat, if enable uncomment the nat section above of this file
- !Ref PublicSubnet1 #!! uncomment this to use nat, if enable uncomment the nat section above of this file
- !Ref PublicSubnet2 #!! uncomment this to use nat, if enable uncomment the nat section above of this file
SecurityGroups:
- !Ref AppSecurityGroup
ServiceRegistries:
- RegistryArn: !GetAtt DiscoveryService.Arn
#################
# Celery Service#
#################
CeleryAppTaskDefinition:
Type: AWS::ECS::TaskDefinition
DependsOn:
- CeleryAppLogGroup
- DjangoCeleryRepository
Properties:
Family: !Join ['-', [!Ref AwsIamUserId, !Ref CeleryAppServiceName, TaskDefinition]]
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
Cpu: 512
Memory: "1GB" #512 (0.5GB), 1024 (1GB), 2048 (2GB), 3072 (3GB), 4096 (4GB)
ExecutionRoleArn: !GetAtt ECSExecutionRole.Arn
TaskRoleArn: !GetAtt EcsTaskRole.Arn
ContainerDefinitions:
- Name: celery-default-worker
Image: !Sub "${DjangoCeleryRepository.RepositoryUri}:latest"
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-region: !Sub ${AwsRegion}
awslogs-group: !Ref CeleryAppLogGroup
awslogs-stream-prefix: Celery
EntryPoint:
- "/entrypoint"
Command:
- "/start-celery-default-worker"
Secrets:
- Name: DJANGO_ECS_SECRETS
ValueFrom: !Ref DjangoSecrets
- Name: AWS_ACCESS_CREDENTIALS
ValueFrom: !Ref AwsAccessSecrets
Environment:
- Name: celery-long-running-worker
Image: !Sub "${DjangoCeleryRepository.RepositoryUri}:latest"
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-region: !Sub ${AwsRegion}
awslogs-group: !Ref CeleryAppLogGroup
awslogs-stream-prefix: Celery
EntryPoint:
- "/entrypoint"
Command:
- "/start-celery-long-running-worker"
Secrets:
- Name: DJANGO_ECS_SECRETS
ValueFrom: !Ref DjangoSecrets
- Name: AWS_ACCESS_CREDENTIALS
ValueFrom: !Ref AwsAccessSecrets
Environment:
CeleryAppService:
Type: AWS::ECS::Service
Properties:
ServiceName: !Ref CeleryAppServiceName
Cluster: !Ref GeneralECSCluster
TaskDefinition: !Ref CeleryAppTaskDefinition
DeploymentConfiguration:
MinimumHealthyPercent: 0
MaximumPercent: 200
DesiredCount: 0 #! this scales down the service to 0 task, if you want 1 minimum task always runing change to 1
LaunchType: FARGATE
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: ENABLED #!! Switch to DISABLED to use nat, if DISABLED uncomment the nat section above of this file
Subnets:
#- !Ref PrivateSubnet1 #!! uncomment this to use nat, if enable uncomment the nat section above of this file
#- !Ref PrivateSubnet2 #!! uncomment this to use nat, if enable uncomment the nat section above of this file
- !Ref PublicSubnet1 #!! uncomment this to use nat, if enable uncomment the nat section above of this file
- !Ref PublicSubnet2 #!! uncomment this to use nat, if enable uncomment the nat section above of this file
SecurityGroups:
- !Ref AppSecurityGroup
ServiceRegistries:
- RegistryArn: !GetAtt DiscoveryService.Arn
#####################################################################################
# Service Discovery to enable communication between Django and Celery Service #
#####################################################################################
# Private Namespace for Service Discovery
# https://aws.amazon.com/premiumsupport/knowledge-center/cloudformation-ecs-service-discovery/
PrivateNamespace:
Type: AWS::ServiceDiscovery::PrivateDnsNamespace
Properties:
Name: !Sub "${AwsIamUserId}_local"
Vpc: !Ref VPC
DiscoveryService:
Type: AWS::ServiceDiscovery::Service
Properties:
Description: Discovery Service to enable communication between Django and Celery Service
DnsConfig:
RoutingPolicy: WEIGHTED #MULTIVALUE
DnsRecords:
- TTL: 60
Type: A
HealthCheckCustomConfig:
FailureThreshold: 1
Name: discovery-service
NamespaceId: !Ref PrivateNamespace
###########################
# Load balancer resources #
###########################
DjangoAppLoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
LoadBalancerAttributes:
- Key: 'idle_timeout.timeout_seconds'
Value: 4000
- Key: 'routing.http2.enabled'
Value: 'true'
- Key: 'access_logs.s3.enabled'
Value: 'false'
Scheme: internet-facing
SecurityGroups:
- !Ref ELBSecurityGroup
Subnets:
- !Ref PublicSubnet1
- !Ref PublicSubnet2
Type: application
DjangoAppHttpsListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
Certificates:
- CertificateArn: !FindInMap [ appConfigs, !Ref EnvStageName, loadbalancerCertArn ]
DefaultActions:
- TargetGroupArn: !Ref DjangoAppTargetGroup
Type: forward
LoadBalancerArn: !Ref DjangoAppLoadBalancer
Port: 443
Protocol: HTTPS
DjangoAppHttpListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
DefaultActions:
- RedirectConfig:
Host: "#{host}"
Path: "/#{path}"
Port: 443
Protocol: "HTTPS"
Query: "#{query}"
StatusCode: HTTP_301
Type: redirect
LoadBalancerArn: !Ref DjangoAppLoadBalancer
Port: 80
Protocol: HTTP
DjangoAppTargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Port: !Ref AppContainerPort
HealthCheckPath: '/health/'
HealthCheckProtocol: HTTP
HealthCheckIntervalSeconds: 90
HealthCheckTimeoutSeconds: 60
HealthyThresholdCount: 2
UnhealthyThresholdCount: 10
Matcher:
HttpCode: '200,301'
Protocol: HTTP
TargetType: ip
TargetGroupAttributes:
- Key: deregistration_delay.timeout_seconds
Value: 60
VpcId: !Ref VPC
This is a CloudFormation template to create application load balancer.
I'm getting an error that says- Value of property Subnets must be of type List of String.
Are the security group entities declared rightly?
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
Name:
Description: Name of the project
Type: String
Environment:
Description: Environment of the Application Load balancer
Type: String
PublicSubnet:
Description: Subnet
Type: List<AWS::EC2::Subnet::Id>
Vpc:
Description: VPC
Type: AWS::EC2::VPC::Id
Resources:
SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: ElastiCache Security Group
VpcId: !Ref Vpc
SecurityGroupIngress:
-
IpProtocol: tcp
FromPort: "80"
ToPort: "80"
FromPort: "443"
ToPort: "443"
CidrIp: "0.0.0.0/0"
Tags:
-
Key: Name
Value: "App-SG"
ApplicationLB:
Type: 'AWS::ElasticLoadBalancingV2::LoadBalancer'
Properties:
IpAddressType: ipv4
Name: Test-ALB
Scheme: internet-facing
SecurityGroups:
- !Ref SecurityGroup
Subnets:
- !Ref PublicSubnet
Tags:
- Key: Name
Value: Test-ALB
Type: application
ALBListener:
Type: 'AWS::ElasticLoadBalancingV2::Listener'
Properties:
DefaultActions:
- Type: forward
TargetGroupArn: !Ref ALBTargetGroup
LoadBalancerArn:
Ref: ApplicationLB
Port: '80'
Protocol: HTTP
ALBTargetGroup:
Type: 'AWS::ElasticLoadBalancingV2::TargetGroup'
Properties:
HealthCheckIntervalSeconds: 30
HealthCheckTimeoutSeconds: 5
HealthyThresholdCount: 3
Port: 80
Protocol: HTTP
UnhealthyThresholdCount: 5
VpcId: !Ref Vpc
Also it would be helpful to review the entire template, incase there are more errors.
Your PublicSubnet is already a list. So you can just do:
Subnets: !Ref PublicSubnet
I am trying to use the Cloudformation Codedeploy Blue/Green deployment feature so have a task set that looks like the following.
TaskSet:
Type: AWS::ECS::TaskSet
Properties:
Cluster:
Fn::ImportValue: !Sub "${ClusterStackName}-ClusterID"
LaunchType: FARGATE
NetworkConfiguration:
AwsVpcConfiguration:
AssignPublicIp: ENABLED
SecurityGroups:
- !Ref FargateSecurityGroup
Subnets:
- Fn::ImportValue: !Sub "${ClusterStackName}-SUBNET1ID"
- Fn::ImportValue: !Sub "${ClusterStackName}-SUBNET2ID"
PlatformVersion: 1.3.0
Scale:
Unit: PERCENT
Value: 1
Service: !Ref ECSService
TaskDefinition: !Ref TaskDefinition
LoadBalancers:
- ContainerName: !Ref ServiceName
ContainerPort: !Ref ContainerPort
TargetGroupArn: !Ref TargetGroup
My ECS Service definition looks like the following
ECSService:
Type: AWS::ECS::Service
DependsOn: HTTPSListener
Properties:
ServiceName: !Sub "${ServiceName}-service"
Cluster:
Fn::ImportValue: !Sub "${ClusterStackName}-ClusterID"
DeploymentController:
Type: EXTERNAL
DesiredCount: 4
EnableECSManagedTags: true
HealthCheckGracePeriodSeconds: 30
SchedulingStrategy: REPLICA
This causes the following error in Cloudformation
Invalid request provided: CreateService error: Health check grace period is only valid for services configured to use load balancers
However omitting HealthCheckGracePeriodSeconds causes my tasks to fail the healthcheck while starting. I have checked the docs and there appears to be no option to add HealthCheckGracePeriodSeconds to the TaskSet definition.
How can I use Cloudformation blue/green deployment for ECS with a health check grace period?
Full Example
Here is a full template that I would expect to work but does not.
AWSTemplateFormatVersion: 2010-09-09
Parameters:
Image:
Type: String
Vpc:
Type: 'AWS::EC2::VPC::Id'
Subnet1:
Type: 'AWS::EC2::Subnet::Id'
Subnet2:
Type: 'AWS::EC2::Subnet::Id'
Transform:
- AWS::CodeDeployBlueGreen
Hooks:
CodeDeployBlueGreenHook:
Type: AWS::CodeDeploy::BlueGreen
Properties:
TrafficRoutingConfig:
Type: AllAtOnce
Applications:
- Target:
Type: AWS::ECS::Service
LogicalID: ECSService
ECSAttributes:
TaskDefinitions:
- TaskDefinition
- GreenTaskDefinition
TaskSets:
- TaskSet
- GreenTaskSet
TrafficRouting:
ProdTrafficRoute:
Type: AWS::ElasticLoadBalancingV2::Listener
LogicalID: HTTPListener
TargetGroups:
- TargetGroup
- TargetGroupGreen
Resources:
ALBSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Allow HTTP to load balancer
VpcId: !Ref Vpc
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
SecurityGroupEgress:
- IpProtocol: -1
CidrIp: 0.0.0.0/0
FargateSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Allow port 80 to service
VpcId: !Ref Vpc
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
SourceSecurityGroupId: !Ref ALBSecurityGroup
SecurityGroupEgress:
- IpProtocol: -1
CidrIp: 0.0.0.0/0
LoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: load-balancer
Type: application
IpAddressType: ipv4
Scheme: internet-facing
SecurityGroups: [!Ref ALBSecurityGroup]
Subnets: [!Ref Subnet1, !Ref Subnet2]
HTTPListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
DefaultActions:
- TargetGroupArn: !Ref TargetGroup
Type: forward
LoadBalancerArn: !Ref LoadBalancer
Port: 80
Protocol: HTTP
TargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
HealthCheckEnabled: true
HealthCheckIntervalSeconds: 30
HealthCheckPath: "/"
HealthCheckTimeoutSeconds: 10
HealthyThresholdCount: 5
Name: targetgroup-blue
Port: 80
Protocol: HTTP
TargetType: ip
UnhealthyThresholdCount: 10
VpcId: !Ref Vpc
TargetGroupGreen:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
HealthCheckEnabled: true
HealthCheckIntervalSeconds: 30
HealthCheckPath: "/"
HealthCheckTimeoutSeconds: 10
HealthyThresholdCount: 5
Name: targetgroup-green
Port: 80
Protocol: HTTP
TargetType: ip
UnhealthyThresholdCount: 10
VpcId: !Ref Vpc
TaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
Family: "task-family-name"
NetworkMode: awsvpc
RequiresCompatibilities: [FARGATE]
Cpu: 512
Memory: 1024
ContainerDefinitions:
- Name: Container
Essential: true
Image: !Ref Image
PortMappings:
- ContainerPort: 80
TaskSet:
Type: AWS::ECS::TaskSet
Properties:
Cluster: !Ref ECSCluster
LaunchType: FARGATE
NetworkConfiguration:
AwsVpcConfiguration:
AssignPublicIp: ENABLED
SecurityGroups: [!Ref FargateSecurityGroup]
Subnets: [!Ref Subnet1, !Ref Subnet2]
Scale:
Unit: PERCENT
Value: 100
Service: !Ref ECSService
TaskDefinition: !Ref TaskDefinition
PlatformVersion: LATEST
LoadBalancers:
- ContainerName: Container
ContainerPort: 80
TargetGroupArn: !Ref TargetGroup
ECSService:
Type: AWS::ECS::Service
DependsOn: HTTPListener
Properties:
ServiceName: "service"
Cluster: !Ref ECSCluster
DeploymentController:
Type: EXTERNAL
DesiredCount: 4
EnableECSManagedTags: true
HealthCheckGracePeriodSeconds: 30
SchedulingStrategy: REPLICA
PrimaryTaskSet:
Type: 'AWS::ECS::PrimaryTaskSet'
Properties:
Cluster: !Ref ECSCluster
Service: !Ref ECSService
TaskSetId: !GetAtt 'TaskSet.Id'
ECSCluster:
Type: AWS::ECS::Cluster
Properties:
ClusterName: Cluster
CapacityProviders:
- FARGATE
DefaultCapacityProviderStrategy:
- CapacityProvider: FARGATE
Weight: 1
The error is correct, health checks at the Service level, will need a Load Balancer. However, you can define at the task level, health checks:
EscTaskDef:
Type: 'AWS::ECS::TaskDefinition'
Properties:
...
ContainerDefinitions:
- Name: !Ref AWS::StackName
Image: !Ref Image
PortMappings:
- ContainerPort: 80
HealthCheck:
Command: [ "CMD-SHELL", "curl -f http://localhost:80/api/healthcheck/ping || exit 1" ]
StartPeriod: 200
Here is an example of a rollback example using this type of health check with Cloudformation.
In my Cloudformation template, I am using the blue-green deployment trigger and have the following TaskDefinition
TaskDefinition:
Type: AWS::ECS::TaskDefinition
DependsOn: LogGroup
Properties:
Family: "task-family-name"
NetworkMode: awsvpc
RequiresCompatibilities: [FARGATE]
Cpu: 512
Memory: 1024
ExecutionRoleArn: arn:aws:iam::579072907853:role/ecsTaskExecutionRole
TaskRoleArn: !Ref ServiceRole
ContainerDefinitions:
- Name: !Ref ServiceName
Essential: true
Image: !Ref ImageARN
PortMappings:
- ContainerPort: !Ref ContainerPort
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-region: !Ref AWS::Region
awslogs-group: !Ref LogGroup
awslogs-stream-prefix: ecs
Environment:
- Name: RACK_ENV
Value: !Ref EnvironmentName
If I update the stack with a new value for the ImageARN parameter, I get the following error
'CodeDeployBlueGreenHook' of type AWS::CodeDeploy::BlueGreen failed with message: The submitted template's primary task definition 'TaskDefinition' was never updated. The template's 'AWS::CodeDeployBlueGreen' Transform might be missing or removed
If I don't use a parameter but instead use a hardcoded Image then update the template it works fine.
Is there a way to use a parameter?
Example
Below is a full example of a template that has this issue. I change the ImageARN parameter between these two images
nginx:1.18.0#sha256:001487e0a8c6abf91351ab45fdb308c0c2b95e8a67260b7ca5cf064462689a9e
nginx:1.19.4#sha256:aec3f367f48745b280ee2fd8d8469c0c0ec6b9b2fca3cd3e6cff03e1b69ae054
AWSTemplateFormatVersion: 2010-09-09
Parameters:
Image:
Type: String
CIDRBlock:
Type: String
Default: 192.168.5.0/24
Description: CIDR block for VPC
ExecutionRoleARN:
Type: String
Transform:
- AWS::CodeDeployBlueGreen
Hooks:
CodeDeployBlueGreenHook:
Type: AWS::CodeDeploy::BlueGreen
Properties:
TrafficRoutingConfig:
Type: AllAtOnce
Applications:
- Target:
Type: AWS::ECS::Service
LogicalID: ECSService
ECSAttributes:
TaskDefinitions:
- TaskDefinition
- GreenTaskDefinition
TaskSets:
- TaskSet
- GreenTaskSet
TrafficRouting:
ProdTrafficRoute:
Type: AWS::ElasticLoadBalancingV2::Listener
LogicalID: HTTPListener
TargetGroups:
- TargetGroup
- TargetGroupGreen
Resources:
ALBSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Allow HTTP to load balancer
VpcId: !Ref Vpc
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
SecurityGroupEgress:
- IpProtocol: -1
CidrIp: 0.0.0.0/0
FargateSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Allow port 80 to service
VpcId: !Ref Vpc
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
SourceSecurityGroupId: !Ref ALBSecurityGroup
SecurityGroupEgress:
- IpProtocol: -1
CidrIp: 0.0.0.0/0
LoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: load-balancer
Type: application
IpAddressType: ipv4
Scheme: internet-facing
SecurityGroups: [!Ref ALBSecurityGroup]
Subnets: [!Ref Subnet1, !Ref Subnet2]
HTTPListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
DefaultActions:
- TargetGroupArn: !Ref TargetGroup
Type: forward
LoadBalancerArn: !Ref LoadBalancer
Port: 80
Protocol: HTTP
TargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
HealthCheckEnabled: true
HealthCheckIntervalSeconds: 30
HealthCheckPath: "/"
HealthCheckTimeoutSeconds: 10
HealthyThresholdCount: 5
Name: targetgroup-blue
Port: 80
Protocol: HTTP
TargetType: ip
UnhealthyThresholdCount: 10
VpcId: !Ref Vpc
TargetGroupGreen:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
HealthCheckEnabled: true
HealthCheckIntervalSeconds: 30
HealthCheckPath: "/"
HealthCheckTimeoutSeconds: 10
HealthyThresholdCount: 5
Name: targetgroup-green
Port: 80
Protocol: HTTP
TargetType: ip
UnhealthyThresholdCount: 10
VpcId: !Ref Vpc
TaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
Family: "task-family-name"
NetworkMode: awsvpc
RequiresCompatibilities: [FARGATE]
Cpu: 512
Memory: 1024
ExecutionRoleArn: !Ref ExecutionRoleARN
ContainerDefinitions:
- Name: Container
Essential: true
Image: !Ref Image
PortMappings:
- ContainerPort: 80
TaskSet:
Type: AWS::ECS::TaskSet
Properties:
Cluster: !Ref ECSCluster
LaunchType: FARGATE
NetworkConfiguration:
AwsVpcConfiguration:
AssignPublicIp: ENABLED
SecurityGroups: [!Ref FargateSecurityGroup]
Subnets: [!Ref Subnet1, !Ref Subnet2]
Scale:
Unit: PERCENT
Value: 100
Service: !Ref ECSService
TaskDefinition: !Ref TaskDefinition
PlatformVersion: LATEST
LoadBalancers:
- ContainerName: Container
ContainerPort: 80
TargetGroupArn: !Ref TargetGroup
ECSService:
Type: AWS::ECS::Service
DependsOn: HTTPListener
Properties:
ServiceName: service
Cluster: !Ref ECSCluster
DeploymentController:
Type: EXTERNAL
DesiredCount: 4
EnableECSManagedTags: true
PrimaryTaskSet:
Type: 'AWS::ECS::PrimaryTaskSet'
Properties:
Cluster: !Ref ECSCluster
Service: !Ref ECSService
TaskSetId: !GetAtt 'TaskSet.Id'
Vpc:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref CIDRBlock
EnableDnsHostnames: true
EnableDnsSupport: true
Subnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref Vpc
CidrBlock: !Select [0, !Cidr [!Ref CIDRBlock, 2, 6 ]]
AvailabilityZone: !Select [0, Fn::GetAZs: !Ref 'AWS::Region']
Subnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref Vpc
CidrBlock: !Select [1, !Cidr [!Ref CIDRBlock, 2, 6 ]]
AvailabilityZone: !Select [1, Fn::GetAZs: !Ref 'AWS::Region']
InternetGateway:
Type: AWS::EC2::InternetGateway
InternetGatewayVPCAssoc:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref Vpc
InternetGatewayId: !Ref InternetGateway
RouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref Vpc
RouteTableSub1Assoc:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref RouteTable
SubnetId: !Ref Subnet1
RouteTableSub2Assoc:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref RouteTable
SubnetId: !Ref Subnet2
InternetGatewayRoute:
Type: AWS::EC2::Route
DependsOn: InternetGateway
Properties:
RouteTableId: !Ref RouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
ECSCluster:
Type: AWS::ECS::Cluster
Properties:
ClusterName: Cluster
CapacityProviders:
- FARGATE
DefaultCapacityProviderStrategy:
- CapacityProvider: FARGATE
Weight: 1
I looked at your example, and what I have in my own projects. I can confirm that your example does not work as expected.
I investigated more and determined that the issue is likely to be due to creation of entire networking stack in your template. In my own projects I don't have VPC or subnets in my ecs templates, thus it all works.
So once I removed the VPC related things from your template it started to work as required. There are some other smaller changes, but they may not be necessary. I think the VPC-stuff is the main reason.
Also, please update the template by using Replace current template option, rather then Use current template. If you use the second option it will still not work. I think this is due to how macros work in CFN, and template should be replaced, rather then updated in-place. If you replace it, B/G in CodeDeploy is successful trigger and you can view it CodeDeploy console.
Here is the modified template:
AWSTemplateFormatVersion: 2010-09-09
Parameters:
ImageUrl:
Type: String
VpcId:
Type: 'AWS::EC2::VPC::Id'
Subnet1:
Type: 'AWS::EC2::Subnet::Id'
Subnet2:
Type: 'AWS::EC2::Subnet::Id'
ExecutionRoleARN:
Type: String
DesiredCount:
Type: Number
Default: 2
Transform:
- 'AWS::CodeDeployBlueGreen'
Hooks:
CodeDeployBlueGreenHook:
Type: 'AWS::CodeDeploy::BlueGreen'
Properties:
TrafficRoutingConfig:
Type: AllAtOnce
Applications:
- Target:nginx:1.19.4#sha256:aec3f367f48745b280ee2fd8d8469c0c0ec6b9b2fca3cd3e6cff03e1b69ae054
Type: 'AWS::ECS::Service'
LogicalID: ECSService
ECSAttributes:
TaskDefinitions:
- TaskDefinition
- GreenTaskDefinition
TaskSets:
- TaskSet
- GreenTaskSet
TrafficRouting:
ProdTrafficRoute:
Type: 'AWS::ElasticLoadBalancingV2::Listener'
LogicalID: HTTPListener
TargetGroups:
- TargetGroupnginx:1.19.4#sha256:aec3f367f48745b280ee2fd8d8469c0c0ec6b9b2fca3cd3e6cff03e1b69ae054
- TargetGroupGreen
Resources:
ALBSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Allow HTTP to load balancer
VpcId: !Ref VpcId
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
SecurityGroupEgress:
- IpProtocol: -1
CidrIp: 0.0.0.0/0
FargateSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Allow port 80 to service
VpcId: !Ref VpcId
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
SourceSecurityGroupId: !Ref ALBSecurityGroup
SecurityGroupEgress:
- IpProtocol: -1
CidrIp: 0.0.0.0/0
LoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
#Name: load-balancer
Type: application
IpAddressType: ipv4
Scheme: internet-facing
SecurityGroups:
- !Ref ALBSecurityGroup
Subnets:
- !Ref Subnet1
- !Ref Subnet2
HTTPListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
DefaultActions:
- TargetGroupArn: !Ref TargetGroup
Type: forward
LoadBalancerArn: !Ref LoadBalannginx:1.19.4#sha256:aec3f367f48745b280ee2fd8d8469c0c0ec6b9b2fca3cd3e6cff03e1b69ae054cer
Port: 80
Protocol: HTTP
TargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
HealthCheckEnabled: true
HealthCheckIntervalSeconds: 30
HealthCheckPath: "/"
HealthCheckTimeoutSeconds: 10
HealthyThresholdCount: 5
#Name: targetgroup-blue
Port: 80
Protocol: HTTP
TargetType: ip
UnhealthyThresholdCount: 10
VpcId: !Ref VpcId
TargetGroupGreen:
Type: nginx:1.19.4#sha256:aec3f367f48745b280ee2fd8d8469c0c0ec6b9b2fca3cd3e6cff03e1b69ae054AWS::ElasticLoadBalancingV2nginx:1.19.4#sha256:aec3f367f48745b280ee2fd8d8469c0c0ec6b9b2fca3cd3e6cff03e1b69ae054::TargetGroup
Properties:
HealthCheckEnabled: true
HealthCheckIntervalSeconds: 30
HealthCheckPath: "/"
HealthCheckTimeoutSeconds: 10
HealthyThresholdCount: 5
#Name: targetgroup-green
Port: 80
Protocol: HTTP
TargetType: ip
UnhealthyThresholdCount: 10
VpcId: !Ref VpcId
TaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
Family: "task-family-name"
NetworkMode: awsvpc
RequiresCompatibilities: [FARGATE]
Cpu: 256
Memory: 512
ExecutionRoleArn: !Ref ExecutionRoleARN
ContainerDefinitions:
- Name: Container
Essential: true
Image: !Ref ImageUrl
PortMappings:
- ContainerPort: 80
Protocol: tcp
TaskSet:
Type: AWS::ECS::TaskSet
Properties:
Cluster: !Ref ECSCluster
LaunchType: FARGATE
NetworkConfiguration:
AwsVpcConfiguration:
AssignPublicIp: ENABLED
SecurityGroups:
- !Ref FargateSecurityGroup
Subnets:
- !Ref Subnet1
- !Ref Subnet2
Scale:
Unit: PERCENT
Value: 100
Service: !Ref ECSService
TaskDefinition: !Ref TaskDefinition
PlatformVersion: LATEST
LoadBalancers:
- ContainerName: Container
ContainerPort: 80
TargetGroupArn: !Ref TargetGroup
ECSService:
Type: AWS::ECS::Service
DependsOn: HTTPListener
Properties:
#ServiceName: service
Cluster: !Ref ECSCluster
DeploymentController:
Type: EXTERNAL
DesiredCount: !Ref DesiredCount
EnableECSManagedTags: true
PrimaryTaskSet:
Type: 'AWS::ECS::PrimaryTaskSet'
Properties:
Cluster: !Ref ECSCluster
Service: !Ref ECSService
TaskSetId: !GetAtt TaskSet.Id
ECSCluster:
Type: AWS::ECS::Cluster
Properties:
#ClusterName: Cluster
CapacityProviders:
- FARGATE
DefaultCapacityProviderStrategy:
- CapacityProvider: FARGATE
Weight: 1
Outputs:
ALBDNSName:
Value: !GetAtt LoadBalancer.DNSName
When I had this issue the solution was to replace the Image value within a step in the pipeline before the AWS CloudFomration step, basically "building" the file before deployment instead of passing parameters. Is not fancy but it works, I expect there are better solutions to this.
Hi I am using cloudforamtion to create ecs cluster, service and task. In task I am simply pulling wordpress image and connecting it to rds database. The problem here is the instance in the target group goes to draining state and then turn unhealthy. Wordpress shows up in the dns of alb but when you refresh it sometime gives error 502 bad gateway. I am only attaching the template where the error can exist most probably I might be giving the port wrong. I have good connection to the database (I have checked it as I did ssh into the instance and then used mysql -umysqldb -pmysql123a -h rds.endpoint command). The targets in targetgroup shoes an error Health checks failed with these codes: [502]
Task:
---
AWSTemplateFormatVersion: 2010-09-09
Parameters:
ExRole:
Type: String
RDS:
Type: String
Resources:
Task:
Type: AWS::ECS::TaskDefinition
Properties:
Family: wordpress
Cpu: 1 vCPU
ExecutionRoleArn: !Ref ExRole
Memory: 1 GB
NetworkMode: bridge
RequiresCompatibilities:
- EC2
TaskRoleArn: !Ref ExRole
ContainerDefinitions:
- Essential: true
Image: wordpress:latest
Name: wordpress
PortMappings:
- ContainerPort: 80
HostPort: 0
Protocol: tcp
Environment:
- Name: WORDPRESS_DB_HOST
Value: !Ref RDS
- Name: WORDPRESS_DB_USER
Value: mysqldb
- Name: WORDPRESS_DB_PASSWORD
Value: mysql123a
- Name: WORDPRESS_DB_NAME
Value: mysqldb
Outputs:
Task:
Description: Contains all the task specifications
Value: !Ref Task
Export:
Name: "Task"
alb:
---
AWSTemplateFormatVersion: 2010-09-09
Parameters:
SubnetA:
Type: String
SubnetB:
Type: String
VpcID:
Type: String
Resources:
Albsg:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: alb-sg
VpcId: !Ref VpcID
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
Description: For traffic from Internet
GroupDescription: Security Group for demo server
Alb:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
IpAddressType: ipv4
Name: Alb
Scheme: internet-facing
SecurityGroups:
- !Ref Albsg
Subnets:
- Ref: "SubnetA"
- Ref: "SubnetB"
Type: application
DefaultTargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
DependsOn: Alb
Properties:
Name: alb-tg
VpcId: !Ref VpcID
Port: 80
Protocol: HTTP
LoadBalancerListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
LoadBalancerArn: !Ref Alb
Port: 80
Protocol: HTTP
DefaultActions:
- Type: forward
TargetGroupArn: !Ref DefaultTargetGroup
Outputs:
Albsg:
Description: security group for application load balancer
Value: !Ref Albsg
Export:
Name: "Albsg"
Alb:
Description: application load balancer
Value: !Ref Alb
Export:
Name: "Alb"
DefaultTargetGroup:
Description: Default Target Group
Value: !Ref DefaultTargetGroup
Export:
Name: "DefaultTargetGroup"
Cluster and service
---
AWSTemplateFormatVersion: 2010-09-09
Parameters:
KeyName:
Type: AWS::EC2::KeyPair::KeyName
Default: webserver
DesiredCapacity:
Type: Number
Default: 2
MinSize:
Type: Number
Default: 1
MaxSize:
Type: Number
Default: 4
InstanceProfile:
Type: String
DefaultTargetGroup:
Type: String
Task:
Type: String
Albsg:
Type: String
VpcID:
Type: String
SubnetA:
Type: String
SubnetB:
Type: String
webserver:
Type: String
Resources:
MyCluster:
Type: AWS::ECS::Cluster
Properties: {}
Myservice:
Type: AWS::ECS::Service
Properties:
Cluster: !Ref MyCluster
DeploymentController:
Type: ECS
DesiredCount: 2
LaunchType: EC2
LoadBalancers:
- ContainerName: wordpress
ContainerPort: 80
TargetGroupArn: !Ref DefaultTargetGroup
#Role: !Ref InstanceProfile
SchedulingStrategy: REPLICA
ServiceName: wordpress
TaskDefinition: !Ref Task
ec2instance:
Type: AWS::AutoScaling::LaunchConfiguration
Properties:
UserData:
Fn::Base64: !Sub |
#!/bin/bash -xe
yum update -y && yum install -y aws-cfn-bootstrap
echo ECS_CLUSTER=${MyCluster} >> /etc/ecs/ecs.config
echo ECS_BACKEND_HOST= >> /etc/ecs/ecs.config
/opt/aws/bin/cfn-signal -e $? \
--stack ${AWS::StackName} \
--resource myASG \
--region ${AWS::Region}
BlockDeviceMappings:
- DeviceName: /dev/xvda
Ebs:
DeleteOnTermination: "true"
VolumeSize: 30
VolumeType: gp2
ImageId: ami-06e05a843071324d1
InstanceType: t2.small
IamInstanceProfile: !Ref InstanceProfile
KeyName: !Ref KeyName
SecurityGroups:
- Ref: webserver
myASG:
Type: AWS::AutoScaling::AutoScalingGroup
CreationPolicy:
ResourceSignal:
Timeout: PT5M
Count: !Ref DesiredCapacity
Properties:
#AutoScalingGroupName: myASG
MinSize: !Ref MinSize
MaxSize: !Ref MaxSize
DesiredCapacity: !Ref DesiredCapacity
HealthCheckGracePeriod: 300
LaunchConfigurationName:
Ref: ec2instance
VPCZoneIdentifier:
- !Ref SubnetA
- !Ref SubnetB
TargetGroupARNs:
- !Ref DefaultTargetGroup
most probably I might be giving the port wrong
In your Type: AWS::ECS::TaskDefinition you have defined port 80 for the wordpress.
But your target group is using port 8080:
Properties:
Name: alb-tg
VpcId: !Ref VpcID
Port: 8080 # <--- should be 80
Protocol: HTTP
To use 302 code for health checks:
DefaultTargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
DependsOn: Alb
Properties:
Name: alb-tg
VpcId: !Ref VpcID
Port: 80
Protocol: HTTP
Matcher:
HttpCode: 302