I'm recently learning ECS from AWS documents from Module Two - Deploy the Monolith | AWS.
While I read the YAML file for the CloudFormation, the file creates two EC2 instances in the cluster and also specified two public subnets in the VPC. I'm new to the VPC, so is it because of the creation of 2 EC2 instances so two public subnets are needed?
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
DesiredCapacity:
Type: Number
Default: '2'
Description: Number of instances to launch in your ECS cluster.
MaxSize:
Type: Number
Default: '2'
Description: Maximum number of instances that can be launched in your ECS cluster.
InstanceType:
Description: EC2 instance type
Type: String
Default: t2.micro
AllowedValues: [t2.micro, t2.small, t2.medium, t2.large, m3.medium, m3.large,
m3.xlarge, m3.2xlarge, m4.large, m4.xlarge, m4.2xlarge, m4.4xlarge, m4.10xlarge,
c4.large, c4.xlarge, c4.2xlarge, c4.4xlarge, c4.8xlarge, c3.large, c3.xlarge,
c3.2xlarge, c3.4xlarge, c3.8xlarge, r3.large, r3.xlarge, r3.2xlarge, r3.4xlarge,
r3.8xlarge, i2.xlarge, i2.2xlarge, i2.4xlarge, i2.8xlarge]
ConstraintDescription: Please choose a valid instance type.
Mappings:
AWSRegionToAMI:
us-east-1:
AMIID: ami-eca289fb
us-east-2:
AMIID: ami-446f3521
us-west-1:
AMIID: ami-9fadf8ff
us-west-2:
AMIID: ami-7abc111a
eu-west-1:
AMIID: ami-a1491ad2
eu-central-1:
AMIID: ami-54f5303b
ap-northeast-1:
AMIID: ami-9cd57ffd
ap-southeast-1:
AMIID: ami-a900a3ca
ap-southeast-2:
AMIID: ami-5781be34
SubnetConfig:
VPC:
CIDR: '10.0.0.0/16'
PublicOne:
CIDR: '10.0.0.0/24'
PublicTwo:
CIDR: '10.0.1.0/24'
Resources:
# VPC into which stack instances will be placed
VPC:
Type: AWS::EC2::VPC
Properties:
EnableDnsSupport: true
EnableDnsHostnames: true
CidrBlock: !FindInMap ['SubnetConfig', 'VPC', 'CIDR']
PublicSubnetOne:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone:
Fn::Select:
- 0
- Fn::GetAZs: {Ref: 'AWS::Region'}
VpcId: !Ref 'VPC'
CidrBlock: !FindInMap ['SubnetConfig', 'PublicOne', 'CIDR']
MapPublicIpOnLaunch: true
PublicSubnetTwo:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone:
Fn::Select:
- 1
- Fn::GetAZs: {Ref: 'AWS::Region'}
VpcId: !Ref 'VPC'
CidrBlock: !FindInMap ['SubnetConfig', 'PublicTwo', 'CIDR']
MapPublicIpOnLaunch: true
InternetGateway:
Type: AWS::EC2::InternetGateway
GatewayAttachement:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref 'VPC'
InternetGatewayId: !Ref 'InternetGateway'
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref 'VPC'
PublicRoute:
Type: AWS::EC2::Route
DependsOn: GatewayAttachement
Properties:
RouteTableId: !Ref 'PublicRouteTable'
DestinationCidrBlock: '0.0.0.0/0'
GatewayId: !Ref 'InternetGateway'
PublicSubnetOneRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnetOne
RouteTableId: !Ref PublicRouteTable
PublicSubnetTwoRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnetTwo
RouteTableId: !Ref PublicRouteTable
# ECS Resources
ECSCluster:
Type: AWS::ECS::Cluster
EcsSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: ECS Security Group
VpcId: !Ref 'VPC'
EcsSecurityGroupHTTPinbound:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref 'EcsSecurityGroup'
IpProtocol: tcp
FromPort: '80'
ToPort: '80'
CidrIp: 0.0.0.0/0
EcsSecurityGroupSSHinbound:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref 'EcsSecurityGroup'
IpProtocol: tcp
FromPort: '22'
ToPort: '22'
CidrIp: 0.0.0.0/0
EcsSecurityGroupALBports:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref 'EcsSecurityGroup'
IpProtocol: tcp
FromPort: '31000'
ToPort: '61000'
SourceSecurityGroupId: !Ref 'EcsSecurityGroup'
CloudwatchLogsGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Join ['-', [ECSLogGroup, !Ref 'AWS::StackName']]
RetentionInDays: 14
ECSALB:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: demo
Scheme: internet-facing
LoadBalancerAttributes:
- Key: idle_timeout.timeout_seconds
Value: '30'
Subnets:
- !Ref PublicSubnetOne
- !Ref PublicSubnetTwo
SecurityGroups: [!Ref 'EcsSecurityGroup']
ECSAutoScalingGroup:
Type: AWS::AutoScaling::AutoScalingGroup
Properties:
VPCZoneIdentifier:
- !Ref PublicSubnetOne
- !Ref PublicSubnetTwo
LaunchConfigurationName: !Ref 'ContainerInstances'
MinSize: '1'
MaxSize: !Ref 'MaxSize'
DesiredCapacity: !Ref 'DesiredCapacity'
CreationPolicy:
ResourceSignal:
Timeout: PT15M
UpdatePolicy:
AutoScalingReplacingUpdate:
WillReplace: 'true'
ContainerInstances:
Type: AWS::AutoScaling::LaunchConfiguration
Properties:
ImageId: !FindInMap [AWSRegionToAMI, !Ref 'AWS::Region', AMIID]
SecurityGroups: [!Ref 'EcsSecurityGroup']
InstanceType: !Ref 'InstanceType'
IamInstanceProfile: !Ref 'EC2InstanceProfile'
UserData:
Fn::Base64: !Sub |
#!/bin/bash -xe
echo ECS_CLUSTER=${ECSCluster} >> /etc/ecs/ecs.config
yum install -y aws-cfn-bootstrap
/opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource ECSAutoScalingGroup --region ${AWS::Region}
ECSServiceRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: [ecs.amazonaws.com]
Action: ['sts:AssumeRole']
Path: /
Policies:
- PolicyName: ecs-service
PolicyDocument:
Statement:
- Effect: Allow
Action:
- 'elasticloadbalancing:DeregisterInstancesFromLoadBalancer'
- 'elasticloadbalancing:DeregisterTargets'
- 'elasticloadbalancing:Describe*'
- 'elasticloadbalancing:RegisterInstancesWithLoadBalancer'
- 'elasticloadbalancing:RegisterTargets'
- 'ec2:Describe*'
- 'ec2:AuthorizeSecurityGroupIngress'
Resource: '*'
EC2Role:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: [ec2.amazonaws.com]
Action: ['sts:AssumeRole']
Path: /
Policies:
- PolicyName: ecs-service
PolicyDocument:
Statement:
- Effect: Allow
Action:
- 'ecs:CreateCluster'
- 'ecs:DeregisterContainerInstance'
- 'ecs:DiscoverPollEndpoint'
- 'ecs:Poll'
- 'ecs:RegisterContainerInstance'
- 'ecs:StartTelemetrySession'
- 'ecs:Submit*'
- 'logs:CreateLogStream'
- 'logs:PutLogEvents'
- 'ecr:GetAuthorizationToken'
- 'ecr:BatchGetImage'
- 'ecr:GetDownloadUrlForLayer'
Resource: '*'
AutoscalingRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: [application-autoscaling.amazonaws.com]
Action: ['sts:AssumeRole']
Path: /
Policies:
- PolicyName: service-autoscaling
PolicyDocument:
Statement:
- Effect: Allow
Action:
- 'application-autoscaling:*'
- 'cloudwatch:DescribeAlarms'
- 'cloudwatch:PutMetricAlarm'
- 'ecs:DescribeServices'
- 'ecs:UpdateService'
Resource: '*'
EC2InstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Path: /
Roles: [!Ref 'EC2Role']
Outputs:
ClusterName:
Description: The name of the ECS cluster, used by the deploy script
Value: !Ref 'ECSCluster'
Export:
Name: !Join [':', [!Ref "AWS::StackName", "ClusterName" ]]
Url:
Description: The url at which the application is available
Value: !Join ['', [!GetAtt 'ECSALB.DNSName']]
ALBArn:
Description: The ARN of the ALB, exported for later use in creating services
Value: !Ref 'ECSALB'
Export:
Name: !Join [':', [!Ref "AWS::StackName", "ALBArn" ]]
ECSRole:
Description: The ARN of the ECS role, exports for later use in creating services
Value: !GetAtt 'ECSServiceRole.Arn'
Export:
Name: !Join [':', [!Ref "AWS::StackName", "ECSRole" ]]
VPCId:
Description: The ID of the VPC that this stack is deployed in
Value: !Ref 'VPC'
Export:
Name: !Join [':', [!Ref "AWS::StackName", "VPCId" ]]
In your example, two AZs are being used which requires two subnets (one for each AZ). This is not related to the number of EC2 instances.
A typical best practices with AWS and other cloud vendors is to use multiple availability zones (AZ) for fault tolerance. For AWS each AZ needs its own subnet. Auto scaling and load balancing will attempt to keep the number of instances the same in each AZ.
PS. If I was learning AWS, I would not start with this example. This example is very complex but very realistic for a real world deployment. There are lots of cloudformation examples that are much easy to master to start with.
Related
I'm stuck at circular dependency loop in Cfn (ECS Stack), This can be easily resolve by segregating resources in different stack, but challenge is to resolve it within same/single stack. Spent a night solving it, still not getting any close to resolve it.
After a lot of debugging finally though of seeking some help, let me know if anyone can assist me in this, I'd really appreciate any leads or help.
`
AWSTemplateFormatVersion: '2010-09-09'
Description: This stack will deploy following resources , May
# Metadata:
Parameters:
VPC:
Description: Select One VPC available in your existing account
Type: AWS::EC2::VPC::Id
PubSubnets:
Type: 'List<AWS::EC2::Subnet::Id>'
Description: The list of PubSubnetIds in selected VPC)
PvtSubnets:
Type: 'List<AWS::EC2::Subnet::Id>'
Description: The list of PvtSubnetIds in selected VPC)
ClientName:
Type: String
Default: test
# Mappings:
# Conditions:
Resources:
ELBTargetGroup:
Type: 'AWS::ElasticLoadBalancingV2::TargetGroup'
# DependsOn:
# - ElasticLoadBalancer
Properties:
Name: "ELB-TG"
HealthCheckIntervalSeconds: 6
HealthCheckTimeoutSeconds: 5
HealthyThresholdCount: 2
Port: 80
Protocol: HTTP
UnhealthyThresholdCount: 2
VpcId: !Ref VPC
TargetType: instance
ELBSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: "ELBTraffic"
GroupDescription: "Enable HTTP access on the inbound port for ELB"
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
Tags:
- Key: Name
Value: ELBSecurityGroup
ElasticLoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
# DependsOn:
# - ELBSecurityGroup
Properties:
Subnets:
- !Ref PubSubnets
SecurityGroups:
- !Ref ELBSecurityGroup
ElbListener:
Type: 'AWS::ElasticLoadBalancingV2::Listener'
# DependsOn:
# - ElasticLoadBalancer
Properties:
DefaultActions:
- Type: forward
TargetGroupArn: !Ref ELBTargetGroup
LoadBalancerArn: !Ref ElasticLoadBalancer
Port: 80
Protocol: HTTP
AsgConfig:
Type: AWS::EC2::LaunchTemplate
DependsOn:
- ELBSecurityGroup
Properties:
LaunchTemplateName: !Sub ${ClientName}-launch-template
LaunchTemplateData:
ImageId: ami-0171959e760b38d59
InstanceType: t3.medium
SecurityGroups:
- !Ref ELBSecurityGroup
#ImageId: "ami-0171959e760b38d59"
UserData:
Fn::Base64: !Sub |
#!/bin/bash -xe
echo ECS_CLUSTER=${ECSCluster} >> /etc/ecs/ecs.config
sudo yum install -y python-pip pip
yum install -y aws-cfn-bootstrap
/opt/aws/apitools/cfn-init-2.0-6/bin/cfn-signal -e $? --stack ${AWS::StackId} --resource AsGroup --region ${AWS::Region}
/opt/aws/apitools/cfn-init-2.0-6/bin/cfn-init -v --stack ${AWS::StackName} --resource AsgConfig --region ${AWS::Region} -c default
AsGroup:
Type: AWS::AutoScaling::AutoScalingGroup
DependsOn:
- AsgConfig
Properties:
VPCZoneIdentifier:
- !Ref PvtSubnets
LaunchTemplate:
LaunchTemplateId: !Ref AsgConfig
Version: !GetAtt AsgConfig.LatestVersionNumber
# LaunchConfigurationName: !Ref AsgConfig
MinSize: '1'
DesiredCapacity: '2'
MaxSize: '4'
TargetGroupARNs:
- !Ref ELBTargetGroup
CapacityProvider:
Type: AWS::ECS::CapacityProvider
DependsOn:
- AsGroup
Properties:
AutoScalingGroupProvider:
AutoScalingGroupArn: !Ref AsGroup
CodeCommitRepository1:
Type: AWS::CodeCommit::Repository
Properties:
RepositoryDescription: "HTML code"
RepositoryName: "HTML_code"
CodeCommitRepository2:
Type: AWS::CodeCommit::Repository
Properties:
RepositoryDescription: "Python code"
RepositoryName: "Python_code"
CodeCommitRepository3:
Type: AWS::CodeCommit::Repository
Properties:
RepositoryDescription: "Node code"
RepositoryName: "Node_code"
ECRrepo:
Type: AWS::ECR::Repository
Properties:
RepositoryName: "cfn_repo"
ECSCluster:
Type: AWS::ECS::Cluster
DependsOn:
- CapacityProvider
Properties:
CapacityProviders:
- !Ref CapacityProvider
ClusterName: !Ref ClientName
Configuration:
ExecuteCommandConfiguration:
Logging: DEFAULT
ClusterSettings:
- Name: containerInsights
Value: enabled
ECSServiceRole:
Type: AWS::IAM::Role
Properties:
RoleName: "ecs-service-role"
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: ecs.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceRole
ExecutionRole:
Type: AWS::IAM::Role
Properties:
RoleName: "ecs-execution-role"
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: ecs-tasks.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
ContainerSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: "ContainerSecurityGroup"
GroupDescription: "Security group for container"
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
Service:
Type: AWS::ECS::Service
DependsOn:
- ECSCluster
- ElasticLoadBalancer
# - ECSServiceRole
# - TaskDefinition
- ELBTargetGroup
Properties:
Cluster: !Ref ECSCluster
Role: !Ref ECSServiceRole
DesiredCount: 1
TaskDefinition: !Ref TaskDefinition
LaunchType: EC2
LoadBalancers:
- ContainerName: "deployment-container"
ContainerPort: 80
TargetGroupArn: !Ref ELBTargetGroup
AutoScalingRole:
Type: AWS::IAM::Role
Properties:
RoleName: service-auto-scaling-role
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: [application-autoscaling.amazonaws.com]
Action: ["sts:AssumeRole"]
Policies:
- PolicyName: service-auto-scaling-policy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- ecs:DescribeServices
- ecs:UpdateService
- cloudwatch:PutMetricAlarm
- cloudwatch:DescribeAlarms
- cloudwatch:DeleteAlarms
Resource:
- "*"
ScalableTarget:
Type: AWS::ApplicationAutoScaling::ScalableTarget
# DependsOn:
# -
Properties:
RoleARN: !GetAtt AutoScalingRole.Arn
ResourceId: !Sub service/${ClientName}/Service
ServiceNamespace: ecs
ScalableDimension: ecs:Service:DesiredCount
MinCapacity: 1
MaxCapacity: 5
ScalingPolicy:
Type: AWS::ApplicationAutoScaling::ScalingPolicy
DependsOn:
- ScalableTarget
Properties:
PolicyName: service-auto-scaling-policy
PolicyType: TargetTrackingScaling
ScalingTargetId: !Ref ScalableTarget
TargetTrackingScalingPolicyConfiguration:
PredefinedMetricSpecification:
PredefinedMetricType: ECSServiceAverageCPUUtilization
TargetValue: 80.0
TaskDefinition:
Type: AWS::ECS::TaskDefinition
# DependsOn:
# - ExecutionRole
# - Service
Properties:
Family: deployment-task
Cpu: "256"
Memory: "512"
NetworkMode: bridge
ExecutionRoleArn: !Ref ExecutionRole
ContainerDefinitions:
- Name: deployment-container
Image: cfn_repo/cfnrepo:latest
PortMappings:
- ContainerPort: 80
RequiresCompatibilities:
- EC2
# Outputs:
# outputELBTargetGroup:
# Description: A reference to the created Target Group
# Value: !Ref ELBTargetGroup
# outputELBSecurityGroup:
# Description: A reference to the created Security Group
# Value: !Ref ELBSecurityGroup
# outputElasticLoadBalancer:
# Description: A reference to the created Elastic Load Balancer
# Value: !Ref ElasticLoadBalancer
# outputElasticListener:
# Description: A reference to the created Elastic Load Balancer Listener
# Value: !Ref ElbListener
# outputAsgConfig:
# Description: Id for autoscaling launch configuration
# Value: !Ref AsgConfig
# outputAsgGroup:
# Description: Id for autoscaling group
# Value: !Ref AsgGroup
# outputECSCluster:
# Description: Cluster name
# Value: !Ref ECSCluster
`
You have a circular dependency, as follows:
AsGroup depends on AsgConfig
AsgConfig depends on ECSCluster because of echo ECS_CLUSTER=${ECSCluster} in user data
ECSCluster depends on CapacityProvider
CapacityProvider depends on AsGroup (which is #1 above)
I suggest that instead of configuring the ECSCluster with the CapacityProvider, you simply create the ECSCluster without a capacity provider (and without the DependsOn) and add a later AWS::ECS::ClusterCapacityProviderAssociations to associate the CapacityProvider with the ECSCluster.
For example (note that I have not tested this so some tweaks may be required):
CapacityProvider:
Type: AWS::ECS::CapacityProvider
DependsOn:
- AsGroup
Properties:
AutoScalingGroupProvider:
AutoScalingGroupArn: !Ref AsGroup
ECSCluster:
Type: AWS::ECS::Cluster
Properties:
ClusterName: !Ref ClientName
Configuration:
ExecuteCommandConfiguration:
Logging: DEFAULT
ClusterSettings:
- Name: containerInsights
Value: enabled
ClusterCapacityProviderAssociation:
Type: AWS::ECS::ClusterCapacityProviderAssociations
Properties:
CapacityProviders:
- !Ref CapacityProvider
Cluster: ECSCluster
DefaultCapacityProviderStrategy:
- Base: 0
Weight: 1
CapacityProvider: !Ref CapacityProvider
Error:
service was unable to place a task because no container instance met all of its requirements. Reason: No Container Instances were found in your cluster
I see the list of resources are properly created:
VPC, subnets, route tables, internet gateways, NatGW, EC2 instance, security groups, load balancer.
Ec2 instance is up and running but still the deployment is stuck in progress and times out with rollback state.
I added the signaling script as well:
/opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource ECSAutoScalingGroup --region ${AWS::Region}
Don't know what else is missing.
Cloud formation template:
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
VPCEnv:
Type: String
MinLength: 1
Description: 'The id for references to test Services created items.'
Environment:
Type: String
Description: 'Environment to create backend infra for'
KeyName:
Type: String
Description: 'Name of an existing EC2 KeyPair to enable SSH access to the ECS instances.'
DesiredCapacity:
Type: String
Default: '1'
Description: 'Number of instances to launch in your ECS cluster.'
MaxSize:
Type: String
Default: '1'
Description: Maximum number of instances that can be launched in your ECS cluster.
InstanceType:
Description: 'EC2 instance type'
Type: String
Default: 't2.medium'
BackendContainerImage:
Type: String
MinLength: 1
Version:
Type: String
MinLength: 1
AMIID:
Type: String
MinLength: 1
Resources:
ExecutionRole:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: 'Allow'
Principal:
Service: ['ecs-tasks.amazonaws.com']
Action: ['sts:AssumeRole']
Policies:
- PolicyName: !Sub test-${Environment}-execution-user-role
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: 'Allow'
Action: ['ecs:CreateCluster', 'ecs:DeregisterContainerInstance', 'ecs:DiscoverPollEndpoint',
'ecs:Poll', 'ecs:RegisterContainerInstance', 'ecs:StartTelemetrySession',
'ecs:UpdateContainerInstancesState', 'ecs:Submit*', 'ecr:GetAuthorizationToken',
'ecr:BatchCheckLayerAvailability', 'ecr:GetDownloadUrlForLayer', 'ecr:BatchGetImage',
'logs:CreateLogStream', 'logs:PutLogEvents', 'ssm:GetParameter', 'kms:Decrypt', 'ssm:GetParameters']
Resource: '*'
ECSCluster:
Type: AWS::ECS::Cluster
EcsSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: ECS Security Group
VpcId:
Fn::ImportValue: !Sub "${VPCEnv}-VPC"
SecurityGroupIngress:
-
IpProtocol: tcp
FromPort: '22'
ToPort: '22'
SourceSecurityGroupId:
Fn::ImportValue: !Sub "${VPCEnv}-BastionSecurityGroup"
-
IpProtocol: tcp
FromPort: '31000'
ToPort: '61000'
SourceSecurityGroupId: !Ref LoadBalancerSecurityGroup
LoadBalancerSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub "test-${Environment}-LBSecurityGroup"
GroupDescription: test service Load Balancer Security Group
VpcId:
Fn::ImportValue: !Sub "${VPCEnv}-VPC"
SecurityGroupIngress:
-
IpProtocol: tcp
FromPort: '80'
ToPort: '80'
SourceSecurityGroupId:
Fn::ImportValue: !Sub "${VPCEnv}-APILoadBalancerSecurityGroup"
testServiceTaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
Family: !Sub 'test-${Environment}'
ExecutionRoleArn: !Ref ExecutionRole
ContainerDefinitions:
- Name: !Sub 'test-${Environment}-container'
Cpu: 600
Essential: 'true'
Image: !Ref BackendContainerImage
Memory: 1800
PortMappings:
- ContainerPort: 3000
ECSALBDNS:
Type: "AWS::Route53::RecordSet"
Properties:
AliasTarget:
DNSName: !GetAtt [ ECSALB, DNSName ]
HostedZoneId: !GetAtt [ ECSALB, CanonicalHostedZoneID ]
Comment: Internal DNS entry for audit service load balancer.
HostedZoneId: Z03303053NOQR6YO05FA7
Name: !Sub "api.internal.audit.service.${Environment}.altusplatform.com."
Type: A
ECSALB:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: !Sub "test-${Environment}-lb"
Scheme: internal
LoadBalancerAttributes:
- Key: idle_timeout.timeout_seconds
Value: '20'
Subnets:
Fn::Split:
- ','
- Fn::ImportValue: !Sub "${VPCEnv}-PrivateSubnets2"
SecurityGroups:
- !Ref LoadBalancerSecurityGroup
- Fn::ImportValue : !Sub "${VPCEnv}-APILoadBalancerSecurityGroup"
ALBListener:
Type: AWS::ElasticLoadBalancingV2::Listener
DependsOn: ECSServiceRole
Properties:
DefaultActions:
- Type: forward
TargetGroupArn: !Ref 'ECSTG'
LoadBalancerArn: !Ref 'ECSALB'
Port: '80'
Protocol: HTTP
ECSALBListenerRule:
Type: AWS::ElasticLoadBalancingV2::ListenerRule
DependsOn: ALBListener
Properties:
Actions:
- Type: forward
TargetGroupArn: !Ref 'ECSTG'
Conditions:
- Field: path-pattern
Values: [/]
ListenerArn: !Ref 'ALBListener'
Priority: 1
ECSTG:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
DependsOn: ECSALB
Properties:
HealthCheckIntervalSeconds: 10
HealthCheckPath: /health
HealthCheckProtocol: HTTP
HealthCheckTimeoutSeconds: 5
HealthyThresholdCount: 2
Name: !Sub "test-${Environment}-tg"
Port: 80
Protocol: HTTP
UnhealthyThresholdCount: 2
VpcId:
Fn::ImportValue: !Sub "${VPCEnv}-VPC"
ECSCapacityProvider:
Type: AWS::ECS::CapacityProvider
Properties:
AutoScalingGroupProvider:
AutoScalingGroupArn: !Ref 'ECSAutoScalingGroup'
ManagedScaling:
MaximumScalingStepSize: 10
MinimumScalingStepSize: 1
Status: ENABLED
TargetCapacity: 100
Tags:
- Key: environment
Value: !Sub '${Environment}'
ECSAutoScalingGroup:
Type: AWS::AutoScaling::AutoScalingGroup
Properties:
VPCZoneIdentifier:
Fn::Split:
- ','
- Fn::ImportValue: !Sub "${VPCEnv}-PrivateSubnets2"
LaunchConfigurationName: !Ref 'ContainerInstances'
MinSize: '1'
MaxSize: !Ref 'MaxSize'
DesiredCapacity: !Ref 'DesiredCapacity'
ContainerInstances:
Type: AWS::AutoScaling::LaunchConfiguration
Properties:
ImageId: !Sub '${AMIID}'
SecurityGroups: [!Ref 'EcsSecurityGroup']
InstanceType: !Ref 'InstanceType'
IamInstanceProfile: !Ref 'EC2InstanceProfile'
UserData:
Fn::Base64: !Sub |
#!/bin/bash -xe
yum update -y
echo ECS_CLUSTER=${ECSCluster} >> /etc/ecs/ecs.config
yum install -y aws-cfn-bootstrap
/opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource ECSAutoScalingGroup --region ${AWS::Region}
yum install -y awslogs jq
region=$(curl -s 169.254.169.254/latest/dynamic/instance-identity/document | jq -r .region)
sed -i -e "s/region = us-east-1/region = $region/g" /etc/awslogs/awscli.conf
yum install -y https://amazon-ssm-$region.s3.amazonaws.com/latest/linux_amd64/amazon-ssm-agent.rpm
service:
Type: AWS::ECS::Service
DependsOn: ALBListener
Properties:
Cluster: !Ref 'ECSCluster'
DesiredCount: '2'
LoadBalancers:
- ContainerName: !Sub 'test-${Environment}-container'
ContainerPort: 3000
TargetGroupArn: !Ref 'ECSTG'
Role: !Ref 'ECSServiceRole'
TaskDefinition: !Ref 'testServiceTaskDefinition'
DeploymentConfiguration:
MaximumPercent: 150
MinimumHealthyPercent: 50
ECSServiceRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: [ecs.amazonaws.com]
Action: ['sts:AssumeRole']
Path: /
Policies:
- PolicyName: ecs-service
PolicyDocument:
Statement:
- Effect: Allow
Action: ['elasticloadbalancing:DeregisterInstancesFromLoadBalancer', 'elasticloadbalancing:DeregisterTargets',
'elasticloadbalancing:Describe*', 'elasticloadbalancing:RegisterInstancesWithLoadBalancer',
'elasticloadbalancing:RegisterTargets', 'ec2:Describe*', 'ec2:AuthorizeSecurityGroupIngress']
Resource: '*'
ServiceScalingTarget:
Type: AWS::ApplicationAutoScaling::ScalableTarget
DependsOn: service
Properties:
MaxCapacity: 1
MinCapacity: 1
ResourceId: !Join ['', [service/, !Ref 'ECSCluster', /, !GetAtt [service, Name]]]
RoleARN: !GetAtt [AutoscalingRole, Arn]
ScalableDimension: ecs:service:DesiredCount
ServiceNamespace: ecs
ServiceScalingPolicy:
Type: AWS::ApplicationAutoScaling::ScalingPolicy
Properties:
PolicyName: AStepPolicy
PolicyType: StepScaling
ScalingTargetId: !Ref 'ServiceScalingTarget'
StepScalingPolicyConfiguration:
AdjustmentType: PercentChangeInCapacity
Cooldown: 60
MetricAggregationType: Average
StepAdjustments:
- MetricIntervalLowerBound: 0
ScalingAdjustment: 200
EC2Role:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: [ec2.amazonaws.com]
Action: ['sts:AssumeRole']
Path: /
Policies:
- PolicyName: ecs-service
PolicyDocument:
Statement:
- Effect: Allow
Action: ['ecs:CreateCluster', 'ecs:DeregisterContainerInstance', 'ecs:DiscoverPollEndpoint',
'ecs:Poll', 'ecs:RegisterContainerInstance', 'ecs:StartTelemetrySession', 'ecs:UpdateContainerInstancesState',
'ecs:Submit*', 'ecr:GetAuthorizationToken', 'ecr:BatchCheckLayerAvailability', 'ecr:GetDownloadUrlForLayer', 'ecr:BatchGetImage',
'logs:CreateLogStream', 'logs:PutLogEvents']
Resource: '*'
AutoscalingRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: [application-autoscaling.amazonaws.com]
Action: ['sts:AssumeRole']
Path: /
Policies:
- PolicyName: service-autoscaling
PolicyDocument:
Statement:
- Effect: Allow
Action: ['application-autoscaling:*', 'cloudwatch:DescribeAlarms', 'cloudwatch:PutMetricAlarm',
'ecs:DescribeServices', 'ecs:UpdateService']
Resource: '*'
EC2InstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Path: /
Roles: [!Ref 'EC2Role']
Outputs:
ecsservice:
Value: !Ref 'service'
ecscluster:
Value: !Ref 'ECSCluster'
ECSALB:
Description: Your ALB DNS URL
Value: !Join ['', [!GetAtt [ECSALB, DNSName]]]
taskdef:
Value: !Ref 'testServiceTaskDefinition'
Exported values:
Update: Added the ECSCapacityProvider with no luck
I have encountered the above issue when configuring autoscaling with an ECS Cluster. In order for autoscaling to correctly report the instances it creates back to the ECS Cluster an autoscaling group capacity provider needs to be created:
Type: AWS::ECS::CapacityProvider
Properties:
AutoScalingGroupProvider:
AutoScalingGroupProvider
Name: String
Tags:
- Tag
https://docs.aws.amazon.com/AmazonECS/latest/developerguide/asg-capacity-providers.html
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecs-capacityprovider.html
I'm creating a CloudFormation stack for ECS automation and I'm using an internal NLB.
I'm having a problem with the ListenerRule that returns the following error in CloudFormation: Resource handler returned message: "Invalid request provided: Rules are unsupported for Network Load Balancer listeners
My script is this:
AWSTemplateFormatVersion: 2010-09-09
Description: ECS Fargate
Parameters:
Name:
Type: String
VPC:
Type: AWS::EC2::VPC::Id
Subnets:
Type: List<AWS::EC2::Subnet::Id>
SecurityGroup:
Type: AWS::EC2::SecurityGroup::Id
CreationVCPEndpoint:
Type: String
AllowedValues: [true, false]
DesiredCount:
Type: String
Conditions:
CreationVCPEndpointSelected: !Equals [!Ref CreationVCPEndpoint, true]
Resources:
# Endpoints necessários para o serviço do ECS funcionar.
EndpointLogs:
Type: AWS::EC2::VPCEndpoint
Condition: CreationVCPEndpointSelected
Properties:
ServiceName: !Join
- ''
- - com.amazonaws.
- !Ref 'AWS::Region'
- .logs
PrivateDnsEnabled: true
SecurityGroupIds:
- !Ref SecurityGroup
SubnetIds:
Ref: Subnets
VpcEndpointType: Interface
VpcId:
Ref: VPC
EndpointS3:
Type: AWS::EC2::VPCEndpoint
Condition: CreationVCPEndpointSelected
Properties:
ServiceName: !Join
- ''
- - com.amazonaws.
- !Ref 'AWS::Region'
- .s3
PrivateDnsEnabled: true
SecurityGroupIds:
- !Ref SecurityGroup
SubnetIds:
Ref: Subnets
VpcEndpointType: Interface
VpcId:
Ref: VPC
EndpointECR:
Type: AWS::EC2::VPCEndpoint
Condition: CreationVCPEndpointSelected
Properties:
ServiceName: !Join
- ''
- - com.amazonaws.
- !Ref 'AWS::Region'
- .ecr.api
PrivateDnsEnabled: true
SecurityGroupIds:
- !Ref SecurityGroup
SubnetIds:
Ref: Subnets
VpcEndpointType: Interface
VpcId:
Ref: VPC
EndpointSSM:
Type: AWS::EC2::VPCEndpoint
Condition: CreationVCPEndpointSelected
Properties:
ServiceName: !Join
- ''
- - com.amazonaws.
- !Ref 'AWS::Region'
- .ssm
PrivateDnsEnabled: true
SecurityGroupIds:
- !Ref SecurityGroup
SubnetIds:
Ref: Subnets
VpcEndpointType: Interface
VpcId:
Ref: VPC
EndpointDKR:
Type: AWS::EC2::VPCEndpoint
Condition: CreationVCPEndpointSelected
Properties:
ServiceName: !Join
- ''
- - com.amazonaws.
- !Ref 'AWS::Region'
- .ecr.dkr
PrivateDnsEnabled: true
SecurityGroupIds:
- !Ref SecurityGroup
SubnetIds:
Ref: Subnets
VpcEndpointType: Interface
VpcId:
Ref: VPC
# Criação do NLB Privado
LoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: !Ref Name
Subnets: !Ref Subnets
Type: network
Scheme: internal
Tags:
- Key: Name
Value: !Ref Name
LoadBalancerListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
LoadBalancerArn: !Ref LoadBalancer
Port: 80
Protocol: TCP
DefaultActions:
- Type: forward
TargetGroupArn: !Ref TargetGroup
TargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Name: !Sub ${Name}-tg
VpcId: !Ref VPC
Port: 80
Protocol: TCP
TargetType: ip
ListenerRule:
Type: AWS::ElasticLoadBalancingV2::ListenerRule
Properties:
ListenerArn: !Ref LoadBalancerListener
Priority: 1
Conditions:
- Field: source-ip
#Values:
# - /
Actions:
- TargetGroupArn: !Ref TargetGroup
Type: forward
# Criação da IAM para o ECS
ECSIAM:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Principal:
Service:
- "ecs.amazonaws.com"
Action:
- "sts:AssumeRole"
Path: "/"
Policies:
-
PolicyName: !Ref Name
PolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Action: "*"
Resource: "*"
# Criação do ECS Fargate
ECSCluster:
Type: AWS::ECS::Cluster
Properties:
ClusterName: !Ref Name
ECSService:
Type: AWS::ECS::Service
DependsOn: ListenerRule
Properties:
Cluster: !Ref ECSCluster
Role: !Ref ECSIAM
DesiredCount: !Ref DesiredCount
TaskDefinition: !Ref ECSTaskDefinition
LoadBalancers:
- ContainerName: "website-service"
ContainerPort: 80
TargetGroupArn: !Ref TargetGroup
ECSTaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
Family: website-service
NetworkMode: awsvpc
ContainerDefinitions:
- Name: website-service
Essential: true
Image: 489732776903.dkr.ecr.us-west-2.amazonaws.com/daniel:latest
Memory: 128
#Environment:
# - Name: PRODUCT_SERVICE_URL
# Value: !Ref ProductServiceUrl
PortMappings:
- ContainerPort: 80
#LogConfiguration:
# LogDriver: awslogs
# Options:
# awslogs-group: !Ref CloudWatchLogsGroup
# awslogs-region: !Ref AWS::Region
Does anyone have any idea what the correct ListenerRule configuration is?
I managed to get the template right, there is the template ready.
AWSTemplateFormatVersion: 2010-09-09
Description: ECS Fargate
Parameters:
Name:
Type: String
VPC:
Type: AWS::EC2::VPC::Id
Subnets:
Type: List<AWS::EC2::Subnet::Id>
SecurityGroup:
Type: AWS::EC2::SecurityGroup::Id
CreationVCPEndpoint:
Type: String
AllowedValues: [true, false]
DesiredCount:
Type: String
Conditions:
CreationVCPEndpointSelected: !Equals [!Ref CreationVCPEndpoint, true]
Resources:
# Endpoints necessários para o serviço do ECS funcionar.
EndpointLogs:
Type: AWS::EC2::VPCEndpoint
Condition: CreationVCPEndpointSelected
Properties:
ServiceName: !Join
- ''
- - com.amazonaws.
- !Ref 'AWS::Region'
- .logs
PrivateDnsEnabled: true
SecurityGroupIds:
- !Ref SecurityGroup
SubnetIds:
Ref: Subnets
VpcEndpointType: Interface
VpcId:
Ref: VPC
EndpointS3:
Type: AWS::EC2::VPCEndpoint
Condition: CreationVCPEndpointSelected
Properties:
ServiceName: !Join
- ''
- - com.amazonaws.
- !Ref 'AWS::Region'
- .s3
PrivateDnsEnabled: false
SecurityGroupIds:
- !Ref SecurityGroup
SubnetIds:
Ref: Subnets
VpcEndpointType: Interface
VpcId:
Ref: VPC
EndpointECR:
Type: AWS::EC2::VPCEndpoint
Condition: CreationVCPEndpointSelected
Properties:
ServiceName: !Join
- ''
- - com.amazonaws.
- !Ref 'AWS::Region'
- .ecr.api
PrivateDnsEnabled: true
SecurityGroupIds:
- !Ref SecurityGroup
SubnetIds:
Ref: Subnets
VpcEndpointType: Interface
VpcId:
Ref: VPC
EndpointSSM:
Type: AWS::EC2::VPCEndpoint
Condition: CreationVCPEndpointSelected
Properties:
ServiceName: !Join
- ''
- - com.amazonaws.
- !Ref 'AWS::Region'
- .ssm
PrivateDnsEnabled: true
SecurityGroupIds:
- !Ref SecurityGroup
SubnetIds:
Ref: Subnets
VpcEndpointType: Interface
VpcId:
Ref: VPC
EndpointDKR:
Type: AWS::EC2::VPCEndpoint
Condition: CreationVCPEndpointSelected
Properties:
ServiceName: !Join
- ''
- - com.amazonaws.
- !Ref 'AWS::Region'
- .ecr.dkr
PrivateDnsEnabled: true
SecurityGroupIds:
- !Ref SecurityGroup
SubnetIds:
Ref: Subnets
VpcEndpointType: Interface
VpcId:
Ref: VPC
# Criação do NLB Privado
LoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: !Ref Name
Subnets: !Ref Subnets
Type: network
Scheme: internal
Tags:
- Key: Name
Value: !Ref Name
LoadBalancerListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
LoadBalancerArn: !Ref LoadBalancer
Port: 80
Protocol: TCP
DefaultActions:
- Type: forward
TargetGroupArn: !Ref TargetGroup
TargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Name: !Sub ${Name}-tg
VpcId: !Ref VPC
Port: 80
Protocol: TCP
TargetType: ip
# Criação da IAM para o ECS
ECSIAM:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Principal:
Service:
- "ecs.amazonaws.com"
- "ecs-tasks.amazonaws.com"
Action:
- "sts:AssumeRole"
Path: "/"
Policies:
-
PolicyName: !Ref Name
PolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Action: "*"
Resource: "*"
# Criação do ECS Fargate
ECSCluster:
Type: AWS::ECS::Cluster
Properties:
ClusterName: !Ref Name
ECSService:
Type: AWS::ECS::Service
DependsOn: LoadBalancerListener
Properties:
Cluster: !Ref ECSCluster
LaunchType: FARGATE
DesiredCount: !Ref DesiredCount
TaskDefinition: !Ref ECSTaskDefinition
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: ENABLED
SecurityGroups:
- sg-0c2e8f8e84a22bdd4
Subnets:
- subnet-04d79b3e4ac16ba6f
- subnet-07baab102179ee184
LoadBalancers:
- ContainerName: "test-container"
ContainerPort: 80
TargetGroupArn: !Ref TargetGroup
ECSTaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
Family: test-container
NetworkMode: awsvpc
Cpu: 512
Memory: 1024
ExecutionRoleArn: !Ref ECSIAM
RequiresCompatibilities:
- FARGATE
ContainerDefinitions:
- Name: test-container
Essential: true
Image: URI
Cpu: 512
Memory: 1024
PortMappings:
- ContainerPort: 80
I'm experiencing a very annoying problem. I created a CI/CD pipelines using AWS CodePipeline and CloudFormation.
This is the template.yml used by CloudFormation to create a ScheduledTask on ECS.
AWSTemplateFormatVersion: "2010-09-09"
Description: Template for deploying a ECR image on ECS
Resources:
VPC:
Type: "AWS::EC2::VPC"
Properties:
CidrBlock: "10.0.0.0/16"
EnableDnsSupport: true
EnableDnsHostnames: true
InstanceTenancy: default
Subnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [0, !GetAZs ""]
CidrBlock: !Sub "10.0.0.0/20"
MapPublicIpOnLaunch: true
Subnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [1, !GetAZs ""]
CidrBlock: !Sub "10.0.32.0/20"
MapPublicIpOnLaunch: true
InternetGateway:
Type: "AWS::EC2::InternetGateway"
VPCGatewayAttachment:
Type: "AWS::EC2::VPCGatewayAttachment"
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref VPC
RouteTable:
Type: "AWS::EC2::RouteTable"
Properties:
VpcId: !Ref VPC
RouteTableAssociation1:
Type: "AWS::EC2::SubnetRouteTableAssociation"
Properties:
SubnetId: !Ref Subnet1
RouteTableId: !Ref RouteTable
RouteTableAssociation2:
Type: "AWS::EC2::SubnetRouteTableAssociation"
Properties:
SubnetId: !Ref Subnet2
RouteTableId: !Ref RouteTable
InternetRoute:
Type: "AWS::EC2::Route"
DependsOn: VPCGatewayAttachment
Properties:
GatewayId: !Ref InternetGateway
RouteTableId: !Ref RouteTable
DestinationCidrBlock: "0.0.0.0/0"
ECSCluster:
Type: AWS::ECS::Cluster
Properties:
ClusterName: "SLAComputation"
LoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: ecs-services
Subnets:
- !Ref "Subnet1"
- !Ref "Subnet2"
SecurityGroups:
- !Ref LoadBalancerSecurityGroup
LoadBalancerListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
LoadBalancerArn: !Ref LoadBalancer
Protocol: HTTP
Port: 80
DefaultActions:
- Type: forward
TargetGroupArn: !Ref DefaultTargetGroup
LoadBalancerSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group for loadbalancer to services on ECS
VpcId: !Ref "VPC"
SecurityGroupIngress:
- CidrIp: 0.0.0.0/0
IpProtocol: -1
DefaultTargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Name: default
VpcId: !Ref "VPC"
Protocol: "HTTP"
Port: "80"
CloudWatchLogsGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: "sla_computation"
RetentionInDays: 1
ContainerSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
VpcId: !Ref "VPC"
GroupDescription: for ecs containers
SecurityGroupIngress:
- SourceSecurityGroupId: !Ref "LoadBalancerSecurityGroup"
IpProtocol: -1
Task:
Type: AWS::ECS::TaskDefinition
Properties:
Family: apis
Cpu: 1024
Memory: 2048
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
ExecutionRoleArn: !Ref ECSTaskExecutionRole
ContainerDefinitions:
- Name: ass001
Image: !Sub 649905970782.dkr.ecr.eu-west-1.amazonaws.com/ass001:latest
Cpu: 1024
Memory: 2048
HealthCheck:
Command: [ "CMD-SHELL", "exit 0" ]
Interval: 30
Retries: 5
Timeout: 10
StartPeriod: 30
PortMappings:
- ContainerPort: 8080
Protocol: tcp
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-group: "sla_computation"
awslogs-region: !Ref AWS::Region
awslogs-stream-prefix: "ass001"
TargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Name: employee-tg
VpcId: !Ref VPC
Port: 80
Protocol: HTTP
Matcher:
HttpCode: 200-299
TargetType: ip
ListenerRule:
Type: AWS::ElasticLoadBalancingV2::ListenerRule
Properties:
ListenerArn: !Ref LoadBalancerListener
Priority: 2
Conditions:
- Field: path-pattern
Values:
- /*
Actions:
- TargetGroupArn: !Ref TargetGroup
Type: forward
ECSTaskExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: [ecs-tasks.amazonaws.com]
Action: ["sts:AssumeRole"]
Path: /
Policies:
- PolicyName: AmazonECSTaskExecutionRolePolicy
PolicyDocument:
Statement:
- Effect: Allow
Action:
# ECS Tasks to download images from ECR
- "ecr:GetAuthorizationToken"
- "ecr:BatchCheckLayerAvailability"
- "ecr:GetDownloadUrlForLayer"
- "ecr:BatchGetImage"
# ECS tasks to upload logs to CloudWatch
- "logs:CreateLogStream"
- "logs:PutLogEvents"
Resource: "*"
TaskSchedule:
Type: AWS::Events::Rule
Properties:
Description: SLA rule ass001
Name: ass001
ScheduleExpression: cron(0/5 * * * ? *)
State: ENABLED
Targets:
- Arn:
!GetAtt ECSCluster.Arn
Id: dump-data-ecs-task
RoleArn:
!GetAtt ECSTaskExecutionRole.Arn
EcsParameters:
TaskDefinitionArn:
!Ref Task
TaskCount: 1
LaunchType: FARGATE
PlatformVersion: LATEST
NetworkConfiguration:
AwsVpcConfiguration:
AssignPublicIp: ENABLED
SecurityGroups:
- sg-07db5ae6616a8c5fc
Subnets:
- subnet-031d0787ad492c1c4
TaskSchedule:
Type: AWS::Events::Rule
Properties:
Description: SLA rule ass002
Name: ass002
ScheduleExpression: cron(0/5 * * * ? *)
State: ENABLED
Targets:
- Arn:
!GetAtt ECSCluster.Arn
Id: dump-data-ecs-task
RoleArn:
!GetAtt ECSTaskExecutionRole.Arn
EcsParameters:
TaskDefinitionArn:
!Ref Task
TaskCount: 1
LaunchType: FARGATE
PlatformVersion: LATEST
NetworkConfiguration:
AwsVpcConfiguration:
AssignPublicIp: ENABLED
SecurityGroups:
- sg-07db5ae6616a8c5fc
Subnets:
- subnet-031d0787ad492c1c4
Outputs:
ApiEndpoint:
Description: Employee API Endpoint
Value: !Join ["", ["http://", !GetAtt LoadBalancer.DNSName, "/employees"]]
Export:
Name: "EmployeeApiEndpoint"
The ScheduledTask is created successfully but it is not running actually. Very strange. But the strangest thing is that the ScheduledTask starts working when I click on "Edit" from the AWS console and (without making any change) I save.
The main issue I see is that you are using wrong role for your scheduled rule. It can't be !GetAtt ECSTaskExecutionRole.Arn. Instead you should create new role (or edit existing one) which has AmazonEC2ContainerServiceEventsRole AWS Managed policy.
It works after you edit in console, because AWS console will probably create the correct role in the background and use it instead of yours.
I changed the status code for the health check from 200 to 302. After which the Target group turned to a healthy state. I wasn't able to get the healthy state with the 200 code. But when I try to access the DNS of the ALB. It times out and haven't been able to figure out why?
The ecs drupal instances logs provide these outputs "GET / HTTP/1.1" 302 573 "-" "ELB-HealthChecker/2.0" drupal
Any help would be much appreciated
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
KeyName:
Type: AWS::EC2::KeyPair::KeyName
Description: Name of an existing EC2 KeyPair to enable SSH access to the ECS instances.
VpcId:
Type: AWS::EC2::VPC::Id
Description: Select a VPC that allows instances access to the Internet.
SubnetId:
Type: List<AWS::EC2::Subnet::Id>
Description: Select at two subnets in your selected VPC.
DesiredCapacity:
Type: Number
Default: '1'
Description: Number of instances to launch in your ECS cluster.
MaxSize:
Type: Number
Default: '1'
Description: Maximum number of instances that can be launched in your ECS cluster.
InstanceType:
Description: EC2 instance type
Type: String
Default: t2.medium
AllowedValues:
- t2.micro
- t2.small
- t2.medium
- t2.large
- m3.medium
- m3.large
- m3.xlarge
- m3.2xlarge
- m4.large
- m4.xlarge
- m4.2xlarge
- m4.4xlarge
- m4.10xlarge
- c4.large
- c4.xlarge
- c4.2xlarge
- c4.4xlarge
- c4.8xlarge
- c3.large
- c3.xlarge
- c3.2xlarge
- c3.4xlarge
- c3.8xlarge
- r3.large
- r3.xlarge
- r3.2xlarge
- r3.4xlarge
- r3.8xlarge
- i2.xlarge
- i2.2xlarge
- i2.4xlarge
- i2.8xlarge
ConstraintDescription: Please choose a valid instance type.
Mappings:
AWSRegionToAMI:
us-east-1:
AMIID: ami-0be13a99cd970f6a9
us-east-2:
AMIID: ami-0a9e12068cb98a01d
us-west-1:
AMIID: ami-0fa6c8d131a220017
us-west-2:
AMIID: ami-078c97cf1cefd1b38
eu-west-1:
AMIID: ami-0c9ef930279337028
eu-central-1:
AMIID: ami-065c1e34da68f2b02
ap-northeast-1:
AMIID: ami-02265963d1614d04d
ap-southeast-1:
AMIID: ami-0b68661b29b9e058c
ap-southeast-2:
AMIID: ami-00e4b147599c13588
ap-south-1:
AMIID: ami-036eaa870decb368d
Resources:
ECSCluster:
Type: AWS::ECS::Cluster
#DependsOn: ECSAutoScalingGroup
#Properties:
#CapacityProviders:
#- Nse
ECSCapacityProvider:
Type: AWS::ECS::CapacityProvider
Properties:
AutoScalingGroupProvider:
AutoScalingGroupArn: !Ref ECSAutoScalingGroup
Name: Nse
EcsSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: ECS Security Group
VpcId: !Ref 'VpcId'
EcsSecurityGroupHTTPinbound:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref 'EcsSecurityGroup'
IpProtocol: tcp
FromPort: '80'
ToPort: '80'
CidrIp: 0.0.0.0/0
EcsSecurityGroupDrupalinbound:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref 'EcsSecurityGroup'
IpProtocol: tcp
FromPort: '8080'
ToPort: '8080'
CidrIp: 0.0.0.0/0
EcsSecurityGroupSQLInbound:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref 'EcsSecurityGroup'
IpProtocol: tcp
FromPort: '3306'
ToPort: '3306'
CidrIp: 0.0.0.0/0
EcsSecurityGroupSSHinbound:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref 'EcsSecurityGroup'
IpProtocol: tcp
FromPort: '22'
ToPort: '22'
CidrIp: 0.0.0.0/0
EcsSecurityGroupALBports:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref 'EcsSecurityGroup'
IpProtocol: tcp
FromPort: '31000'
ToPort: '61000'
SourceSecurityGroupId: !Ref 'EcsSecurityGroup'
CloudwatchLogsGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Join ['-', [ECSLogGroup, !Ref 'AWS::StackName']]
RetentionInDays: 14
taskdefinition:
Type: AWS::ECS::TaskDefinition
Properties:
Family: !Join ['', [!Ref 'AWS::StackName', -ecs-demo-app]]
ExecutionRoleArn: arn:aws:iam::268500393272:role/ecsTaskExecutionRole
#TaskRoleArn: arn:aws:iam::aws:policy/AmazonS3FullAccess
NetworkMode: bridge
#RequiredCompatibilities:
#- "EC2"
ContainerDefinitions:
- Name: drupal
Cpu: 256
#Essential: false
Image: drupal:latest
Memory: 512
Hostname: drupal
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-group: !Ref 'CloudwatchLogsGroup'
awslogs-region: !Ref 'AWS::Region'
awslogs-stream-prefix: ecs-demo-app
MountPoints:
- ContainerPath: /var/www/html
SourceVolume: drupal-data
PortMappings:
- ContainerPort: 80
HostPort: 8080
Volumes:
- Name: drupal-data
ECSALB:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
#DependsOn: service
Properties:
Name: ECSALB
Scheme: internet-facing
LoadBalancerAttributes:
- Key: idle_timeout.timeout_seconds
Value: '60'
Subnets: !Ref 'SubnetId'
SecurityGroups: [!Ref 'EcsSecurityGroup']
ALBListener:
Type: AWS::ElasticLoadBalancingV2::Listener
DependsOn: ECSServiceRole
Properties:
DefaultActions:
- Type: forward
TargetGroupArn: !Ref 'ECSTG'
#RedirectConfig:
#Protocol: "HTTP"
#Host: "#{host}"
#Path: "/#{path}"
#Query: "#{query}"
#Port: 80
#StatusCode: "HTTP_302"
LoadBalancerArn: !Ref 'ECSALB'
Port: '80'
Protocol: HTTP
ECSALBListenerRule:
Type: AWS::ElasticLoadBalancingV2::ListenerRule
DependsOn: ALBListener
Properties:
Actions:
- Type: forward
TargetGroupArn: !Ref 'ECSTG'
#RedirectConfig:
#Protocol: "HTTP"
#Host: "#{host}"
#Path: "/#{path}"
#Query: "#{query}"
#Port: 80
#StatusCode: "HTTP_302"
Conditions:
- Field: path-pattern
Values: [/]
ListenerArn: !Ref 'ALBListener'
Priority: 1
ECSTG:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
DependsOn: ECSALB
Properties:
HealthCheckIntervalSeconds: 80
HealthCheckPath: /
HealthCheckProtocol: HTTP
HealthCheckTimeoutSeconds: 60
HealthyThresholdCount: 2
Matcher:
HttpCode: 302
Name: ECSTG
Port: 8080
Protocol: HTTP
TargetType: instance
UnhealthyThresholdCount: 2
VpcId: !Ref 'VpcId'
ECSAutoScalingGroup:
Type: AWS::AutoScaling::AutoScalingGroup
Properties:
VPCZoneIdentifier: !Ref 'SubnetId'
#VPCZoneIdentifier:
#- subnet-0c228c2e5e42708aa
#- subnet-0bf3fcea01d2dd0a4
#- subnet-0c6a01197480771b3
LaunchConfigurationName: !Ref 'ContainerInstances'
#LoadBalancerNames:
#- ECSALB
TargetGroupARNs: [!Ref 'ECSTG']
MinSize: '1'
MaxSize: !Ref 'MaxSize'
DesiredCapacity: !Ref 'DesiredCapacity'
CreationPolicy:
ResourceSignal:
Timeout: PT15M
UpdatePolicy:
AutoScalingReplacingUpdate:
WillReplace: 'true'
ContainerInstances:
Type: AWS::AutoScaling::LaunchConfiguration
Properties:
ImageId: !FindInMap [AWSRegionToAMI, !Ref 'AWS::Region', AMIID]
SecurityGroups: [!Ref 'EcsSecurityGroup']
InstanceType: !Ref 'InstanceType'
IamInstanceProfile: !Ref 'EC2InstanceProfile'
KeyName: !Ref 'KeyName'
UserData:
Fn::Base64: !Sub |
#!/bin/bash -xe
echo ECS_CLUSTER=${ECSCluster} >> /etc/ecs/ecs.config
yum update -y
yum install tmux -y
yum install -y aws-cfn-bootstrap
#/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource ECSAutoScalingGroup --region ${AWS::Region}
#sudo cat /var/log/cloud-init-output.log
/opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource ECSAutoScalingGroup --region ${AWS::Region}
service:
Type: AWS::ECS::Service
DependsOn:
- ALBListener
#- ECSAutoScalingGroup
Properties:
Cluster: !Ref 'ECSCluster'
DesiredCount: '1'
HealthCheckGracePeriodSeconds: 2147483647
LoadBalancers:
- ContainerName: drupal
ContainerPort: '80'
TargetGroupArn: !Ref 'ECSTG'
#LoadBalancerName: !GetAtt ECSALB.LoadBalancerName
#LoadBalancerName: ECSALB
#NetworkConfiguration:
# AwsvpcConfiguration:
# AssignPublicIp: ENABLED
# SecurityGroups:
# - !Ref 'EcsSecurityGroup'
# Subnets: !Ref 'SubnetId'
#ServiceName: Ecs
Role: !Ref 'ECSServiceRole'
TaskDefinition: !Ref 'taskdefinition'
ECSServiceRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: [ecs.amazonaws.com]
Action: ['sts:AssumeRole']
Path: /
Policies:
- PolicyName: ecs-service
PolicyDocument:
Statement:
- Effect: Allow
Action: ['elasticloadbalancing:DeregisterInstancesFromLoadBalancer', 'elasticloadbalancing:DeregisterTargets',
'elasticloadbalancing:Describe*', 'elasticloadbalancing:RegisterInstancesWithLoadBalancer',
'elasticloadbalancing:RegisterTargets', 'ec2:Describe*', 'ec2:AuthorizeSecurityGroupIngress']
Resource: '*'
ServiceScalingTarget:
Type: AWS::ApplicationAutoScaling::ScalableTarget
#DependsOn: service
Properties:
MaxCapacity: 3
MinCapacity: 1
ResourceId: !Join ['', [service/, !Ref 'ECSCluster', /, !GetAtt [service, Name]]]
RoleARN: !GetAtt [AutoscalingRole, Arn]
ScalableDimension: ecs:service:DesiredCount
ServiceNamespace: ecs
ServiceScalingPolicy:
Type: AWS::ApplicationAutoScaling::ScalingPolicy
Properties:
PolicyName: AStepPolicy
PolicyType: StepScaling
ScalingTargetId: !Ref 'ServiceScalingTarget'
StepScalingPolicyConfiguration:
AdjustmentType: PercentChangeInCapacity
Cooldown: 60
MetricAggregationType: Average
StepAdjustments:
- MetricIntervalLowerBound: 0
ScalingAdjustment: 200
ALB500sAlarmScaleUp:
Type: AWS::CloudWatch::Alarm
Properties:
EvaluationPeriods: '1'
Statistic: Average
Threshold: '10'
AlarmDescription: Alarm if our ALB generates too many HTTP 500s.
Period: '60'
AlarmActions: [!Ref 'ServiceScalingPolicy']
Namespace: AWS/ApplicationELB
Dimensions:
- Name: LoadBalancer
Value: !GetAtt
- ECSALB
- LoadBalancerFullName
ComparisonOperator: GreaterThanThreshold
MetricName: HTTPCode_ELB_5XX_Count
EC2Role:
Type: AWS::IAM::Role
Properties:
#ManagedPolicyArns:
#- arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: [ec2.amazonaws.com]
Action: ['sts:AssumeRole']
Path: /
Policies:
- PolicyName: ecs-service
PolicyDocument:
Statement:
- Effect: Allow
Action: ['ecs:CreateCluster', 'ecs:DeregisterContainerInstance', 'ecs:DiscoverPollEndpoint',
'ecs:Poll', 'ecs:RegisterContainerInstance', 'ecs:StartTelemetrySession',
'ecs:Submit*', 'logs:CreateLogStream', 'logs:PutLogEvents']
Resource: '*'
AutoscalingRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: [application-autoscaling.amazonaws.com]
Action: ['sts:AssumeRole']
Path: /
Policies:
- PolicyName: service-autoscaling
PolicyDocument:
Statement:
- Effect: Allow
Action: ['application-autoscaling:*', 'cloudwatch:DescribeAlarms', 'cloudwatch:PutMetricAlarm',
'ecs:DescribeServices', 'ecs:UpdateService']
Resource: '*'
EC2InstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Path: /
Roles: [!Ref 'EC2Role']
Outputs:
ecsservice:
Value: !Ref 'service'
ecscluster:
Value: !Ref 'ECSCluster'
ECSALB:
Description: Your ALB DNS URL
Value: !Join ['', [!GetAtt [ECSALB, DNSName]]]
taskdef:
Value: !Ref 'taskdefinition'
Based on the comments.
The template is fine. The ALB does not work because it is placed in private subnets along with ECS service. Assuming that private subnets are correctly setup to work with NAT gateway and access the internet, the following should be made:
Place ALB in public subnets - it must be there, as otherwise it will no be accessible from the internet.
Also double check all the route tables for NAT, public subnets, internet gateway.