部署 Lambda 函数以定期删除 EC2 实例

我是寺冈,一名基础设施工程师。
这次,我想总结一下我部署用于内部使用的 Lambda 函数的经验。
除了我们运营管理的客户服务器运行所在的 AWS 账户外,
我们还管理着内部工程师用于技术验证的账户。
此账户已定义登录安全设置规则(IAM 用户角色/MFA 身份验证),但
对于拥有必要权限的成员,账户内的操作通常不受限制。
规则过多会使其难以使用,尤其是在测试方面。
在我们的案例中,验证过程中创建频率最高的资源是 EC2 实例。
虽然可以随意创建,但也意味着忘记停止或删除这些实例的情况相当普遍。
当然,频繁发生此类事件会导致不必要的成本。
虽然原则上实例在使用后应该立即停止或删除,但
为了防止遗忘,我们还是实施了一种机制,利用 Lambda 函数定期自动删除 EC2 实例。
以下是我所做工作的总结。
创建 Lambda 函数
我用Python写的。
但我个人更熟悉Golang,所以我觉得我应该用Golang来写。
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在 instance['Tags'] 中:如果 tag['Key'] == 'Name':instance_name = tag['Value'] instance_state_enc = json.dumps(instance['State']) instance_state_dec = json.loads(instance_state_enc) 如果 instance_name != '':notify_instances += instance['InstanceId'] + ' -> ' + instance_name + '(' + instance_state_dec['Name'] + ')' + '\n' 否则:notify_instances += instance['InstanceId'] + ' -> ' + 'None' + '(' + instance_state_dec['Name'] + ')' + '\n' message = '[To:350118][To:1786285][info][title][Beyond POC] There are returned instances (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)
我想你看了代码就能明白了。
- 删除没有排除标签的实例
- 通知 Chatwork 哪些实例已被排除在删除范围之外
这个过程由系统自动执行。
但是,由于未经询问就删除文件可能会造成问题,因此
在这种情况下,您应该添加排除标签“EXCLUDE_TERMINATE: true”
公司内部有一条规定,
Serverless Framework 配置
本次演讲将讲解如何将您编写的代码部署到 Lambda。
我们正在使用一个名为 Serverless Framework 的工具。https
://serverless.com
到 Lambda 等无服务器服务
一个 CLI 工具,它根据指定的配置文件将函数部署
;对于 Lambda,它与 CloudFormation 协同工作以执行部署。
准备一个名为 serverless.yml 的配置文件。
# 欢迎使用 Serverless!# # 此文件是您服务的主要配置文件。# 目前它非常简洁,使用默认值。# 您可以随时添加更多配置选项以获得更精细的控制。# 我们在此处提供了一些注释掉的配置示例。# 只需取消注释即可启用相应的配置选项。# # 有关完整的配置选项,请查看文档:# docs.serverless.com # # 祝您编码愉快!服务:beyond-poc 插件:- serverless-prune-plugin 提供程序:名称:aws 运行时:python3.8 配置文件:${opt:profile, ''} 区域:ap-northeast-1 角色:[IAM 角色名称] 环境变量:EXCLUDE_TERMINATE_TAG:EXCLUDE_TERMINATE CHATWORK_API_KEY:[ChatWark API 密钥] CHATWORK_ROOM_ID:[ChatWark 房间 ID] CHATWORK_ENDPOINT:https://api.chatwork.com/v2 超时:10 # 我的自定义环境变量 custom: prune: automatic: true number: 5 # 您可以在此处覆盖默认值 # stage: dev # region: us-east-1 # 您可以在此处添加打包信息 #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
在“环境”部分,我已将排除标签和 ChatWork 身份验证信息设置为环境变量。
在“函数”部分,我已实际设置了函数。
在“事件”部分,我设置了一个计划任务,该
任务将与 CloudWatch Events 同步,每天 15:00 (UTC) 定期运行。
在日本标准时间 (JST),这相当于午夜 (0:00)。
将要部署的代码保存到此配置文件所在的目录中。
$ sis deploy --profile [AWS Profile Name]
由 Serverless Framework 自动创建的
CloudFormation 堆栈
最后
我厚颜无耻地把它当成博客素材,但
实际上,我是公司里最经常忘记删除东西的人。真抱歉……
1
