When you have many EC2 instances, you may forget to stop instances that you are not using.
If you keep running the instances that aren't necessary, it keeps adding to your costs of running the instances.
To avoid this, you can use Lambda to delete EC2 instances automatically and periodically.
Create Lambda functions
I wrote in Python.
import json
import boto3
import os
import requests
def terminate(event, context):
EXCLUDE_TERMINATE_TAG = os.environ['EXCLUDE_TERMINATE_TAG']
CHATWORK_API_KEY = os.environ['CHATWORK_API_KEY']
CHATWORK_ROOM_ID = os.environ['CHATWORK_ROOM_ID']
CHATWORK_ENDPOINT = os.environ['CHATWORK_ENDPOINT']
ec2 = boto3.client('ec2')
without_tag = ec2.describe_instances()
with_tag = ec2.describe_instances(
Filters=[{ 'Name': 'tag:' + EXCLUDE_TERMINATE_TAG, 'Values': ['true'] }]
)
without_tag_set = set(ec2['InstanceId'] for resId in without_tag['Reservations'] for ec2 in resId['Instances'])
with_tag_set = set(ec2['InstanceId'] for resId in with_tag['Reservations'] for ec2 in resId['Instances'])
target_instances = without_tag_set - with_tag_set
list_target_instances = list(target_instances)
if len(list_target_instances) != 0:
terminateInstances = ec2.terminate_instances(
InstanceIds=list_target_instances
)
notify_instances = ''
if len(with_tag['Reservations']) != 0:
for reservation in with_tag['Reservations']:
for instance in reservation['Instances']:
if len(instance['Tags']) != 0:
instance_name = ''
for tag in instance['Tags']:
if tag['Key'] == 'Name':
instance_name = tag['Value']
instance_state_enc = json.dumps(instance['State'])
instance_state_dec = json.loads(instance_state_enc)
if instance_name != '':
notify_instances += instance['InstanceId'] + ' -> ' + instance_name + '(' + instance_state_dec['Name'] + ')'+ '\n'
else:
notify_instances += instance['InstanceId'] + ' -> ' + 'None' + '(' + instance_state_dec['Name'] + ')'+ '\n'
message = '[To:350118][To:1786285][info][title][Beyond POC] There are instance that have been removed (devil)[/title]' + notify_instances + '[/info]'
PostChatwork(CHATWORK_ENDPOINT,CHATWORK_API_KEY,CHATWORK_ROOM_ID,message)
response = {
"TerminateInstances": list_target_instances
}
print (response)
return response
def PostChatwork(ENDPOINT,API_KEY,ROOM_ID,MESSAGE):
post_message_url = '{}/rooms/{}/messages'.format(ENDPOINT, ROOM_ID)
headers = { 'X-ChatWorkToken': API_KEY }
params = { 'body': MESSAGE }
resp = requests.post(post_message_url,
headers=headers,
params=params)
You might have an idea if you read the code, but it says
Delete the instances that have no exclude tag.
Notify in Chatwork, a business chat tool we use, of the instances that have been excluded from the deletion target.
Sometimes there are some instances that I still want to keep, in that case, I add a “EXCLUDE_TERMINATE:true” tag.
Serverless Framework setting
How to deploy this code to Lambda.
I used a tool that’s called Serverless Framework.
This is a CLI tool that deploys functions according to the configuration file. In the case of Lambda, it works with CloudFront to deploy.
I named serverless.yml for the configuration file.
# Welcome to Serverless!
#
# This file is the main config file for your service.
# It's very minimal at this point and uses default values.
# You can always add more config options for more control.
# We've included some commented out config examples here.
# Just uncomment any of them to get that config option.
#
# For full config options, check the docs:
# docs.serverless.com
#
# Happy Coding!
service: beyond-poc
plugins:
- serverless-prune-plugin
provider:
name: aws
runtime: python3.8
profile: ${opt:profile, ''}
region: ap-northeast-1
role: [IAM Role Name]
environment:
EXCLUDE_TERMINATE_TAG: EXCLUDE_TERMINATE
CHATWORK_API_KEY: [ChatWark API KEY]
CHATWORK_ROOM_ID: [ChatWark ROOM ID]
CHATWORK_ENDPOINT: https://api.chatwork.com/v2
timeout: 10
# my custom env
custom:
prune:
automatic: true
number: 5
# you can overwrite defaults here
# stage: dev
# region: us-east-1
# you can add packaging information here
#package:
# include:
# - include-me.py
# - include-me-dir/**
# exclude:
# - exclude-me.py
# - exclude-me-dir/**
functions:
terminate-instance:
handler: handler.terminate
events:
- schedule: cron(0 15 ? * * *)
timeout: 120
memorySize: 128
reservedConcurrency: 1
The environment section sets confirmation information of the Chatwork and exclusion tag as an environment variable.
The event section sets a schedule, but with this setting, it works with CloudWatchEvents and executes at 15:00(UTC) every day.
Save the code to deploy to the directory where this configuration file is located.
$ sis deploy --profile [AWS Profile Name]
Now you can deploy the CloudFormation stack that has been automatically created by Serverless Framework from the command line.
This blog post is translated from a blog post written by Yuki Teraoka on our Japanese website Beyond Co..
Comments