Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
761 lines (664 sloc) 19.4 KB
AWSTemplateFormatVersion: "2010-09-09"
Description: |
ECS Cluster resources.
##
##
Mappings:
VpcCidrs:
vpc:
cidr: 10.1.0.0/16
pubsubnet1:
cidr: 10.1.0.0/24
pubsubnet2:
cidr: 10.1.1.0/24
##
##
Parameters:
Version:
Type: String
Default: '0.2.0'
Description: |
The daemon version
EcsAmiId:
Type: String
Description: |
Required - ECS EC2 Linux AMI ID (http://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-optimized_AMI.html)
EcsInstanceType:
Type: String
Description: |
Required - ECS EC2 instance type
Default: t2.micro
SshKeyId:
Type: 'String'
Description: |
Optional - Name of an existing EC2 KeyPair to enable SSH access to the ECS
instances
Default: ''
VpcId:
Type: String
Description: |
Optional - VPC Id of existing VPC. Leave blank to have a new VPC created
Default: ''
AllowedPattern: '^(?:vpc-[0-9a-f]{8}|)$'
ConstraintDescription: VPC Id must begin with 'vpc-' or leave blank to have a new VPC created
SubnetIds:
Type: CommaDelimitedList
Description: |
Optional - Comma separated list of two (2) existing VPC Subnet Ids where
ECS instances will run. Required if setting VpcId.
Default: ''
AsgMaxSize:
Type: Number
Description: Maximum size of ECS Auto Scaling Group
Default: '1'
AsgDesiredSize:
Type: Number
Description: Initial Desired Capacity of ECS Auto Scaling Group
Default: '1'
SecurityGroup:
Type: String
Description: |
Optional - Existing security group to associate the container instances.
Creates one by default.
Default: ''
SourceCidr:
Type: String
Description: Optional - CIDR/IP range for EcsPort - defaults to 0.0.0.0/0
Default: 0.0.0.0/0
EcsPort:
Type: String
Description: |
Optional - Security Group port to open on ECS instances - defaults to port
80
Default: '80'
VpcAvailabilityZones:
Type: CommaDelimitedList
Description: >-
Optional - Comma-delimited list of VPC availability zones in which to
create subnets. Required if setting VpcId.
Default: ''
EcsCluster:
Type: String
Description: ECS Cluster Name
Default: default
HostedZoneId:
Type: String
Description: |
Required - Route53 Hosted Zone Identity to update service discovery
Default: ''
LoadBalancer:
Type: String
Description: |
Optional - Set a valid Load Balancer schema (internal, internet-facing) if cluster request
Load Balancing solutions
Default: ''
SSLCertificate:
Type: String
Description: |
Optional - Identify of SSL Certificate
Default: ''
IncidentChannel:
Type: String
Description: |
Optional - Channel for Incident communication
Default: ''
LocalFileSystem:
Type: String
Description: |
Optional - Specify the size of required volume to create Local File System.
Default: ''
EcsSpawnTasks:
Type: String
Description: |
Optional - Specify the list of task to spawn in the cluster at boot time
Default: ''
##
##
Conditions:
CreateVpcResources: !Equals
- !Ref VpcId
- ''
CreateSecurityGroup: !Equals
- !Ref SecurityGroup
- ''
UseSpecifiedVpcAvailabilityZones: !Not
- !Equals
- !Join
- ''
- !Ref VpcAvailabilityZones
- ''
HasSshKeyId: !Not
- !Equals
- !Ref SshKeyId
- ''
CreateLbResource: !Not
- !Equals
- !Ref LoadBalancer
- ''
CreateIncidentChannel: !Not
- !Equals
- !Ref IncidentChannel
- ''
CreateLocalFileSystem: !Not
- !Equals
- !Ref LocalFileSystem
- ''
##
##
Resources:
EcsEmptyCluster:
Type: "AWS::ECS::Cluster"
Properties:
ClusterName: !Ref EcsCluster
Vpc:
Condition: CreateVpcResources
Type: 'AWS::EC2::VPC'
Properties:
## DNS options are required to enable EFS
EnableDnsSupport: true
EnableDnsHostnames: true
CidrBlock: !FindInMap
- VpcCidrs
- vpc
- cidr
PubSubnetAz1:
Condition: CreateVpcResources
Type: 'AWS::EC2::Subnet'
Properties:
VpcId: !Ref Vpc
CidrBlock: !FindInMap
- VpcCidrs
- pubsubnet1
- cidr
AvailabilityZone: !If
- UseSpecifiedVpcAvailabilityZones
- !Select
- '0'
- !Ref VpcAvailabilityZones
- !Select
- '0'
- !GetAZs
Ref: 'AWS::Region'
PubSubnetAz2:
Condition: CreateVpcResources
Type: 'AWS::EC2::Subnet'
Properties:
VpcId: !Ref Vpc
CidrBlock: !FindInMap
- VpcCidrs
- pubsubnet2
- cidr
AvailabilityZone: !If
- UseSpecifiedVpcAvailabilityZones
- !Select
- '1'
- !Ref VpcAvailabilityZones
- !Select
- '1'
- !GetAZs
Ref: 'AWS::Region'
InternetGateway:
Condition: CreateVpcResources
Type: 'AWS::EC2::InternetGateway'
AttachGateway:
Condition: CreateVpcResources
Type: 'AWS::EC2::VPCGatewayAttachment'
Properties:
VpcId: !Ref Vpc
InternetGatewayId: !Ref InternetGateway
RouteViaIgw:
Condition: CreateVpcResources
Type: 'AWS::EC2::RouteTable'
Properties:
VpcId: !Ref Vpc
PublicRouteViaIgw:
Condition: CreateVpcResources
DependsOn: AttachGateway
Type: 'AWS::EC2::Route'
Properties:
RouteTableId: !Ref RouteViaIgw
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
PubSubnet1RouteTableAssociation:
Condition: CreateVpcResources
Type: 'AWS::EC2::SubnetRouteTableAssociation'
Properties:
SubnetId: !Ref PubSubnetAz1
RouteTableId: !Ref RouteViaIgw
PubSubnet2RouteTableAssociation:
Condition: CreateVpcResources
Type: 'AWS::EC2::SubnetRouteTableAssociation'
Properties:
SubnetId: !Ref PubSubnetAz2
RouteTableId: !Ref RouteViaIgw
EcsSecurityGroup:
Condition: CreateSecurityGroup
Type: 'AWS::EC2::SecurityGroup'
Properties:
GroupDescription: ECS Allowed Ports
VpcId: !If
- CreateVpcResources
- !Ref Vpc
- !Ref VpcId
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: !Ref SourceCidr
- !If
- CreateLbResource
- IpProtocol: tcp
FromPort: 0
ToPort: 65535
SourceSecurityGroupId: !Ref EcsLoadBalancerSg
- IpProtocol: tcp
FromPort: !Ref EcsPort
ToPort: !Ref EcsPort
CidrIp: !Ref SourceCidr
EcsInstancePolicy:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- ec2.amazonaws.com
Action:
- 'sts:AssumeRole'
Path: /
ManagedPolicyArns:
- "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role"
- "arn:aws:iam::aws:policy/AmazonRoute53FullAccess"
- "arn:aws:iam::aws:policy/CloudWatchLogsFullAccess"
- "arn:aws:iam::aws:policy/AmazonECS_FullAccess"
EcsInstanceProfile:
Type: 'AWS::IAM::InstanceProfile'
Properties:
Path: /
Roles:
- !Ref EcsInstancePolicy
EcsInstanceLc:
Type: 'AWS::AutoScaling::LaunchConfiguration'
Metadata:
Comment: |
Install a simple application,
AWS::CloudFormation::Init:
config:
## executed in the order: packages, groups, users, sources, files, commands, services
packages:
yum:
aws-cli: []
nfs-utils: []
jq: []
files:
/etc/cfn/cfn-hup.conf:
content: !Join
- ''
- - "[main]\n"
- !Sub "stack=${AWS::StackId}\n"
- !Sub "region=${AWS::Region}\n"
mode: "000400"
owner: root
group: root
/etc/cfn/hooks.d/cfn-auto-reloader.conf:
content: !Join
- ''
- - "[cfn-auto-reloader-hook]\n"
- "triggers=post.update\n"
- "path=Resources.EcsInstanceLc.Metadata.AWS::CloudFormation::Init\n"
- "action=/opt/aws/bin/cfn-init -v "
- !Sub " --stack ${AWS::StackName} "
- " --resource EcsInstanceLc "
- !Sub " --region ${AWS::Region}\n"
- "runas=root\n"
/usr/local/ecsd-latest+x86_64.Linux.bundle:
source: !Sub 'https://github.com/fogfish/ecsd/releases/download/${Version}/ecsd-${Version}.x86_64.Linux.bundle'
mode: "000755"
owner: root
group: root
/usr/local/boot/ecs-task:
content: !Join
- ''
- - "set -x\n"
- "until curl -s http://localhost:51678/v1/metadata\n"
- "do\n"
- " sleep 1\n"
- "done\n"
- "instance_arn=$(curl -s http://localhost:51678/v1/metadata | jq -r '. | .ContainerInstanceArn' | awk -F/ '{print $NF}' )\n"
- !Sub "test -n \"${EcsSpawnTasks}\" && for T in $(echo ${EcsSpawnTasks} | sed \"s/,/ /g\") ; do aws ecs start-task --cluster ${EcsCluster} --task-definition $T --container-instances $instance_arn --started-by $instance_arn --region ${AWS::Region} ; done || echo \"ok\"\n"
services:
sysvinit:
cfn-hup:
enable: true
ensureRunning: true
files:
- /etc/cfn/cfn-hup.conf
- /etc/cfn/hooks.d/cfn-auto-reloader.conf
commands:
## optimize kernel for container that uses mmap (e.g. elastic)
01sysctl:
command: "sysctl -w vm.max_map_count=262144"
## install ecsd supervisor
02ecsd-install:
command: sh /usr/local/ecsd-latest+x86_64.Linux.bundle
03ecsd-config:
command: !Sub |
mkdir -p /etc/ecsd &&
echo "[{ecsd, [{hosted_zone_id, \"${HostedZoneId}\"}]}]." >> /etc/ecsd/app.config
04ecsd-spawn:
command: /etc/init.d/ecsd start
Properties:
ImageId: !Ref EcsAmiId
InstanceType: !Ref EcsInstanceType
AssociatePublicIpAddress: true
IamInstanceProfile: !Ref EcsInstanceProfile
BlockDeviceMappings: !If
- CreateLocalFileSystem
- - DeviceName: "/dev/xvdk"
Ebs:
VolumeType: gp2
VolumeSize: !Ref LocalFileSystem
- AWS::NoValue
KeyName: !If
- HasSshKeyId
- !Ref SshKeyId
- !Ref AWS::NoValue
SecurityGroups: !If
- CreateSecurityGroup
- - !Ref EcsSecurityGroup
- - !Ref SecurityGroup
UserData: !Base64
'Fn::Join':
- ''
- - |
#!/bin/bash
## configure aws ecs agent
- echo ECS_CLUSTER=
- !Ref EcsCluster
- |2
>> /etc/ecs/ecs.config
- echo 'ECS_AVAILABLE_LOGGING_DRIVERS=["json-file","awslogs"]'
- |2
>> /etc/ecs/ecs.config
## install cloud init
- |
yum update -y
- |
yum install -y aws-cfn-bootstrap
- /opt/aws/bin/cfn-init -v
- " --stack="
- !Ref AWS::StackName
- " --resource=EcsInstanceLc"
- " --region="
- !Ref AWS::Region
- |2
- |
PATH=$PATH:/usr/local/bin
- |
AZ=`curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone`
- !Sub EFS=$AZ.${FileSystem}.efs.${AWS::Region}.amazonaws.com
- |2
- |
mkdir /mnt/efs
- |
mount -t nfs4 $EFS:/ /mnt/efs
- |
echo -e "$EFS:/ \t\t /mnt/efs \x09 nfs \x09 defaults \x09 0 \x09 0" | tee -a /etc/fstab
- |
test -b /dev/xvdk && mkdir /mnt/lfs
- |
test -b /dev/xvdk && mkfs.ext4 /dev/xvdk
- |
test -b /dev/xvdk && mount -t ext4 /dev/xvdk /mnt/lfs
- |
test -b /dev/xvdk && echo -e "/dev/xvdk \x09 /mnt/lfs \x09 ext4 \x09 defaults \x09 0 \x09 0" | tee -a /etc/fstab
- |
service docker stop
- |
service docker start
- !Sub /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource EcsInstanceAsg --region ${AWS::Region}
- |2
EcsInstanceAsg:
Type: 'AWS::AutoScaling::AutoScalingGroup'
Properties:
VPCZoneIdentifier: !If
- CreateVpcResources
- - !Join
- ','
- - !Ref PubSubnetAz1
- !Ref PubSubnetAz2
- !Ref SubnetIds
LaunchConfigurationName: !Ref EcsInstanceLc
MinSize: '0'
MaxSize: !Ref AsgMaxSize
DesiredCapacity: !Ref AsgDesiredSize
MetricsCollection:
- Granularity: 1Minute
Metrics:
- GroupInServiceInstances
Tags:
- Key: Name
Value: !Sub ${AWS::StackName} ecsd ${Version}
PropagateAtLaunch: 'true'
EcsCloudWatchLog:
Type: "AWS::Logs::LogGroup"
Properties:
LogGroupName: !Ref 'AWS::StackName'
RetentionInDays: 30
FileSystemSg:
Type: "AWS::EC2::SecurityGroup"
Properties:
GroupDescription: Elastic File System allowed ports
VpcId: !If
- CreateVpcResources
- !Ref Vpc
- !Ref VpcId
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 2049
ToPort: 2049
CidrIp: !Ref SourceCidr
FileSystem:
Type: "AWS::EFS::FileSystem"
Properties:
FileSystemTags:
- Key: "Name"
Value: !Ref EcsCluster
MountTarget1:
Type: "AWS::EFS::MountTarget"
Properties:
FileSystemId: !Ref FileSystem
SubnetId: !If
- CreateVpcResources
- !Ref PubSubnetAz1
- !Select [ 0, !Ref SubnetIds ]
SecurityGroups:
- !Ref FileSystemSg
MountTarget2:
Type: "AWS::EFS::MountTarget"
Properties:
FileSystemId: !Ref FileSystem
SubnetId: !If
- CreateVpcResources
- !Ref PubSubnetAz2
- !Select [ 1, !Ref SubnetIds ]
SecurityGroups:
- !Ref FileSystemSg
EcsLoadBalancer:
Condition: CreateLbResource
Type: "AWS::ElasticLoadBalancingV2::LoadBalancer"
Properties:
Name: !Sub ${EcsCluster}-lb
Type: application
Scheme: !Ref LoadBalancer
SecurityGroups:
- !Ref EcsLoadBalancerSg
Subnets:
- !If
- CreateVpcResources
- !Ref PubSubnetAz1
- !Select [ 0, !Ref SubnetIds ]
- !If
- CreateVpcResources
- !Ref PubSubnetAz2
- !Select [ 1, !Ref SubnetIds ]
EcsLoadBalancerSg:
Condition: CreateLbResource
Type: 'AWS::EC2::SecurityGroup'
Properties:
GroupDescription: ECS Allowed Ports
VpcId: !If
- CreateVpcResources
- !Ref Vpc
- !Ref VpcId
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
EcsLoadBalancerHttps:
Condition: CreateLbResource
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
DefaultActions:
- Type: forward
TargetGroupArn: !Ref EcsLoadBalancerDefaultTarget
LoadBalancerArn: !Ref EcsLoadBalancer
Port: 443
Protocol: HTTPS
Certificates:
- CertificateArn: !Ref SSLCertificate
SslPolicy: "ELBSecurityPolicy-TLS-1-2-2017-01"
EcsLoadBalancerHttp:
Condition: CreateLbResource
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
DefaultActions:
- Type: forward
TargetGroupArn: !Ref EcsLoadBalancerDefaultTarget
LoadBalancerArn: !Ref EcsLoadBalancer
Port: 80
Protocol: HTTP
EcsLoadBalancerDefaultTarget:
Condition: CreateLbResource
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
HealthCheckIntervalSeconds: 60
UnhealthyThresholdCount: 10
HealthCheckPath: /
Name: !Sub ${EcsCluster}-lb-def
Port: 80
Protocol: HTTP
VpcId: !If
- CreateVpcResources
- !Ref Vpc
- !Ref VpcId
##
##
EcsLoadBalancerManagementRole:
Condition: CreateLbResource
Type: AWS::IAM::Role
Properties:
RoleName: !Sub ${EcsCluster}-lb-role
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- ecs.amazonaws.com
Action:
- sts:AssumeRole
Path: "/"
Policies:
- PolicyName: !Sub ${EcsCluster}-lb-policy
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- "ec2:AuthorizeSecurityGroupIngress"
- "ec2:Describe*"
- "elasticloadbalancing:DeregisterInstancesFromLoadBalancer"
- "elasticloadbalancing:DeregisterTargets"
- "elasticloadbalancing:Describe*"
- "elasticloadbalancing:RegisterInstancesWithLoadBalancer"
- "elasticloadbalancing:RegisterTargets"
Resource:
- "*"
##
##
EcsIncidentChannel:
Condition: CreateIncidentChannel
Type: AWS::SNS::Topic
Properties:
DisplayName: !Sub ${EcsCluster}-incident-channel
TopicName: !Sub ${EcsCluster}-incident-channel
Subscription:
- Protocol: email
Endpoint: !Ref IncidentChannel
Outputs:
ClusterAsg:
Value: !Ref EcsInstanceAsg
Export:
Name: !Sub ${AWS::StackName}-asg
ClusterFs:
Value: !Ref FileSystem
Export:
Name: !Sub ${AWS::StackName}-fs
ClusterLb:
Value: !If
- CreateLbResource
- !Ref EcsLoadBalancer
- 'None'
Export:
Name: !Sub ${AWS::StackName}-lb
##
## Exports are used to a CloudWatch alarms with Incident Channel
## * AWS::CloudWatch::Alarm
ClusterIncidentChannel:
Value: !If
- CreateIncidentChannel
- !Ref EcsIncidentChannel
- 'None'
Export:
Name: !Sub ${AWS::StackName}-incident-channel
##
## Exports are used to bind a "backing" resources with cluster LB
## * AWS::ElasticLoadBalancingV2::TargetGroup
## * AWS::ElasticLoadBalancingV2::ListenerRule
VpcId:
Value: !If
- CreateVpcResources
- !Ref Vpc
- !Ref VpcId
Export:
Name: !Sub ${AWS::StackName}-VpcId
LbHttps:
Value: !If
- CreateLbResource
- !Ref EcsLoadBalancerHttps
- 'None'
Export:
Name: !Sub ${AWS::StackName}-lb-https
LbHttp:
Value: !If
- CreateLbResource
- !Ref EcsLoadBalancerHttp
- 'None'
Export:
Name: !Sub ${AWS::StackName}-lb-http
##
## The role facilitates a deployment and attachement of container to load balancer
## using ecs-cli comand line
LbManagementRole:
Value: !If
- CreateLbResource
- !Ref EcsLoadBalancerManagementRole
- 'None'
You can’t perform that action at this time.