AWSTemplateFormatVersion: '2010-09-09' Description: Socket.io chat service Parameters: EnvironmentName: Type: String Default: production Description: A name for the environment that this cloudformation will be part of. Used to locate other resources in the same environment. ServiceName: Type: String Default: chat Description: A name for the service ImageUrl: Type: String Default: nginx Description: The url of a docker image that contains the application process that will handle the traffic for this service ContainerPort: Type: Number Default: 3000 Description: What port number the application inside the docker container is binding to ContainerCpu: Type: Number Default: 1024 Description: How much CPU to give the container. 1024 is 1 CPU ContainerMemory: Type: Number Default: 2048 Description: How much memory in megabytes to give the container Path: Type: String Default: "*" Description: A path on the public load balancer that this service should be connected to. Use * to send all load balancer traffic to this service. Priority: Type: Number Default: 1 Description: The priority for the routing rule added to the load balancer. This only applies if your have multiple services which have been assigned to different paths on the load balancer. DesiredCount: Type: Number Default: 2 Description: How many copies of the service task to run Role: Type: String Default: "" Description: (Optional) An IAM role to give the service's containers if the code within needs to access other AWS resources like S3 buckets, DynamoDB tables, etc Conditions: HasCustomRole: !Not [ !Equals [!Ref 'Role', ''] ] Resources: # A log group for storing the container logs for this service LogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: !Join ['-', [!Ref 'EnvironmentName', 'service', !Ref 'ServiceName']] # The task definition. This is a simple metadata description of what # container to run, and what resource requirements it has. TaskDefinition: Type: AWS::ECS::TaskDefinition Properties: Family: !Ref 'ServiceName' Cpu: !Ref 'ContainerCpu' Memory: !Ref 'ContainerMemory' NetworkMode: awsvpc RequiresCompatibilities: - FARGATE ExecutionRoleArn: Fn::ImportValue: !Join [':', [!Ref 'EnvironmentName', 'ECSTaskExecutionRole']] TaskRoleArn: Fn::If: - 'HasCustomRole' - !Ref 'Role' - !Ref "AWS::NoValue" ContainerDefinitions: - Name: !Ref 'ServiceName' Cpu: !Ref 'ContainerCpu' Memory: !Ref 'ContainerMemory' Image: !Ref 'ImageUrl' Environment: - Name: REDIS_ENDPOINT Value: Fn::ImportValue: !Join [':', [!Ref 'EnvironmentName', 'RedisEndpoint']] PortMappings: - ContainerPort: !Ref 'ContainerPort' LogConfiguration: LogDriver: 'awslogs' Options: awslogs-group: !Join ['-', [!Ref 'EnvironmentName', 'service', !Ref 'ServiceName']] awslogs-region: !Ref 'AWS::Region' awslogs-stream-prefix: !Ref 'ServiceName' # The service. The service is a resource which allows you to run multiple # copies of a type of task, and gather up their logs and metrics, as well # as monitor the number of running tasks and replace any that have crashed Service: Type: AWS::ECS::Service DependsOn: LoadBalancerRule Properties: ServiceName: !Ref 'ServiceName' Cluster: Fn::ImportValue: !Join [':', [!Ref 'EnvironmentName', 'ClusterName']] LaunchType: FARGATE DeploymentConfiguration: MaximumPercent: 200 MinimumHealthyPercent: 75 DesiredCount: !Ref 'DesiredCount' NetworkConfiguration: AwsvpcConfiguration: AssignPublicIp: ENABLED SecurityGroups: - Fn::ImportValue: !Join [':', [!Ref 'EnvironmentName', 'FargateContainerSecurityGroup']] Subnets: - Fn::ImportValue: !Join [':', [!Ref 'EnvironmentName', 'PublicSubnetOne']] - Fn::ImportValue: !Join [':', [!Ref 'EnvironmentName', 'PublicSubnetTwo']] TaskDefinition: !Ref 'TaskDefinition' LoadBalancers: - ContainerName: !Ref 'ServiceName' ContainerPort: !Ref 'ContainerPort' TargetGroupArn: !Ref 'TargetGroup' # A target group. This is used for keeping track of all the tasks, and # what IP addresses / port numbers they have. You can query it yourself, # to use the addresses yourself, but most often this target group is just # connected to an application load balancer, or network load balancer, so # it can automatically distribute traffic across all the targets. TargetGroup: Type: AWS::ElasticLoadBalancingV2::TargetGroup Properties: HealthCheckIntervalSeconds: 6 HealthCheckPath: / HealthCheckProtocol: HTTP HealthCheckTimeoutSeconds: 5 HealthyThresholdCount: 2 TargetType: ip Name: !Ref 'ServiceName' Port: 80 Protocol: HTTP UnhealthyThresholdCount: 2 TargetGroupAttributes: - Key: stickiness.enabled Value: true - Key: deregistration_delay.timeout_seconds Value: 30 VpcId: Fn::ImportValue: !Join [':', [!Ref 'EnvironmentName', 'VPCId']] # Create a rule on the load balancer for routing traffic to the target group LoadBalancerRule: Type: AWS::ElasticLoadBalancingV2::ListenerRule Properties: Actions: - TargetGroupArn: !Ref 'TargetGroup' Type: 'forward' Conditions: - Field: path-pattern Values: [!Ref 'Path'] ListenerArn: Fn::ImportValue: !Join [':', [!Ref 'EnvironmentName', 'PublicListener']] Priority: !Ref 'Priority' # Enable autoscaling for this service ScalableTarget: Type: AWS::ApplicationAutoScaling::ScalableTarget DependsOn: Service Properties: ServiceNamespace: 'ecs' ScalableDimension: 'ecs:service:DesiredCount' ResourceId: Fn::Join: - '/' - - service - Fn::ImportValue: !Join [':', [!Ref 'EnvironmentName', 'ClusterName']] - !Ref 'ServiceName' MinCapacity: 2 MaxCapacity: 10 RoleARN: Fn::ImportValue: !Join [':', [!Ref 'EnvironmentName', 'AutoscalingRole']] # Create scaling policies for the service ScaleDownPolicy: Type: AWS::ApplicationAutoScaling::ScalingPolicy DependsOn: ScalableTarget Properties: PolicyName: Fn::Join: - '/' - - scale - !Ref 'EnvironmentName' - !Ref 'ServiceName' - down PolicyType: StepScaling ResourceId: Fn::Join: - '/' - - service - Fn::ImportValue: !Join [':', [!Ref 'EnvironmentName', 'ClusterName']] - !Ref 'ServiceName' ScalableDimension: 'ecs:service:DesiredCount' ServiceNamespace: 'ecs' StepScalingPolicyConfiguration: AdjustmentType: 'ChangeInCapacity' StepAdjustments: - MetricIntervalUpperBound: 0 ScalingAdjustment: -1 MetricAggregationType: 'Average' Cooldown: 60 ScaleUpPolicy: Type: AWS::ApplicationAutoScaling::ScalingPolicy DependsOn: ScalableTarget Properties: PolicyName: Fn::Join: - '/' - - scale - !Ref 'EnvironmentName' - !Ref 'ServiceName' - up PolicyType: StepScaling ResourceId: Fn::Join: - '/' - - service - Fn::ImportValue: !Join [':', [!Ref 'EnvironmentName', 'ClusterName']] - !Ref 'ServiceName' ScalableDimension: 'ecs:service:DesiredCount' ServiceNamespace: 'ecs' StepScalingPolicyConfiguration: AdjustmentType: 'ChangeInCapacity' StepAdjustments: - MetricIntervalLowerBound: 0 MetricIntervalUpperBound: 15 ScalingAdjustment: 1 - MetricIntervalLowerBound: 15 MetricIntervalUpperBound: 25 ScalingAdjustment: 2 - MetricIntervalLowerBound: 25 ScalingAdjustment: 3 MetricAggregationType: 'Average' Cooldown: 60 # Create alarms to trigger these policies LowCpuUsageAlarm: Type: AWS::CloudWatch::Alarm Properties: AlarmName: Fn::Join: - '-' - - low-cpu - !Ref 'EnvironmentName' - !Ref 'ServiceName' AlarmDescription: Fn::Join: - ' ' - - "Low CPU utilization for service" - !Ref 'ServiceName' - "in stack" - !Ref 'EnvironmentName' MetricName: CPUUtilization Namespace: AWS/ECS Dimensions: - Name: ServiceName Value: !Ref 'ServiceName' - Name: ClusterName Value: Fn::ImportValue: !Join [':', [!Ref 'EnvironmentName', 'ClusterName']] Statistic: Average Period: 60 EvaluationPeriods: 1 Threshold: 20 ComparisonOperator: LessThanOrEqualToThreshold AlarmActions: - !Ref ScaleDownPolicy HighCpuUsageAlarm: Type: AWS::CloudWatch::Alarm Properties: AlarmName: Fn::Join: - '-' - - high-cpu - !Ref 'EnvironmentName' - !Ref 'ServiceName' AlarmDescription: Fn::Join: - ' ' - - "High CPU utilization for service" - !Ref 'ServiceName' - "in stack" - !Ref 'EnvironmentName' MetricName: CPUUtilization Namespace: AWS/ECS Dimensions: - Name: ServiceName Value: !Ref 'ServiceName' - Name: ClusterName Value: Fn::ImportValue: !Join [':', [!Ref 'EnvironmentName', 'ClusterName']] Statistic: Average Period: 60 EvaluationPeriods: 1 Threshold: 70 ComparisonOperator: GreaterThanOrEqualToThreshold AlarmActions: - !Ref ScaleUpPolicy