Last active
November 15, 2018 01:20
-
-
Save RothAndrew/ca4cc2a6fa9d8ab7461c5e6a18fe3df1 to your computer and use it in GitHub Desktop.
AWS CloudFormation template for an AWS ECS cluster with RexRay EBS, EFS, and S3FS plugins. Shamelessly lifted from a very nice tutorial located here: https://aws.amazon.com/blogs/compute/amazon-ecs-and-docker-volume-drivers-amazon-ebs/
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"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" | |
}, | |
"DesiredCapacity": { | |
"Type": "Number", | |
"Default": "2", | |
"Description": "Number of instances to launch in your ECS cluster" | |
}, | |
"InstanceType": { | |
"Description": "The EC2 instance type", | |
"Type": "String", | |
"Default": "t2.medium", | |
"AllowedValues": [ | |
"t2.micro", | |
"t2.small", | |
"t2.medium", | |
"m3.medium", | |
"m3.large", | |
"m3.xlarge", | |
"m3.2xlarge", | |
"c3.large", | |
"c3.xlarge", | |
"c3.2xlarge", | |
"c3.4xlarge", | |
"c3.8xlarge", | |
"c4.large", | |
"c4.xlarge", | |
"c4.2xlarge", | |
"c4.4xlarge", | |
"c4.8xlarge", | |
"r3.large", | |
"r3.xlarge", | |
"r3.2xlarge", | |
"r3.4xlarge", | |
"r3.8xlarge", | |
"i2.xlarge", | |
"i2.2xlarge", | |
"i2.4xlarge", | |
"i2.8xlarge", | |
"d2.xlarge", | |
"d2.2xlarge", | |
"d2.4xlarge", | |
"d2.8xlarge", | |
"hi1.4xlarge", | |
"hs1.8xlarge", | |
"cr1.8xlarge", | |
"cc2.8xlarge" | |
], | |
"ConstraintDescription": "must be a valid EC2 instance type." | |
}, | |
"SSHLocation": { | |
"Description": " The IP address range that can be used to SSH to the EC2 instances", | |
"Type": "String", | |
"MinLength": "9", | |
"MaxLength": "18", | |
"Default": "0.0.0.0/0", | |
"AllowedPattern": "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})", | |
"ConstraintDescription": "must be a valid IP CIDR range of the form x.x.x.x/x." | |
}, | |
"CIDRVPC": { | |
"Description": "Enter the CIDR Range for your VPC", | |
"Type": "String", | |
"MinLength": "9", | |
"MaxLength": "18", | |
"Default": "192.168.0.0/16", | |
"AllowedPattern": "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})", | |
"ConstraintDescription": "must be a valid IP CIDR range of the form x.x.x.x/x." | |
}, | |
"CIDRSubnet1": { | |
"Description": "Enter the CIDR Range for your VPC", | |
"Type": "String", | |
"MinLength": "9", | |
"MaxLength": "18", | |
"Default": "192.168.1.0/24", | |
"AllowedPattern": "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})", | |
"ConstraintDescription": "must be a valid IP CIDR range of the form x.x.x.x/x." | |
}, | |
"ECSAMI": { | |
"Description": "AMI ID", | |
"Type": "AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>", | |
"Default": "/aws/service/ecs/optimized-ami/amazon-linux/recommended/image_id" | |
} | |
}, | |
"Resources": { | |
"VPC": { | |
"Type": "AWS::EC2::VPC", | |
"Properties": { | |
"CidrBlock": { | |
"Ref": "CIDRVPC" | |
}, | |
"EnableDnsSupport": true, | |
"EnableDnsHostnames": true, | |
"Tags": [ | |
{ | |
"Key": "Name", | |
"Value": "Volume-Driver VPC" | |
} | |
] | |
} | |
}, | |
"Subnet1": { | |
"Type": "AWS::EC2::Subnet", | |
"Properties": { | |
"VpcId": { | |
"Ref": "VPC" | |
}, | |
"MapPublicIpOnLaunch": true, | |
"CidrBlock": { | |
"Ref": "CIDRSubnet1" | |
}, | |
"AvailabilityZone": { | |
"Fn::Select": [ | |
"0", | |
{ | |
"Fn::GetAZs": { | |
"Ref": "AWS::Region" | |
} | |
} | |
] | |
} | |
} | |
}, | |
"InternetGateway": { | |
"Type": "AWS::EC2::InternetGateway", | |
"Properties": { | |
"Tags": [ | |
{ | |
"Key": "Application", | |
"Value": { | |
"Ref": "AWS::StackName" | |
} | |
}, | |
{ | |
"Key": "Network", | |
"Value": "Public" | |
} | |
] | |
} | |
}, | |
"GatewayToInternet": { | |
"Type": "AWS::EC2::VPCGatewayAttachment", | |
"Properties": { | |
"VpcId": { | |
"Ref": "VPC" | |
}, | |
"InternetGatewayId": { | |
"Ref": "InternetGateway" | |
} | |
} | |
}, | |
"PublicRouteTable": { | |
"DependsOn": [ | |
"VPC" | |
], | |
"Type": "AWS::EC2::RouteTable", | |
"Properties": { | |
"VpcId": { | |
"Ref": "VPC" | |
}, | |
"Tags": [ | |
{ | |
"Key": "Application", | |
"Value": { | |
"Ref": "AWS::StackName" | |
} | |
}, | |
{ | |
"Key": "Network", | |
"Value": "Public" | |
} | |
] | |
} | |
}, | |
"PublicRoute": { | |
"DependsOn": [ | |
"PublicRouteTable", | |
"InternetGateway" | |
], | |
"Type": "AWS::EC2::Route", | |
"Properties": { | |
"RouteTableId": { | |
"Ref": "PublicRouteTable" | |
}, | |
"DestinationCidrBlock": "0.0.0.0/0", | |
"GatewayId": { | |
"Ref": "InternetGateway" | |
} | |
} | |
}, | |
"PublicSubnetRouteTableAssociation": { | |
"DependsOn": [ | |
"Subnet1", | |
"PublicRouteTable" | |
], | |
"Type": "AWS::EC2::SubnetRouteTableAssociation", | |
"Properties": { | |
"SubnetId": { | |
"Ref": "Subnet1" | |
}, | |
"RouteTableId": { | |
"Ref": "PublicRouteTable" | |
} | |
} | |
}, | |
"InstanceSecurityGroup": { | |
"Type": "AWS::EC2::SecurityGroup", | |
"Properties": { | |
"VpcId": { | |
"Ref": "VPC" | |
}, | |
"GroupDescription": "Enable SSH access via port 22", | |
"SecurityGroupIngress": [ | |
{ | |
"IpProtocol": "tcp", | |
"FromPort": "22", | |
"ToPort": "22", | |
"CidrIp": { | |
"Ref": "SSHLocation" | |
} | |
}, | |
{ | |
"IpProtocol": "tcp", | |
"FromPort": "3306", | |
"ToPort": "3306", | |
"CidrIp": { | |
"Ref": "SSHLocation" | |
} | |
} | |
] | |
} | |
}, | |
"NetworkLoadBalancer": { | |
"Type": "AWS::ElasticLoadBalancingV2::LoadBalancer", | |
"Properties": { | |
"Scheme": "internet-facing", | |
"Subnets": [ | |
{ | |
"Ref": "Subnet1" | |
} | |
], | |
"Type": "network" | |
} | |
}, | |
"NLBListener": { | |
"Type": "AWS::ElasticLoadBalancingV2::Listener", | |
"Properties": { | |
"DefaultActions": [ | |
{ | |
"Type": "forward", | |
"TargetGroupArn": { | |
"Ref": "MySQLTargetGroup" | |
} | |
} | |
], | |
"LoadBalancerArn": { | |
"Ref": "NetworkLoadBalancer" | |
}, | |
"Port": 3306, | |
"Protocol": "TCP" | |
} | |
}, | |
"MySQLTargetGroup": { | |
"Type": "AWS::ElasticLoadBalancingV2::TargetGroup", | |
"Properties": { | |
"Port": 3306, | |
"Protocol": "TCP", | |
"TargetType": "ip", | |
"VpcId": { | |
"Ref": "VPC" | |
} | |
} | |
}, | |
"ECSCluster": { | |
"Type": "AWS::ECS::Cluster", | |
"Properties": { | |
"ClusterName": "rexray-demo" | |
} | |
}, | |
"ECSAutoScalingGroup": { | |
"Type": "AWS::AutoScaling::AutoScalingGroup", | |
"Properties": { | |
"AvailabilityZones": [ | |
{ | |
"Fn::Select": [ | |
"0", | |
{ | |
"Fn::GetAZs": { | |
"Ref": "AWS::Region" | |
} | |
} | |
] | |
} | |
], | |
"VPCZoneIdentifier": [ | |
{ | |
"Ref": "Subnet1" | |
} | |
], | |
"LaunchConfigurationName": { | |
"Ref": "ContainerInstances" | |
}, | |
"MinSize": "2", | |
"MaxSize": "2", | |
"DesiredCapacity": "2", | |
"Tags": [ | |
{ | |
"Key": "Name", | |
"Value": "ECS host", | |
"PropagateAtLaunch": "true" | |
} | |
] | |
}, | |
"CreationPolicy": { | |
"ResourceSignal": { | |
"Timeout": "PT15M" | |
} | |
}, | |
"UpdatePolicy": { | |
"AutoScalingRollingUpdate": { | |
"MinInstancesInService": "1", | |
"MaxBatchSize": "1", | |
"PauseTime": "PT15M", | |
"WaitOnResourceSignals": "true", | |
"SuspendProcesses": [ | |
"HealthCheck", | |
"ReplaceUnhealthy", | |
"AZRebalance", | |
"AlarmNotification", | |
"ScheduledActions" | |
] | |
} | |
} | |
}, | |
"ContainerInstances": { | |
"Type": "AWS::AutoScaling::LaunchConfiguration", | |
"Properties": { | |
"ImageId": { | |
"Ref": "ECSAMI" | |
}, | |
"InstanceType": { | |
"Ref": "InstanceType" | |
}, | |
"IamInstanceProfile": { | |
"Ref": "EC2InstanceProfile" | |
}, | |
"KeyName": { | |
"Ref": "KeyName" | |
}, | |
"AssociatePublicIpAddress": true, | |
"SecurityGroups": [ | |
{ | |
"Ref": "InstanceSecurityGroup" | |
} | |
], | |
"UserData": { | |
"Fn::Base64": { | |
"Fn::Sub": "#!/bin/bash\nyum install -y aws-cfn-bootstrap s3fs-fuse\n/opt/aws/bin/cfn-init -v --region ${AWS::Region} --stack ${AWS::StackName} --resource ContainerInstances\n/opt/aws/bin/cfn-signal -e $? --region ${AWS::Region} --stack ${AWS::StackName} --resource ECSAutoScalingGroup\n\nexec 2>>/var/log/ecs/ecs-agent-install.log\nset -x\nuntil curl -s http://localhost:51678/v1/metadata\ndo\n sleep 1\ndone\nsudo sed -i 's/enabled=0/enabled=1/' /etc/yum.repos.d/epel.repo\nsudo yum install -y gcc libstdc++-devel gcc-c++ fuse fuse-devel curl-devel libxml2-devel mailcap automake openssl-devel git\ngit clone https://github.com/s3fs-fuse/s3fs-fuse\ncd s3fs-fuse/\n./autogen.sh\n./configure --prefix=/usr --with-openssl\nmake\nsudo make install\ncd ..\ndocker plugin install rexray/ebs REXRAY_PREEMPT=true EBS_REGION=${AWS::Region} --grant-all-permissions\ndocker plugin install rexray/efs REXRAY_PREEMPT=true EFS_REGION=${AWS::Region} EFS_TAG=rexray --grant-all-permissions\ndocker plugin install rexray/s3fs S3FS_REGION=${AWS::Region} --grant-all-permissions\nstop ecs \nstart ecs\n" | |
} | |
} | |
}, | |
"Metadata": { | |
"AWS::CloudFormation::Init": { | |
"config": { | |
"packages": { | |
"yum": { | |
"aws-cli": [], | |
"jq": [], | |
"ecs-init": [] | |
} | |
}, | |
"commands": { | |
"01_add_instance_to_cluster": { | |
"command": { | |
"Fn::Sub": "echo ECS_CLUSTER=${ECSCluster} >> /etc/ecs/ecs.config" | |
} | |
}, | |
"02_start_ecs_agent": { | |
"command": "start ecs" | |
} | |
}, | |
"files": { | |
"/etc/cfn/cfn-hup.conf": { | |
"mode": 256, | |
"owner": "root", | |
"group": "root", | |
"content": { | |
"Fn::Sub": "[main]\nstack=${AWS::StackId}\nregion=${AWS::Region}\n" | |
} | |
}, | |
"/etc/cfn/hooks.d/cfn-auto-reloader.conf": { | |
"content": { | |
"Fn::Sub": "[cfn-auto-reloader-hook]\ntriggers=post.update\npath=Resources.ContainerInstances.Metadata.AWS::CloudFormation::Init\naction=/opt/aws/bin/cfn-init -v --region ${AWS::Region} --stack ${AWS::StackName} --resource ContainerInstances\n" | |
} | |
} | |
}, | |
"services": { | |
"sysvinit": { | |
"cfn-hup": { | |
"enabled": "true", | |
"ensureRunning": "true", | |
"files": [ | |
"/etc/cfn/cfn-hup.conf", | |
"/etc/cfn/hooks.d/cfn-auto-reloader.conf" | |
] | |
} | |
} | |
} | |
} | |
} | |
} | |
}, | |
"EC2Role": { | |
"Type": "AWS::IAM::Role", | |
"Properties": { | |
"Path": "/", | |
"AssumeRolePolicyDocument": "{\n \"Statement\": [{\n \"Action\": \"sts:AssumeRole\",\n \"Effect\": \"Allow\",\n \"Principal\": {\n \"Service\": \"ec2.amazonaws.com\"\n }\n }]\n}\n", | |
"Policies": [ | |
{ | |
"PolicyName": "ECSforEC2InstanceRolePolicy", | |
"PolicyDocument": "{\n \"Version\": \"2012-10-17\",\n \"Statement\": [\n {\n \"Effect\": \"Allow\",\n \"Action\": [\n \"ecs:CreateCluster\",\n \"ecs:DeregisterContainerInstance\",\n \"ecs:DiscoverPollEndpoint\",\n \"ecs:Poll\",\n \"ecs:RegisterContainerInstance\",\n \"ecs:StartTelemetrySession\",\n \"ecs:Submit*\",\n \"ecr:GetAuthorizationToken\",\n \"ecr:BatchCheckLayerAvailability\",\n \"ecr:GetDownloadUrlForLayer\",\n \"ecr:BatchGetImage\",\n \"logs:CreateLogStream\",\n \"logs:PutLogEvents\"\n ],\n \"Resource\": \"*\"\n }\n ]\n}\n" | |
}, | |
{ | |
"PolicyName": "RexrayPolicy", | |
"PolicyDocument": "{\n \"Version\": \"2012-10-17\",\n \"Statement\": [{\n \"Effect\": \"Allow\",\n \"Action\": [\n \"ec2:AttachVolume\",\n \"ec2:CreateVolume\",\n \"ec2:CreateSnapshot\",\n \"ec2:CreateTags\",\n \"ec2:DeleteVolume\",\n \"ec2:DeleteSnapshot\",\n \"ec2:DescribeAvailabilityZones\",\n \"ec2:DescribeInstances\",\n \"ec2:DescribeVolumes\",\n \"ec2:DescribeVolumeAttribute\",\n \"ec2:DescribeVolumeStatus\",\n \"ec2:DescribeSnapshots\",\n \"ec2:CopySnapshot\",\n \"ec2:DescribeSnapshotAttribute\",\n \"ec2:DetachVolume\",\n \"ec2:ModifySnapshotAttribute\",\n \"ec2:ModifyVolumeAttribute\",\n \"ec2:DescribeTags\",\n \"elasticfilesystem:CreateFileSystem\",\n \"elasticfilesystem:CreateMountTarget\",\n \"ec2:DescribeSubnets\",\n \"ec2:DescribeNetworkInterfaces\",\n \"ec2:CreateNetworkInterface\",\n \"elasticfilesystem:CreateTags\",\n \"elasticfilesystem:DeleteFileSystem\",\n \"elasticfilesystem:DeleteMountTarget\",\n \"ec2:DeleteNetworkInterface\",\n \"elasticfilesystem:DescribeFileSystems\",\n \"elasticfilesystem:DescribeMountTargets\",\n \"s3:*\"\n ],\n \"Resource\": \"*\"\n }]\n}\n" | |
} | |
] | |
} | |
}, | |
"ECSServiceAutoScalingRole": { | |
"Type": "AWS::IAM::Role", | |
"Properties": { | |
"AssumeRolePolicyDocument": { | |
"Version": "2012-10-17", | |
"Statement": { | |
"Action": [ | |
"sts:AssumeRole" | |
], | |
"Effect": "Allow", | |
"Principal": { | |
"Service": [ | |
"application-autoscaling.amazonaws.com" | |
] | |
} | |
} | |
}, | |
"Path": "/", | |
"Policies": [ | |
{ | |
"PolicyName": "ecs-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" | |
} | |
] | |
} | |
}, | |
"CWLogsGroup": { | |
"Type": "AWS::Logs::LogGroup" | |
} | |
}, | |
"Outputs": { | |
"AWSRegion": { | |
"Description": "The name of the region where the stack was launched", | |
"Value": { | |
"Ref": "AWS::Region" | |
} | |
}, | |
"AvailabilityZone": { | |
"Description": "The AZ where the instances are deployed", | |
"Value": { | |
"Fn::GetAtt": [ | |
"Subnet1", | |
"AvailabilityZone" | |
] | |
} | |
}, | |
"NLBFullyQualifiedName": { | |
"Description": "The fully qualified name of the NLB", | |
"Value": { | |
"Fn::GetAtt": [ | |
"NetworkLoadBalancer", | |
"DNSName" | |
] | |
} | |
}, | |
"NLBName": { | |
"Description": "The name of the NLB", | |
"Value": { | |
"Fn::GetAtt": [ | |
"NetworkLoadBalancer", | |
"LoadBalancerName" | |
] | |
} | |
}, | |
"ECSClusterName": { | |
"Description": "The name of the ECS cluster", | |
"Value": { | |
"Ref": "ECSCluster" | |
} | |
}, | |
"CWLogGroupName": { | |
"Description": "The name of the CWLogs group", | |
"Value": { | |
"Ref": "CWLogsGroup" | |
} | |
}, | |
"SubnetId": { | |
"Description": "The ID of the subnet that the instances are associated with", | |
"Value": { | |
"Ref": "Subnet1" | |
} | |
}, | |
"SecurityGroupId": { | |
"Description": "The ID of security group that the instances are members of", | |
"Value": { | |
"Ref": "InstanceSecurityGroup" | |
} | |
}, | |
"MySQLTargetGroupArn": { | |
"Description": "The Arn of the MySQL target group", | |
"Value": { | |
"Ref": "MySQLTargetGroup" | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment