Skip to content
Permalink
Branch: master
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
903 lines (902 sloc) 32.7 KB
---
# Copyright 2018 widdix GmbH
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# FIXME add to cfn-modules
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Fargate: service that runs on a Fargate cluster based on fargate/cluster.yaml and uses a dedicated ALB, a cloudonaut.io template'
Metadata:
'AWS::CloudFormation::Interface':
ParameterGroups:
- Label:
default: 'Parent Stacks'
Parameters:
- ParentVPCStack
- ParentClusterStack
- ParentAuthProxyStack
- ParentAlertStack
- ParentZoneStack
- ParentS3StackAccessLog
- ParentClientStack1
- ParentClientStack2
- ParentClientStack3
- Label:
default: 'Load Balancer Parameters'
Parameters:
- LoadBalancerScheme
- LoadBalancerCertificateArn
- LoadBalancerIdleTimeout
- LoadBalancerDeregistrationDelay
- Label:
default: 'Task Parameters'
Parameters:
- TaskPolicies
- AmbassadorImage
- AmbassadorCommand
- AmbassadorPort
- AmbassadorEnvironment1Key
- AmbassadorEnvironment1Value
- AmbassadorEnvironment2Key
- AmbassadorEnvironment2Value
- AmbassadorEnvironment3Key
- AmbassadorEnvironment3Value
- AppImage
- AppCommand
- AppPort
- AppEnvironment1Key
- AppEnvironment1Value
- AppEnvironment2Key
- AppEnvironment2Value
- AppEnvironment3Key
- AppEnvironment3Value
- SidecarImage
- SidecarCommand
- SidecarPort
- SidecarEnvironment1Key
- SidecarEnvironment1Value
- SidecarEnvironment2Key
- SidecarEnvironment2Value
- SidecarEnvironment3Key
- SidecarEnvironment3Value
- Label:
default: 'Service Parameters'
Parameters:
- SubDomainNameWithDot
- Cpu
- Memory
- SubnetsReach
- AutoScaling
- DesiredCount
- MaxCapacity
- MinCapacity
- HealthCheckGracePeriod
- LogsRetentionInDays
Parameters:
ParentVPCStack:
Description: 'Stack name of parent VPC stack based on vpc/vpc-*azs.yaml template.'
Type: String
ParentClusterStack:
Description: 'Stack name of parent Cluster stack based on fargate/cluster.yaml template.'
Type: String
ParentAuthProxyStack:
Description: 'Optional stack name of parent auth proxy stack based on security/auth-proxy-*.yaml template.'
Type: String
Default: ''
ParentAlertStack:
Description: 'Optional but recommended stack name of parent alert stack based on operations/alert.yaml template.'
Type: String
Default: ''
ParentZoneStack:
Description: 'Optional stack name of parent zone stack based on vpc/zone-*.yaml template.'
Type: String
Default: ''
ParentS3StackAccessLog:
Description: 'Optional stack name of parent s3 stack based on state/s3.yaml template (with Access set to ElbAccessLogWrite) to store access logs.'
Type: String
Default: ''
ParentClientStack1:
Description: 'Optional stack name of parent Client Security Group stack based on state/client-sg.yaml template to allow network access from the service to whatever uses the client security group.'
Type: String
Default: ''
ParentClientStack2:
Description: 'Optional stack name of parent Client Security Group stack based on state/client-sg.yaml template to allow network access from the service to whatever uses the client security group.'
Type: String
Default: ''
ParentClientStack3:
Description: 'Optional stack name of parent Client Security Group stack based on state/client-sg.yaml template to allow network access from the service to whatever uses the client security group.'
Type: String
Default: ''
LoadBalancerScheme:
Description: 'Indicates whether the load balancer in front of the ECS service is internet-facing or internal.'
Type: String
Default: 'internet-facing'
AllowedValues:
- 'internet-facing'
- internal
LoadBalancerCertificateArn:
Description: 'Optional Amazon Resource Name (ARN) of the certificate to associate with the load balancer. If set, HTTP requests are redirected to HTTPS.'
Type: String
Default: ''
LoadBalancerIdleTimeout:
Description: 'The idle timeout value, in seconds.'
Type: Number
Default: 60
MinValue: 1
MaxValue: 4000
LoadBalancerDeregistrationDelay:
Description: 'The amount time (in seconds) to wait before changing the state of a deregistering target from draining to unused.'
Type: Number
Default: 60
ConstraintDescription: 'Must be in the range [0-3600]'
MinValue: 0
MaxValue: 3600
TaskPolicies:
Description: 'Comma-delimited list of IAM managed policy ARNs to attach to the task IAM role'
Type: String
Default: ''
AmbassadorImage:
Description: 'Optional Docker image to use for the ambassador container (https://docs.microsoft.com/en-us/azure/architecture/patterns/ambassador). You can use images in the Docker Hub registry or specify other repositories (repository-url/image:tag).'
Type: String
Default: ''
AmbassadorCommand:
Description: 'Optional command used when starting the ambassador container.'
Type: String
Default: ''
AmbassadorPort:
Description: 'The port exposed by the ambassador container that receives traffic from the load balancer (AmbassadorPort <> AppPort <> SidecarPort; ignored if AmbassadorImage is not set).'
Type: Number
Default: 8000
MinValue: 1
MaxValue: 49150
AmbassadorEnvironment1Key:
Description: 'Optional environment variable 1 key for ambassador container.'
Type: String
Default: ''
AmbassadorEnvironment1Value:
Description: 'Optional environment variable 1 value for ambassador container.'
Type: String
Default: ''
AmbassadorEnvironment2Key:
Description: 'Optional environment variable 2 key for ambassador container.'
Type: String
Default: ''
AmbassadorEnvironment2Value:
Description: 'Optional environment variable 2 value for ambassador container.'
Type: String
Default: ''
AmbassadorEnvironment3Key:
Description: 'Optional environment variable 3 key for ambassador container.'
Type: String
Default: ''
AmbassadorEnvironment3Value:
Description: 'Optional environment variable 3 value for ambassador container.'
Type: String
Default: ''
AppImage:
Description: 'The Docker image to use for the app container. You can use images in the Docker Hub registry or specify other repositories (repository-url/image:tag).'
Type: String
Default: 'widdix/hello:v1'
AppCommand:
Description: 'Optional commands (comma-delimited) used when starting the app container.'
Type: String
Default: ''
AppPort:
Description: 'The port exposed by the app container that receives traffic from the load balancer or the ambassador container (AppPort <> AmbassadorPort <> SidecarPort).'
Type: Number
Default: 80
MinValue: 1
MaxValue: 49150
AppEnvironment1Key:
Description: 'Optional environment variable 1 key for app container.'
Type: String
Default: ''
AppEnvironment1Value:
Description: 'Optional environment variable 1 value for app container.'
Type: String
Default: ''
AppEnvironment2Key:
Description: 'Optional environment variable 2 key for app container.'
Type: String
Default: ''
AppEnvironment2Value:
Description: 'Optional environment variable 2 value for app container.'
Type: String
Default: ''
AppEnvironment3Key:
Description: 'Optional environment variable 3 key for app container.'
Type: String
Default: ''
AppEnvironment3Value:
Description: 'Optional environment variable 3 value for app container.'
Type: String
Default: ''
SidecarImage:
Description: 'Optional Docker image to use for the sidecar container (https://docs.microsoft.com/en-us/azure/architecture/patterns/sidecar). You can use images in the Docker Hub registry or specify other repositories (repository-url/image:tag).'
Type: String
Default: ''
SidecarCommand:
Description: 'Optional command used when starting the sidecar container.'
Type: String
Default: ''
SidecarPort:
Description: 'The port exposed by the sidecar container reachable from the app container on host localhost (SidecarPort <> AmbassadorPort <> AppPort).'
Type: Number
Default: 9000
MinValue: 1
MaxValue: 49150
SidecarEnvironment1Key:
Description: 'Optional environment variable 1 key for sidecar container.'
Type: String
Default: ''
SidecarEnvironment1Value:
Description: 'Optional environment variable 1 value for sidecar container.'
Type: String
Default: ''
SidecarEnvironment2Key:
Description: 'Optional environment variable 2 key for sidecar container.'
Type: String
Default: ''
SidecarEnvironment2Value:
Description: 'Optional environment variable 2 value for sidecar container.'
Type: String
Default: ''
SidecarEnvironment3Key:
Description: 'Optional environment variable 3 key for sidecar container.'
Type: String
Default: ''
SidecarEnvironment3Value:
Description: 'Optional environment variable 3 value for sidecar container.'
Type: String
Default: ''
SubDomainNameWithDot:
Description: 'Name that is used to create the DNS entry with trailing dot, e.g. §{SubDomainNameWithDot}§{HostedZoneName}. Leave blank for naked (or apex and bare) domain. Requires ParentZoneStack parameter!'
Type: String
Default: ''
Cpu:
Description: 'The minimum number of vCPUs to reserve for the container.'
Type: String
Default: '0.25'
AllowedValues: ['0.25', '0.5', '1', '2', '4']
Memory:
Description: 'The amount (in GB) of memory used by the task.'
Type: String
Default: '0.5'
AllowedValues: ['0.5', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30']
DesiredCount:
Description: 'The number of simultaneous tasks, that you want to run on the cluster.'
Type: Number
Default: 2
ConstraintDescription: 'Must be >= 1'
MinValue: 1
MaxCapacity:
Description: 'The maximum number of simultaneous tasks, that you want to run on the cluster.'
Type: Number
Default: 4
ConstraintDescription: 'Must be >= 1'
MinValue: 1
MinCapacity:
Description: 'The minimum number of simultaneous tasks, that you want to run on the cluster.'
Type: Number
Default: 2
ConstraintDescription: 'Must be >= 1'
MinValue: 1
LogsRetentionInDays:
Description: 'Specifies the number of days you want to retain log events in the specified log group.'
Type: Number
Default: 14
AllowedValues: [1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1827, 3653]
SubnetsReach:
Description: 'Should the service have direct access to the Internet or do you prefer private subnets with NAT?'
Type: String
Default: Public
AllowedValues:
- Public
- Private
AutoScaling:
Description: 'Scale number of tasks based on CPU load?'
Type: String
Default: 'true'
AllowedValues: ['true', 'false']
HealthCheckGracePeriod:
Description: 'The period of time, in seconds, that the Amazon ECS service scheduler ignores unhealthy Elastic Load Balancing target health checks after a task has first started.'
Type: Number
Default: 60
MinValue: 0
MaxValue: 1800
Mappings:
CpuMap:
'0.25':
Cpu: 256
'0.5':
Cpu: 512
'1':
Cpu: 1024
'2':
Cpu: 2048
'4':
Cpu: 4096
MemoryMap:
'0.5':
Memory: 512
'1':
Memory: 1024
'2':
Memory: 2048
'3':
Memory: 3072
'4':
Memory: 4096
'5':
Memory: 5120
'6':
Memory: 6144
'7':
Memory: 7168
'8':
Memory: 8192
'9':
Memory: 9216
'10':
Memory: 10240
'11':
Memory: 11264
'12':
Memory: 12288
'13':
Memory: 13312
'14':
Memory: 14336
'15':
Memory: 15360
'16':
Memory: 16384
'17':
Memory: 17408
'18':
Memory: 18432
'19':
Memory: 19456
'20':
Memory: 20480
'21':
Memory: 21504
'22':
Memory: 22528
'23':
Memory: 23552
'24':
Memory: 24576
'25':
Memory: 25600
'26':
Memory: 26624
'27':
Memory: 27648
'28':
Memory: 28672
'29':
Memory: 29696
'30':
Memory: 30720
Conditions:
HasAuthProxySecurityGroup: !Not [!Equals [!Ref ParentAuthProxyStack, '']]
HasNotAuthProxySecurityGroup: !Equals [!Ref ParentAuthProxyStack, '']
HasLoadBalancerSchemeInternal: !Equals [!Ref LoadBalancerScheme, 'internal']
HasLoadBalancerCertificateArn: !Not [!Equals [!Ref LoadBalancerCertificateArn, '']]
HasAuthProxySecurityGroupAndLoadBalancerCertificateArn: !And [!Condition HasAuthProxySecurityGroup, !Condition HasLoadBalancerCertificateArn]
HasNotAuthProxySecurityGroupAndLoadBalancerCertificateArn: !And [!Condition HasNotAuthProxySecurityGroup, !Condition HasLoadBalancerCertificateArn]
HasAlertTopic: !Not [!Equals [!Ref ParentAlertStack, '']]
HasZone: !Not [!Equals [!Ref ParentZoneStack, '']]
HasS3Bucket: !Not [!Equals [!Ref ParentS3StackAccessLog, '']]
HasSubnetsReachPublic: !Equals [!Ref SubnetsReach, Public]
HasAutoScaling: !Equals [!Ref AutoScaling, 'true']
HasClientSecurityGroup1: !Not [!Equals [!Ref ParentClientStack1, '']]
HasClientSecurityGroup2: !Not [!Equals [!Ref ParentClientStack2, '']]
HasClientSecurityGroup3: !Not [!Equals [!Ref ParentClientStack3, '']]
HasTaskPolicies: !Not [!Equals [!Ref TaskPolicies, '']]
HasAppCommand: !Not [!Equals [!Ref AppCommand, '']]
HasAppEnvironment1Key: !Not [!Equals [!Ref AppEnvironment1Key, '']]
HasAppEnvironment2Key: !Not [!Equals [!Ref AppEnvironment2Key, '']]
HasAppEnvironment3Key: !Not [!Equals [!Ref AppEnvironment3Key, '']]
HasAmbassadorImage: !Not [!Equals [!Ref AmbassadorImage, '']]
HasAmbassadorCommand: !Not [!Equals [!Ref AmbassadorCommand, '']]
HasAmbassadorEnvironment1Key: !Not [!Equals [!Ref AmbassadorEnvironment1Key, '']]
HasAmbassadorEnvironment2Key: !Not [!Equals [!Ref AmbassadorEnvironment2Key, '']]
HasAmbassadorEnvironment3Key: !Not [!Equals [!Ref AmbassadorEnvironment3Key, '']]
HasSidecarImage: !Not [!Equals [!Ref SidecarImage, '']]
HasSidecarCommand: !Not [!Equals [!Ref SidecarCommand, '']]
HasSidecarEnvironment1Key: !Not [!Equals [!Ref SidecarEnvironment1Key, '']]
HasSidecarEnvironment2Key: !Not [!Equals [!Ref SidecarEnvironment2Key, '']]
HasSidecarEnvironment3Key: !Not [!Equals [!Ref SidecarEnvironment3Key, '']]
Resources:
RecordSet:
Condition: HasZone
Type: 'AWS::Route53::RecordSet'
Properties:
AliasTarget:
HostedZoneId: !GetAtt LoadBalancer.CanonicalHostedZoneID
DNSName: !GetAtt 'LoadBalancer.DNSName'
HostedZoneId: {'Fn::ImportValue': !Sub '${ParentZoneStack}-HostedZoneId'}
Name: !Sub
- '${SubDomainNameWithDot}${HostedZoneName}'
- SubDomainNameWithDot: !Ref SubDomainNameWithDot
HostedZoneName: {'Fn::ImportValue': !Sub '${ParentZoneStack}-HostedZoneName'}
Type: A
LoadBalancerSecurityGroup:
Type: 'AWS::EC2::SecurityGroup'
Properties:
GroupDescription: !Sub '${AWS::StackName}-load-balancer'
VpcId: {'Fn::ImportValue': !Sub '${ParentVPCStack}-VPC'}
LoadBalancerSecurityGroupInHttpFromWorld:
Type: 'AWS::EC2::SecurityGroupIngress'
Condition: HasNotAuthProxySecurityGroup
Properties:
GroupId: !Ref LoadBalancerSecurityGroup
IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: '0.0.0.0/0'
LoadBalancerSecurityGroupInHttpsFromWorld:
Type: 'AWS::EC2::SecurityGroupIngress'
Condition: HasNotAuthProxySecurityGroupAndLoadBalancerCertificateArn
Properties:
GroupId: !Ref LoadBalancerSecurityGroup
IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: '0.0.0.0/0'
LoadBalancerSecurityGroupInHttpFromAuthProxy:
Type: 'AWS::EC2::SecurityGroupIngress'
Condition: HasAuthProxySecurityGroup
Properties:
GroupId: !Ref LoadBalancerSecurityGroup
IpProtocol: tcp
FromPort: 80
ToPort: 80
SourceSecurityGroupId: {'Fn::ImportValue': !Sub '${ParentAuthProxyStack}-SecurityGroup'}
LoadBalancerSecurityGroupInHttpsFromAuthProxy:
Type: 'AWS::EC2::SecurityGroupIngress'
Condition: HasAuthProxySecurityGroupAndLoadBalancerCertificateArn
Properties:
GroupId: !Ref LoadBalancerSecurityGroup
IpProtocol: tcp
FromPort: 443
ToPort: 443
SourceSecurityGroupId: {'Fn::ImportValue': !Sub '${ParentAuthProxyStack}-SecurityGroup'}
LoadBalancer:
Type: 'AWS::ElasticLoadBalancingV2::LoadBalancer'
Properties:
LoadBalancerAttributes:
- Key: 'idle_timeout.timeout_seconds'
Value: !Ref LoadBalancerIdleTimeout
- Key: 'routing.http2.enabled'
Value: 'true'
- Key: 'access_logs.s3.enabled'
Value: !If [HasS3Bucket, 'true', 'false']
- !If [HasS3Bucket, {Key: 'access_logs.s3.prefix', Value: !Ref 'AWS::StackName'}, !Ref 'AWS::NoValue']
- !If [HasS3Bucket, {Key: 'access_logs.s3.bucket', Value: {'Fn::ImportValue': !Sub '${ParentS3StackAccessLog}-BucketName'}}, !Ref 'AWS::NoValue']
Scheme: !Ref LoadBalancerScheme
SecurityGroups:
- !Ref LoadBalancerSecurityGroup
Subnets: !If
- HasLoadBalancerSchemeInternal
- !Split [',', {'Fn::ImportValue': !Sub '${ParentVPCStack}-SubnetsPrivate'}]
- !Split [',', {'Fn::ImportValue': !Sub '${ParentVPCStack}-SubnetsPublic'}]
Type: application
HTTPCodeELB5XXTooHighAlarm:
Condition: HasAlertTopic
Type: 'AWS::CloudWatch::Alarm'
Properties:
AlarmDescription: 'Application load balancer returns 5XX HTTP status codes'
Namespace: 'AWS/ApplicationELB'
MetricName: HTTPCode_ELB_5XX_Count
Statistic: Sum
Period: 60
EvaluationPeriods: 1
ComparisonOperator: GreaterThanThreshold
Threshold: 0
AlarmActions:
- {'Fn::ImportValue': !Sub '${ParentAlertStack}-TopicARN'}
Dimensions:
- Name: LoadBalancer
Value: !GetAtt 'LoadBalancer.LoadBalancerFullName'
RejectedConnectionCountTooHighAlarm:
Condition: HasAlertTopic
Type: 'AWS::CloudWatch::Alarm'
Properties:
AlarmDescription: 'Application load balancer rejected connections because the load balancer had reached its maximum number of connections'
Namespace: 'AWS/ApplicationELB'
MetricName: RejectedConnectionCount
Statistic: Sum
Period: 60
EvaluationPeriods: 1
ComparisonOperator: GreaterThanThreshold
Threshold: 0
AlarmActions:
- {'Fn::ImportValue': !Sub '${ParentAlertStack}-TopicARN'}
Dimensions:
- Name: LoadBalancer
Value: !GetAtt 'LoadBalancer.LoadBalancerFullName'
HttpListener:
Type: 'AWS::ElasticLoadBalancingV2::Listener'
Properties:
DefaultActions:
- !If
- HasLoadBalancerCertificateArn
- RedirectConfig:
Port: '443'
Protocol: HTTPS
StatusCode: 'HTTP_301'
Type: redirect
- TargetGroupArn: !Ref TargetGroup
Type: forward
LoadBalancerArn: !Ref LoadBalancer
Port: 80
Protocol: HTTP
HttpsListener:
Condition: HasLoadBalancerCertificateArn
Type: 'AWS::ElasticLoadBalancingV2::Listener'
Properties:
Certificates:
- CertificateArn: !Ref LoadBalancerCertificateArn
DefaultActions:
- TargetGroupArn: !Ref TargetGroup
Type: forward
LoadBalancerArn: !Ref LoadBalancer
Port: 443
Protocol: HTTPS
TargetGroup:
Type: 'AWS::ElasticLoadBalancingV2::TargetGroup'
Properties:
HealthCheckIntervalSeconds: 15
HealthCheckPath: '/'
HealthCheckProtocol: HTTP
HealthCheckTimeoutSeconds: 10
HealthyThresholdCount: 2
UnhealthyThresholdCount: 2
Matcher:
HttpCode: '200-299'
Port: 8080 # overriden when containers are attached
Protocol: HTTP
TargetType: ip
TargetGroupAttributes:
- Key: deregistration_delay.timeout_seconds
Value: !Ref LoadBalancerDeregistrationDelay
VpcId: {'Fn::ImportValue': !Sub '${ParentVPCStack}-VPC'}
HTTPCodeTarget5XXTooHighAlarm:
Condition: HasAlertTopic
Type: 'AWS::CloudWatch::Alarm'
Properties:
AlarmDescription: 'Application load balancer receives 5XX HTTP status codes from targets'
Namespace: 'AWS/ApplicationELB'
MetricName: HTTPCode_Target_5XX_Count
Statistic: Sum
Period: 60
EvaluationPeriods: 1
ComparisonOperator: GreaterThanThreshold
Threshold: 0
AlarmActions:
- {'Fn::ImportValue': !Sub '${ParentAlertStack}-TopicARN'}
Dimensions:
- Name: LoadBalancer
Value: !GetAtt 'LoadBalancer.LoadBalancerFullName'
- Name: TargetGroup
Value: !GetAtt TargetGroup.TargetGroupFullName
TargetConnectionErrorCountTooHighAlarm:
Condition: HasAlertTopic
Type: 'AWS::CloudWatch::Alarm'
Properties:
AlarmDescription: 'Application load balancer could not connect to targets'
Namespace: 'AWS/ApplicationELB'
MetricName: TargetConnectionErrorCount
Statistic: Sum
Period: 60
EvaluationPeriods: 1
ComparisonOperator: GreaterThanThreshold
Threshold: 0
AlarmActions:
- {'Fn::ImportValue': !Sub '${ParentAlertStack}-TopicARN'}
Dimensions:
- Name: LoadBalancer
Value: !GetAtt 'LoadBalancer.LoadBalancerFullName'
- Name: TargetGroup
Value: !GetAtt TargetGroup.TargetGroupFullName
TaskExecutionRole:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: 'ecs-tasks.amazonaws.com'
Action: 'sts:AssumeRole'
Policies:
- PolicyName: AmazonECSTaskExecutionRolePolicy # https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_execution_IAM_role.html
PolicyDocument:
Statement:
- Effect: Allow
Action:
- 'ecr:GetAuthorizationToken'
- 'ecr:BatchCheckLayerAvailability'
- 'ecr:GetDownloadUrlForLayer'
- 'ecr:BatchGetImage'
Resource: '*'
- Effect: Allow
Action:
- 'logs:CreateLogStream'
- 'logs:PutLogEvents'
Resource: !GetAtt 'LogGroup.Arn'
TaskRole:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: 'ecs-tasks.amazonaws.com'
Action: 'sts:AssumeRole'
ManagedPolicyArns: !If [HasTaskPolicies, !Split [',', !Ref TaskPolicies], !Ref 'AWS::NoValue']
TaskDefinition:
Type: 'AWS::ECS::TaskDefinition'
Properties:
ContainerDefinitions:
- !If
- HasAmbassadorImage
- Name: ambassador
Image: !Ref AmbassadorImage
Command: !If [HasAmbassadorCommand, !Ref AmbassadorCommand, !Ref 'AWS::NoValue']
PortMappings:
- ContainerPort: !Ref AmbassadorPort
Protocol: tcp
Essential: true
LogConfiguration:
LogDriver: awslogs
Options:
'awslogs-region': !Ref 'AWS::Region'
'awslogs-group': !Ref LogGroup
'awslogs-stream-prefix': ambassador
Environment:
- !If [HasAmbassadorEnvironment1Key, {Name: !Ref AmbassadorEnvironment1Key, Value: !Ref AmbassadorEnvironment1Value}, !Ref 'AWS::NoValue']
- !If [HasAmbassadorEnvironment2Key, {Name: !Ref AmbassadorEnvironment2Key, Value: !Ref AmbassadorEnvironment2Value}, !Ref 'AWS::NoValue']
- !If [HasAmbassadorEnvironment3Key, {Name: !Ref AmbassadorEnvironment3Key, Value: !Ref AmbassadorEnvironment3Value}, !Ref 'AWS::NoValue']
- !Ref 'AWS::NoValue'
- Name: app
Image: !Ref AppImage
Command: !If [HasAppCommand, !Split [',', !Ref AppCommand], !Ref 'AWS::NoValue']
PortMappings:
- ContainerPort: !Ref AppPort
Protocol: tcp
Essential: true
LogConfiguration:
LogDriver: awslogs
Options:
'awslogs-region': !Ref 'AWS::Region'
'awslogs-group': !Ref LogGroup
'awslogs-stream-prefix': app
Environment:
- !If [HasAppEnvironment1Key, {Name: !Ref AppEnvironment1Key, Value: !Ref AppEnvironment1Value}, !Ref 'AWS::NoValue']
- !If [HasAppEnvironment2Key, {Name: !Ref AppEnvironment2Key, Value: !Ref AppEnvironment2Value}, !Ref 'AWS::NoValue']
- !If [HasAppEnvironment3Key, {Name: !Ref AppEnvironment3Key, Value: !Ref AppEnvironment3Value}, !Ref 'AWS::NoValue']
- !If
- HasSidecarImage
- Name: sidecar
Image: !Ref SidecarImage
Command: !If [HasSidecarCommand, !Ref SidecarCommand, !Ref 'AWS::NoValue']
PortMappings:
- ContainerPort: !Ref SidecarPort
Protocol: tcp
Essential: true
LogConfiguration:
LogDriver: awslogs
Options:
'awslogs-region': !Ref 'AWS::Region'
'awslogs-group': !Ref LogGroup
'awslogs-stream-prefix': sidecar
Environment:
- !If [HasSidecarEnvironment1Key, {Name: !Ref SidecarEnvironment1Key, Value: !Ref SidecarEnvironment1Value}, !Ref 'AWS::NoValue']
- !If [HasSidecarEnvironment2Key, {Name: !Ref SidecarEnvironment2Key, Value: !Ref SidecarEnvironment2Value}, !Ref 'AWS::NoValue']
- !If [HasSidecarEnvironment3Key, {Name: !Ref SidecarEnvironment3Key, Value: !Ref SidecarEnvironment3Value}, !Ref 'AWS::NoValue']
- !Ref 'AWS::NoValue'
Cpu: !FindInMap [CpuMap, !Ref Cpu, Cpu]
ExecutionRoleArn: !GetAtt 'TaskExecutionRole.Arn'
Family: !Ref 'AWS::StackName'
Memory: !FindInMap [MemoryMap, !Ref Memory, Memory]
NetworkMode: awsvpc
RequiresCompatibilities: [FARGATE]
TaskRoleArn: !GetAtt 'TaskRole.Arn'
LogGroup:
Type: 'AWS::Logs::LogGroup'
Properties:
RetentionInDays: !Ref LogsRetentionInDays
ServiceSecurityGroup:
Type: 'AWS::EC2::SecurityGroup'
Properties:
GroupDescription: !Sub '${AWS::StackName}-service'
VpcId: {'Fn::ImportValue': !Sub '${ParentVPCStack}-VPC'}
SecurityGroupIngress:
- SourceSecurityGroupId: !Ref LoadBalancerSecurityGroup
FromPort: 0
ToPort: 65535
IpProtocol: tcp
Service:
Type: 'AWS::ECS::Service'
DependsOn: HttpListener
Properties:
Cluster: {'Fn::ImportValue': !Sub '${ParentClusterStack}-Cluster'}
DeploymentConfiguration:
MaximumPercent: 200
MinimumHealthyPercent: 100
DesiredCount: !Ref DesiredCount
HealthCheckGracePeriodSeconds: !Ref HealthCheckGracePeriod
LaunchType: FARGATE
LoadBalancers:
- ContainerName: !If [HasAmbassadorImage, ambassador, app]
ContainerPort: !If [HasAmbassadorImage, !Ref AmbassadorPort, !Ref AppPort]
TargetGroupArn: !Ref TargetGroup
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: !If [HasSubnetsReachPublic, ENABLED, DISABLED]
SecurityGroups:
- !Ref ServiceSecurityGroup
- !If [HasClientSecurityGroup1, {'Fn::ImportValue': !Sub '${ParentClientStack1}-ClientSecurityGroup'}, !Ref 'AWS::NoValue']
- !If [HasClientSecurityGroup2, {'Fn::ImportValue': !Sub '${ParentClientStack2}-ClientSecurityGroup'}, !Ref 'AWS::NoValue']
- !If [HasClientSecurityGroup3, {'Fn::ImportValue': !Sub '${ParentClientStack3}-ClientSecurityGroup'}, !Ref 'AWS::NoValue']
Subnets: !Split [',', {'Fn::ImportValue': !Sub '${ParentVPCStack}-Subnets${SubnetsReach}'}]
TaskDefinition: !Ref TaskDefinition
CPUUtilizationTooHighAlarm:
Condition: HasAlertTopic
Type: 'AWS::CloudWatch::Alarm'
Properties:
AlarmDescription: 'Average CPU utilization over last 10 minutes higher than 80%'
Namespace: 'AWS/ECS'
Dimensions:
- Name: ClusterName
Value: {'Fn::ImportValue': !Sub '${ParentClusterStack}-Cluster'}
- Name: ServiceName
Value: !GetAtt 'Service.Name'
MetricName: CPUUtilization
ComparisonOperator: GreaterThanThreshold
Statistic: Average
Period: 300
EvaluationPeriods: 1
Threshold: 80
AlarmActions:
- {'Fn::ImportValue': !Sub '${ParentAlertStack}-TopicARN'}
ScalableTargetRole: # based on http://docs.aws.amazon.com/AmazonECS/latest/developerguide/autoscale_IAM_role.html
Condition: HasAutoScaling
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: 'application-autoscaling.amazonaws.com'
Action: 'sts:AssumeRole'
Policies:
- PolicyName: AmazonEC2ContainerServiceAutoscaleRole
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- 'ecs:DescribeServices'
- 'ecs:UpdateService'
Resource: '*'
- PolicyName: cloudwatch
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- 'cloudwatch:DescribeAlarms'
Resource: '*'
ScalableTarget:
Condition: HasAutoScaling
Type: 'AWS::ApplicationAutoScaling::ScalableTarget'
Properties:
MaxCapacity: !Ref MaxCapacity
MinCapacity: !Ref MinCapacity
ResourceId: !Sub
- 'service/${Cluster}/${Service}'
- Cluster: {'Fn::ImportValue': !Sub '${ParentClusterStack}-Cluster'}
Service: !GetAtt 'Service.Name'
RoleARN: !GetAtt 'ScalableTargetRole.Arn'
ScalableDimension: 'ecs:service:DesiredCount'
ServiceNamespace: ecs
ScaleUpPolicy:
Condition: HasAutoScaling
Type: 'AWS::ApplicationAutoScaling::ScalingPolicy'
Properties:
PolicyName: !Sub '${AWS::StackName}-scale-up'
PolicyType: StepScaling
ScalingTargetId: !Ref ScalableTarget
StepScalingPolicyConfiguration:
AdjustmentType: PercentChangeInCapacity
Cooldown: 300
MinAdjustmentMagnitude: 1
StepAdjustments:
- MetricIntervalLowerBound: 0
ScalingAdjustment: 25
ScaleDownPolicy:
Condition: HasAutoScaling
Type: 'AWS::ApplicationAutoScaling::ScalingPolicy'
Properties:
PolicyName: !Sub '${AWS::StackName}-scale-down'
PolicyType: StepScaling
ScalingTargetId: !Ref ScalableTarget
StepScalingPolicyConfiguration:
AdjustmentType: PercentChangeInCapacity
Cooldown: 300
MinAdjustmentMagnitude: 1
StepAdjustments:
- MetricIntervalUpperBound: 0
ScalingAdjustment: -25
CPUUtilizationHighAlarm:
Condition: HasAutoScaling
Type: 'AWS::CloudWatch::Alarm'
Properties:
AlarmDescription: 'Service is running out of CPU'
Namespace: 'AWS/ECS'
Dimensions:
- Name: ClusterName
Value: {'Fn::ImportValue': !Sub '${ParentClusterStack}-Cluster'}
- Name: ServiceName
Value: !GetAtt 'Service.Name'
MetricName: CPUUtilization
ComparisonOperator: GreaterThanThreshold
Statistic: Average
Period: 300
EvaluationPeriods: 1
Threshold: 60
AlarmActions:
- !Ref ScaleUpPolicy
CPUUtilizationLowAlarm:
Condition: HasAutoScaling
Type: 'AWS::CloudWatch::Alarm'
Properties:
AlarmDescription: 'Service is wasting CPU'
Namespace: 'AWS/ECS'
Dimensions:
- Name: ClusterName
Value: {'Fn::ImportValue': !Sub '${ParentClusterStack}-Cluster'}
- Name: ServiceName
Value: !GetAtt 'Service.Name'
MetricName: CPUUtilization
ComparisonOperator: LessThanThreshold
Statistic: Average
Period: 300
EvaluationPeriods: 3
Threshold: 30
AlarmActions:
- !Ref ScaleDownPolicy
Outputs:
TemplateID:
Description: 'cloudonaut.io template id.'
Value: 'fargate/service-dedicated-alb'
TemplateVersion:
Description: 'cloudonaut.io template version.'
Value: '__VERSION__'
StackName:
Description: 'Stack name.'
Value: !Sub '${AWS::StackName}'
DNSName:
Description: 'The DNS name for the Fargate service load balancer.'
Value: !GetAtt 'LoadBalancer.DNSName'
Export:
Name: !Sub '${AWS::StackName}-DNSName'
URL:
Description: 'URL to the Fargate service.'
Value: !Sub 'http://${LoadBalancer.DNSName}'
Export:
Name: !Sub '${AWS::StackName}-URL'
You can’t perform that action at this time.