Last active
February 28, 2024 12:00
-
-
Save TheDeveloper/242ebf1bfb11b8b6f9a9b0f454897ac7 to your computer and use it in GitHub Desktop.
Stack to create EC2 instances for ECS cluster.
This file contains 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
# Stack to create EC2 instances for ECS cluster. | |
# | |
# aws cloudformation deploy \ | |
# --stack-name app-cluster-prod \ | |
# --template-file ./aws-cluster-stack.yaml \ | |
# --parameter-overrides \ | |
# KeyName=DEFAULT \ | |
# SecurityGroups=group1,group2 \ | |
# ImageId=ami-123456 \ | |
# InstanceType=c5.large \ | |
# Subnets=subnet-1234,subnet-5678 \ | |
# EcsClusterName=myapp-prod \ | |
# --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM \ | |
# --no-execute-changeset | |
AWSTemplateFormatVersion: '2010-09-09' | |
Transform: 'AWS::Serverless-2016-10-31' | |
Description: EC2 instances for ECS cluster. | |
Parameters: | |
KeyName: | |
Description: The EC2 Key Pair to allow SSH access to the instance | |
Type: AWS::EC2::KeyPair::KeyName | |
SecurityGroups: | |
Description: Security group ids to use for the instances. | |
Type: CommaDelimitedList | |
ImageId: | |
Description: ECS-optimised AMI ID for your region. http://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-optimized_AMI.html | |
Type: String | |
InstanceType: | |
Description: EC2 instance type. | |
Type: String | |
Subnets: | |
Description: Subnet ids for instance placement. | |
Type: CommaDelimitedList | |
EcsClusterName: | |
Description: Name of the ECS cluster. | |
Type: String | |
Resources: | |
## EC2 | |
InstanceRole: | |
Type: AWS::IAM::Role | |
Properties: | |
Path: / | |
ManagedPolicyArns: | |
- arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role | |
- arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM | |
AssumeRolePolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Effect: Allow | |
Action: | |
- sts:AssumeRole | |
Principal: | |
Service: ec2.amazonaws.com | |
Policies: | |
- PolicyName: logs | |
PolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Effect: Allow | |
Action: | |
- logs:* | |
Resource: | |
- !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:* | |
InstanceProfile: | |
Type: AWS::IAM::InstanceProfile | |
Properties: | |
Path: / | |
Roles: | |
- !Ref InstanceRole | |
AutoscalingLaunchConfig: | |
Type: AWS::AutoScaling::LaunchConfiguration | |
Properties: | |
ImageId: !Ref ImageId | |
InstanceType: !Ref InstanceType | |
KeyName: !Ref KeyName | |
SecurityGroups: !Ref SecurityGroups | |
IamInstanceProfile: !Ref InstanceProfile | |
UserData: | |
!Base64: | |
Fn::Sub: | | |
- Content-Type: multipart/mixed; boundary="==BOUNDARY==" | |
MIME-Version: 1.0 | |
--==BOUNDARY== | |
MIME-Version: 1.0 | |
Content-Type: text/x-shellscript; charset="us-ascii" | |
#!/bin/bash | |
echo ECS_CLUSTER=${ClusterName} >> /etc/ecs/ecs.config | |
STACK_NAME=${AWS::StackName} | |
# Install awslogs and the jq JSON parser | |
yum install -y awslogs jq https://s3-${AWS::Region}.amazonaws.com/amazon-ssm-${AWS::Region}/latest/linux_amd64/amazon-ssm-agent.rpm | |
# Inject the CloudWatch Logs configuration file contents | |
cat > /etc/awslogs/awslogs.conf <<- EOF | |
[general] | |
state_file = /var/lib/awslogs/agent-state | |
[/var/log/dmesg] | |
file = /var/log/dmesg | |
log_group_name = /var/log/dmesg | |
log_stream_name = {cluster}/{container_instance_id} | |
[/var/log/messages] | |
file = /var/log/messages | |
log_group_name = /var/log/messages | |
log_stream_name = {cluster}/{container_instance_id} | |
datetime_format = %b %d %H:%M:%S | |
[/var/log/docker] | |
file = /var/log/docker | |
log_group_name = /var/log/docker | |
log_stream_name = {cluster}/{container_instance_id} | |
datetime_format = %Y-%m-%dT%H:%M:%S.%f | |
[/var/log/ecs/ecs-init.log] | |
file = /var/log/ecs/ecs-init.log.* | |
log_group_name = /var/log/ecs/ecs-init.log | |
log_stream_name = {cluster}/{container_instance_id} | |
datetime_format = %Y-%m-%dT%H:%M:%SZ | |
[/var/log/ecs/ecs-agent.log] | |
file = /var/log/ecs/ecs-agent.log.* | |
log_group_name = /var/log/ecs/ecs-agent.log | |
log_stream_name = {cluster}/{container_instance_id} | |
datetime_format = %Y-%m-%dT%H:%M:%SZ | |
[/var/log/ecs/audit.log] | |
file = /var/log/ecs/audit.log.* | |
log_group_name = /var/log/ecs/audit.log | |
log_stream_name = {cluster}/{container_instance_id} | |
datetime_format = %Y-%m-%dT%H:%M:%SZ | |
[/var/log/amazon/ssm/amazon-ssm-agent.log] | |
file = /var/log/amazon/ssm/amazon-ssm-agent.log | |
log_group_name = amazon-ssm | |
log_stream_name = agent-$STACK_NAME/{container_instance_id} | |
datetime_format = %Y-%m-%dT%H:%M:%SZ | |
[/var/log/amazon/ssm/errors.log] | |
file = /var/log/amazon/ssm/errors.log | |
log_group_name = amazon-ssm | |
log_stream_name = errors-$STACK_NAME/{container_instance_id} | |
datetime_format = %Y-%m-%dT%H:%M:%SZ | |
EOF | |
--==BOUNDARY== | |
MIME-Version: 1.0 | |
Content-Type: text/x-shellscript; charset="us-ascii" | |
#!/bin/bash | |
# Set the region to send CloudWatch Logs data to (the region where the container instance is located) | |
region=$(curl 169.254.169.254/latest/meta-data/placement/availability-zone | sed s'/.$//') | |
sed -i -e "s/region = us-east-1/region = $region/g" /etc/awslogs/awscli.conf | |
--==BOUNDARY== | |
MIME-Version: 1.0 | |
Content-Type: text/upstart-job; charset="us-ascii" | |
#upstart-job | |
description "Configure and start CloudWatch Logs agent on Amazon ECS container instance" | |
author "Amazon Web Services" | |
start on started ecs | |
script | |
exec 2>>/var/log/ecs/cloudwatch-logs-start.log | |
set -x | |
until curl -s http://localhost:51678/v1/metadata | |
do | |
sleep 1 | |
done | |
# Grab the cluster and container instance ARN from instance metadata | |
cluster=$(curl -s http://localhost:51678/v1/metadata | jq -r '. | .Cluster') | |
container_instance_id=$(curl -s http://localhost:51678/v1/metadata | jq -r '. | .ContainerInstanceArn' | awk -F/ '{print $2}' ) | |
# Replace the cluster name and container instance ID placeholders with the actual values | |
sed -i -e "s/{cluster}/$cluster/g" /etc/awslogs/awslogs.conf | |
sed -i -e "s/{container_instance_id}/$container_instance_id/g" /etc/awslogs/awslogs.conf | |
service awslogs start | |
chkconfig awslogs on | |
end script | |
--==BOUNDARY==-- | |
- ClusterName: !Ref EcsClusterName | |
AutoscalingGroup: | |
Type: AWS::AutoScaling::AutoScalingGroup | |
Properties: | |
VPCZoneIdentifier: !Ref SubnetIds | |
LaunchConfigurationName: !Ref AutoscalingLaunchConfig | |
MinSize: 0 | |
MaxSize: 0 | |
HealthCheckType: EC2 | |
Tags: | |
- Key: !Ref AWS::StackName | |
Value: 'true' | |
PropagateAtLaunch: true | |
- Key: Name | |
Value: !Ref AWS::StackName | |
PropagateAtLaunch: true | |
- Key: role | |
Value: !Ref AWS::StackName | |
PropagateAtLaunch: true | |
LifecycleHookRole: | |
Type: AWS::IAM::Role | |
Properties: | |
Path: / | |
AssumeRolePolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Effect: Allow | |
Action: | |
- sts:AssumeRole | |
Principal: | |
Service: autoscaling.amazonaws.com | |
Policies: | |
- PolicyName: SNSAccess | |
PolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Effect: Allow | |
Action: | |
- sns:Publish | |
Resource: !ImportValue ops-lambdas-prod:EcsLifecycleHookTopicArn | |
AutoscalingGroupInstanceTerminationHook: | |
Type: AWS::AutoScaling::LifecycleHook | |
Properties: | |
AutoScalingGroupName: !Ref AutoscalingGroup | |
HeartbeatTimeout: 600 | |
LifecycleTransition: autoscaling:EC2_INSTANCE_TERMINATING | |
NotificationTargetARN: !ImportValue ops-lambdas-prod:EcsLifecycleHookTopicArn | |
RoleARN: !GetAtt LifecycleHookRole.Arn | |
InstanceScaleOutPolicy: | |
Type: AWS::AutoScaling::ScalingPolicy | |
Properties: | |
AdjustmentType: PercentChangeInCapacity | |
AutoScalingGroupName: !Ref AutoscalingGroup | |
EstimatedInstanceWarmup: 420 | |
PolicyType: StepScaling | |
StepAdjustments: | |
- MetricIntervalLowerBound: 0 | |
MetricIntervalUpperBound: 10 | |
ScalingAdjustment: 10 | |
- MetricIntervalLowerBound: 10 | |
ScalingAdjustment: 30 | |
InstanceScaleInPolicy: | |
Type: AWS::AutoScaling::ScalingPolicy | |
Properties: | |
AdjustmentType: PercentChangeInCapacity | |
AutoScalingGroupName: !Ref AutoscalingGroup | |
EstimatedInstanceWarmup: 420 | |
PolicyType: StepScaling | |
StepAdjustments: | |
- MetricIntervalUpperBound: 0 | |
MetricIntervalLowerBound: -10 | |
ScalingAdjustment: -10 | |
- MetricIntervalUpperBound: -10 | |
ScalingAdjustment: -30 | |
InstanceCpuAlarmHigh: | |
Type: AWS::CloudWatch::Alarm | |
Properties: | |
EvaluationPeriods: 5 | |
Statistic: Average | |
Threshold: 80 | |
AlarmDescription: Alarm if instance CPU high enough to trigger scale out policy. | |
Period: 60 | |
AlarmActions: | |
- !Ref InstanceScaleOutPolicy | |
Namespace: AWS/EC2 | |
Dimensions: | |
- Name: AutoScalingGroupName | |
Value: !Ref AutoscalingGroup | |
ComparisonOperator: GreaterThanOrEqualToThreshold | |
MetricName: CPUUtilization | |
InstanceCpuAlarmLow: | |
Type: AWS::CloudWatch::Alarm | |
Properties: | |
EvaluationPeriods: 30 | |
Statistic: Average | |
Threshold: 30 | |
AlarmDescription: Alarm if instance CPU low long enough to trigger scale in policy. | |
Period: 60 | |
AlarmActions: | |
- !Ref InstanceScaleInPolicy | |
Namespace: AWS/EC2 | |
Dimensions: | |
- Name: AutoScalingGroupName | |
Value: !Ref AutoscalingGroup | |
ComparisonOperator: LessThanOrEqualToThreshold | |
MetricName: CPUUtilization | |
Outputs: | |
AutoscalingGroupName: | |
Description: Name of ASG. | |
Value: !Ref AutoscalingGroup |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks for it, that helped me a lot, this part of the code:
Is it another stack that created this SNS to use here? Do you have some examples of that?
Thanks again.