Skip to content

Instantly share code, notes, and snippets.

@TheDeveloper
Last active February 28, 2024 12:00
Show Gist options
  • Save TheDeveloper/242ebf1bfb11b8b6f9a9b0f454897ac7 to your computer and use it in GitHub Desktop.
Save TheDeveloper/242ebf1bfb11b8b6f9a9b0f454897ac7 to your computer and use it in GitHub Desktop.

Revisions

  1. TheDeveloper revised this gist Nov 27, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion aws-cluster-stack.yml
    Original file line number Diff line number Diff line change
    @@ -8,7 +8,7 @@
    # SecurityGroups=group1,group2 \
    # ImageId=ami-123456 \
    # InstanceType=c5.large \
    # Subnets=sn-1234,sn-5678 \
    # Subnets=subnet-1234,subnet-5678 \
    # EcsClusterName=myapp-prod \
    # --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM \
    # --no-execute-changeset
  2. TheDeveloper revised this gist Nov 27, 2017. 1 changed file with 10 additions and 5 deletions.
    15 changes: 10 additions & 5 deletions aws-cluster-stack.yml
    Original file line number Diff line number Diff line change
    @@ -1,17 +1,22 @@
    # 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=<KEY_NAME> \
    # --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=sn-1234,sn-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 app ECS cluster.
    Description: EC2 instances for ECS cluster.

    Parameters:
    KeyName:
  3. TheDeveloper created this gist Nov 16, 2017.
    304 changes: 304 additions & 0 deletions aws-cluster-stack.yml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,304 @@
    # 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=<KEY_NAME> \
    # --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM \
    # --no-execute-changeset

    AWSTemplateFormatVersion: '2010-09-09'
    Transform: 'AWS::Serverless-2016-10-31'

    Description: EC2 instances for app 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