AWSTemplateFormatVersion: '2010-09-09' Description: ECS cluster that has EC2 Spot capacity to host the containers Parameters: DesiredCapacity: Type: Number Default: 0 Description: Number of EC2 instances to launch in your ECS cluster. MaxSize: Type: Number Default: 100 Description: Maximum number of EC2 instances that can be launched in your ECS cluster. ECSAMI: Description: AMI ID Type: AWS::SSM::Parameter::Value Default: /aws/service/ecs/optimized-ami/amazon-linux-2/recommended/image_id Description: The Amazon Machine Image ID used for the cluster, leave it as the default value to get the latest AMI VpcId: Type: AWS::EC2::VPC::Id Description: VPC ID where the ECS cluster is launched SubnetIds: Type: List Description: List of subnet IDs where the EC2 instances will be launched Resources: # This is authorizes ECS to manage resources on your # account on your behalf. This role is likely already created on your account # ECSRole: # Type: AWS::IAM::ServiceLinkedRole # Properties: # AWSServiceName: 'ecs.amazonaws.com' # ECS Resources ECSCluster: Type: AWS::ECS::Cluster Properties: ClusterSettings: - Name: containerInsights Value: enabled # The config for each EC2 instance that is added to the cluster ContainerInstances: Type: AWS::EC2::LaunchTemplate Properties: LaunchTemplateData: ImageId: !Ref ECSAMI IamInstanceProfile: Name: !Ref EC2InstanceProfile SecurityGroupIds: - !Ref ContainerHostSecurityGroup UserData: # This injected configuration file is how the EC2 instance # knows which ECS cluster on your AWS account it should be joining Fn::Base64: !Sub | #!/bin/bash echo "ECS_CLUSTER=${ECSCluster}" >> /etc/ecs/ecs.config echo "ECS_ENABLE_SPOT_INSTANCE_DRAINING=true" >> /etc/ecs/ecs.config echo "ECS_CONTAINER_STOP_TIMEOUT=90s" >> /etc/ecs/ecs.config BlockDeviceMappings: - DeviceName: "/dev/xvda" Ebs: VolumeSize: 50 VolumeType: gp3 EC2InstanceProfile: Type: AWS::IAM::InstanceProfile Properties: Path: / Roles: - !Ref EC2Role # Autoscaling group. This launches the actual EC2 instances that will register # themselves as members of the cluster, and run the docker containers. ECSAutoScalingGroup: Type: AWS::AutoScaling::AutoScalingGroup UpdatePolicy: AutoScalingReplacingUpdate: WillReplace: 'true' Properties: VPCZoneIdentifier: - !Select [ 0, !Ref SubnetIds ] - !Select [ 1, !Ref SubnetIds ] MinSize: 0 MaxSize: !Ref MaxSize DesiredCapacity: !Ref DesiredCapacity NewInstancesProtectedFromScaleIn: true # This policy sets up rules which allow the ASG to launch # a variety of mixed spot instance types MixedInstancesPolicy: # Request no on demand, only spot instances InstancesDistribution: OnDemandBaseCapacity: 0 OnDemandPercentageAboveBaseCapacity: 0 SpotAllocationStrategy: capacity-optimized # Rules about what type of instances to launch LaunchTemplate: LaunchTemplateSpecification: LaunchTemplateId: !Ref ContainerInstances Version: !GetAtt ContainerInstances.LatestVersionNumber Overrides: - InstanceRequirements: VCpuCount: Min: 2 Max: 4 MemoryMiB: Min: 4096 Max: 8192 BurstablePerformance: "excluded" InstanceGenerations: - "current" CpuManufacturers: - "intel" - "amd" ExcludedInstanceTypes: ["t2*","r*","d*","g*","i*","z*","x*"] # Create an ECS capacity provider to attach the ASG to the ECS cluster # so that it autoscales as we launch more containers CapacityProvider: Type: AWS::ECS::CapacityProvider Properties: AutoScalingGroupProvider: AutoScalingGroupArn: !Ref ECSAutoScalingGroup ManagedScaling: InstanceWarmupPeriod: 60 MinimumScalingStepSize: 1 MaximumScalingStepSize: 100 Status: ENABLED # Percentage of cluster reservation to try to maintain TargetCapacity: 100 ManagedTerminationProtection: ENABLED # Create a cluster capacity provider assocation so that the cluster # will use the capacity provider CapacityProviderAssociation: Type: AWS::ECS::ClusterCapacityProviderAssociations Properties: CapacityProviders: - !Ref CapacityProvider Cluster: !Ref ECSCluster DefaultCapacityProviderStrategy: - Base: 0 CapacityProvider: !Ref CapacityProvider Weight: 1 # A security group for the EC2 hosts that will run the containers. # This can be used to limit incoming traffic to or outgoing traffic # from the container's host EC2 instance. ContainerHostSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Access to the EC2 hosts that run containers VpcId: !Ref VpcId # Role for the EC2 hosts. This allows the ECS agent on the EC2 hosts # to communciate with the ECS control plane, as well as download the docker # images from ECR to run on your host. EC2Role: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Statement: - Effect: Allow Principal: Service: [ec2.amazonaws.com] Action: ['sts:AssumeRole'] Path: / # See reference: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/security-iam-awsmanpol.html#security-iam-awsmanpol-AmazonEC2ContainerServiceforEC2Role ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role # This is a role which is used within Fargate to allow the Fargate agent # to download images, and upload logs. ECSTaskExecutionRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Statement: - Effect: Allow Principal: Service: [ecs-tasks.amazonaws.com] Action: ['sts:AssumeRole'] Path: / # This role enables all features of ECS. See reference: # https://docs.aws.amazon.com/AmazonECS/latest/developerguide/security-iam-awsmanpol.html#security-iam-awsmanpol-AmazonECSTaskExecutionRolePolicy ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy Outputs: ClusterName: Description: The ECS cluster into which to launch resources Value: !Ref ECSCluster ECSTaskExecutionRole: Description: The role used to start up a task Value: !Ref ECSTaskExecutionRole CapacityProvider: Description: The cluster capacity provider that the service should use to request capacity when it wants to start up a task Value: !Ref CapacityProvider