Skip to content

Commit

Permalink
add ecs-ec2-spot-auto-deregister
Browse files Browse the repository at this point in the history
  • Loading branch information
Kazuki Matsuda committed Nov 13, 2018
1 parent 8d619db commit a05daf6
Show file tree
Hide file tree
Showing 3 changed files with 172 additions and 0 deletions.
40 changes: 40 additions & 0 deletions ecs-ec2-spot-auto-deregister/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Auto Deregistration EC2 Spot Instances from ECS Cluster

Example AWS CloudFormation template for deregistration from Amazon ECS Cluster for Amazon EC2 Spot Instances via Amazon CloudWatch Events & AWS Lambda.

## Getting Started

This CloudFormation template will deploy a rule of CloudWatch Events and a Lambda function with IAM Role.
When CloudWatch Events catch EC2 Spot Instance termination notice, Lambda function hooked automatically [deregister the ECS container instances](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/deregister_container_instance.html) from ECS Cluster.
You can use this template independently.

### Architecture

![ecs-spot-deregister](doc/ecs-spot-deregister.png "ecs-spot-deregister")

### Prerequisites

Nothing. Lambda function searches target ECS Cluster automatically.

### Pricing

AWS CloudFormation is a free service; however, you are charged for the AWS resources you include in your stacks at the current rates for each. For more information about AWS pricing, go to the detail page for each product on http://aws.amazon.com.

## Deployment

After signing up for an AWS account, you can use AWS CloudFormation through the AWS Management Console, AWS CloudFormation API, or AWS CLI.

Use the [template](ecs-ec2-spot-auto-deregister.yaml) to create a CloudFormation stack.

You can learn more about working with CloudFormation stacks [here](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/stacks.html).

## AWS services used

* [AWS CloudFormation](https://aws.amazon.com/cloudformation/)
* [Amazon EC2 Container Service (ECS)](https://aws.amazon.com/ecs/)
* [Amazon CloudWatch](https://aws.amazon.com/cloudwatch/)
* [AWS Lambda](https://aws.amazon.com/lambda/)

## Authors

* [**Kazuki Matsuda**](https://github.com/mats16)
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
132 changes: 132 additions & 0 deletions ecs-ec2-spot-auto-deregister/ecs-ec2-spot-auto-deregister.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
---
AWSTemplateFormatVersion: 2010-09-09
Description: 'Spot instances auto-deregister from ECS cluster'

Resources:
EventRule:
Type: AWS::Events::Rule
Properties:
Description: Spot Instance Interruption Warning
EventPattern:
source:
- "aws.ec2"
detail-type:
- "EC2 Spot Instance Interruption Warning"
State: "ENABLED"
Targets:
-
Arn: !GetAtt LambdaFunction.Arn
Id: "TargetFunctionV1"

PermissionForEventsToInvokeLambda:
Type: AWS::Lambda::Permission
Properties:
FunctionName: !Ref LambdaFunction
Action: "lambda:InvokeFunction"
Principal: "events.amazonaws.com"
SourceArn: !GetAtt EventRule.Arn

LambdaFunction:
Type: "AWS::Lambda::Function"
Properties:
Handler: "index.handler"
Role: !GetAtt LambdaExecutionRole.Arn
Code:
ZipFile: |
import boto3
import logging
import os
region = os.getenv('AWS_DEFAULT_REGION')
logger = logging.getLogger()
logger.setLevel(logging.INFO)
class ECS():
def __init__(self, region):
self.cl = boto3.client('ecs', region_name=region)
def get_cluster_arns(self):
cluster_arns = []
res = self.cl.list_clusters(maxResults=100)
cluster_arns += res['clusterArns']
while True:
if 'nextToken' in res:
res = self.cl.list_clusters(nextToken=res['nextToken'], maxResults=100)
cluster_arns += res['clusterArns']
else:
break
return cluster_arns
def deregister(self, instance_id):
container_instance_arn = None # default
cluster_arns = self.get_cluster_arns()
logger.info('Get all ecs clusters)
for cluster_arn in cluster_arns:
res = self.cl.list_container_instances(
cluster=cluster_arn,
filter='ec2InstanceId=={}'.format(instance_id),
maxResults=1,
)
if len(res['containerInstanceArns']) == 1:
container_instance_arn = res['containerInstanceArns'][0]
logger.info('{0} is used in {0}'.format(instance_id, cluster_arn))
else:
logger.debug('{0} is not used in {1}'.format(instance_id, cluster_arn))
if container_instance_arn:
res = self.cl.deregister_container_instance(
cluster=cluster_arn,
containerInstance=container_instance_arn,
force=True
)
logger.info(res)
else:
logger.warning('{0} is not used in all ecs clusters'.format(instance_id))
def handler(event, context):
instance_id = event['detail']['instance-id']
logger.info('{0} will be terminated in 2 minutes'.format(instance_id))
ecs = ECS(region)
ecs.deregister(instance_id)
Runtime: "python3.6"
Timeout: "10"

LambdaExecutionRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Principal:
Service:
- "lambda.amazonaws.com"
Action:
- "sts:AssumeRole"
Path: "/"
ManagedPolicyArns:
- "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
Policies:
-
PolicyName: "ecsDeregisterContainerInstance"
PolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Action: "ecs:ListClusters"
Resource: "*"
-
Effect: "Allow"
Action: "ecs:ListContainerInstances"
Resource: !Sub "arn:aws:ecs:${AWS::Region}:*:cluster/*"
-
Effect: "Allow"
Action: "ecs:DeregisterContainerInstance"
Resource: !Sub "arn:aws:ecs:${AWS::Region}:*:cluster/*"

0 comments on commit a05daf6

Please sign in to comment.