-
Notifications
You must be signed in to change notification settings - Fork 319
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Kazuki Matsuda
committed
Nov 13, 2018
1 parent
8d619db
commit a05daf6
Showing
3 changed files
with
172 additions
and
0 deletions.
There are no files selected for viewing
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
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
132
ecs-ec2-spot-auto-deregister/ecs-ec2-spot-auto-deregister.yaml
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
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/*" | ||
|