曾经用超了Lightsail的套餐流量,代价甚是高昂。不过后来发现亚马逊的官方博客上其实有提供一个使用Lambda监控流量使用额度,超过设置时自动关机并通过SNS(Simple Notification Service)发送电子邮件提醒的教程。
Lambda自身有免费使用额度,而SNS通知按照使用量计费,价格并不高。邮件通知只会在Lightsail流量超过设置额度时才会发送,因此总的来说要比支付高昂的超额流量费要划算的多。当然,这个邮件通知也并非必要的,不需要的话完全可以注释掉其中有关邮件通知的内容。
虽然官方的教程大部分地方都足够详细,不过有少量几个说的不太清,比较容易让人不知道怎么办的地方。
区域
为了确保Lambda、Lightsail和SNS Topic都能互相通信,这三个东西最好都在同一个区域创建。比如先在us-east-1创建了Lightsail,那另外两个也跟着一起在Lightsail同样的区域中创建。us-west-2或者其他区域也一样。
创建SNS Topic(主题)
官方教程只提到了要创建一个Topic,但是并未告知在哪里创建。当然,在亚马逊的文档里指出了应该在SNS控制台中创建。至于剩下的,只要按照官方教程,类型选择“标准”,然后取个名字就行了,其余设置项保持默认即可。
不过,如果你不需要邮件通知,那这个步骤完全可以跳过。
创建Lambda函数
可以参考Lambda的文档,不过实际上在AWS控制台主页搜索Lambda也能找到Lambda的页面了。然后点击创建函数即可。
接下来参考官方教程,起一个名字,运行时选择Python 3.12,架构选择x86_64就可以了。
编辑Lambda要执行的代码
首先,用请用Chrome或者同内核的浏览器,因为这个Lambda的代码编辑器在其他内核浏览器中有可能不能用。
如果你想按照官方教程使用SNS提醒的话,直接将官方教程的Python脚本代码粘贴进去就行:
import json
import boto3
import calendar
import os
from datetime import datetime, date, time,timedelta
SNS_TOPIC = os.environ['SNS_TOPIC']
def get_current_month_first_day_zero_time():
today = date.today()
first_day = today.replace(day=1)
first_day_zero_time = datetime.combine(first_day, time.min)
return first_day_zero_time
def get_current_month_last_day_last_time():
today = date.today()
last_day = today.replace(day=calendar.monthrange(today.year, today.month)[1])
last_day_last_time = datetime.combine(last_day, time(23, 59, 59))
return last_day_last_time
def stop_instance(instance_name):
client = boto3.client('lightsail')
response = client.stop_instance(
instanceName=instance_name,
force=True
)
def list_instances(instances_list):
client = boto3.client('lightsail')
paginator = client.get_paginator('get_instances')
# Create a PageIterator from the Paginator
page_iterator = paginator.paginate()
for page in page_iterator:
for instance in page['instances']:
print(instance['name'])
instances_list.append(instance['name'])
def get_month_dto_quota(instance_name):
client = boto3.client('lightsail')
response = client.get_instance(
instanceName=instance_name
)
#print("response : {}".format(response))
dto_quota = response['instance']['networking']['monthlyTransfer']['gbPerMonthAllocated']
current_datetime = datetime.now()
instance_created_datetime = response['instance']['createdAt']
if (instance_created_datetime.year == current_datetime.year) and (instance_created_datetime.month == current_datetime.month):
month_ts = get_current_month_last_day_last_time().timestamp() - get_current_month_first_day_zero_time().timestamp()
instance_valide_ts = get_current_month_last_day_last_time().timestamp() - instance_created_datetime.timestamp()
dto_quota = (instance_valide_ts/month_ts) * dto_quota
print("created in current month, quota: {}GB".format(dto_quota))
else:
dto_quota = response['instance']['networking']['monthlyTransfer']['gbPerMonthAllocated']
print("created in previous month, full quota: {}GB".format(dto_quota))
return dto_quota
def get_instance_data_usage(instance_name, data_type):
client = boto3.client('lightsail')
current_time = datetime.utcnow()
start_time = get_current_month_first_day_zero_time()
end_time = get_current_month_last_day_last_time()
start_time_str = start_time.strftime('%Y-%m-%dT%H:%M:%SZ')
end_time_str = end_time.strftime('%Y-%m-%dT%H:%M:%SZ')
response = client.get_instance_metric_data(
instanceName=instance_name,
metricName=data_type,
period= 6 * 600 * 24,
unit='Bytes',
statistics=[
'Sum'
],
startTime=start_time_str,
endTime=end_time_str
)
data_points = response['metricData']
total_data_usage = sum([data_point['sum'] for data_point in data_points])
print("total {} usage: {}".format(data_type, total_data_usage))
return total_data_usage
def push_notification(arn, msg):
sns_client = boto3.client('sns')
print("sqs arn: {}".format(arn))
response = sns_client.publish(
TopicArn=arn,
Message=msg,
Subject='Lightsail NetworkOut exceeded quota '
)
def lambda_handler(event, context):
instance_name= []
list_instances(instance_name)
for i in instance_name:
quota = get_month_dto_quota(i) * 1000 * 1000 * 1000
total = get_instance_data_usage(i, "NetworkOut") + get_instance_data_usage(i, "NetworkIn")
msg = f"instance_name: {i} \nusage: {total} Byte \nquota: {quota} Byte \nusage percent: {(total/quota)*100} %"
print(msg)
if int(quota) < int(total):
print("quota < total, soforce close instance: {}".format(1))
push_notification(SNS_TOPIC, msg)
stop_instance(i)
return {
'statusCode': 200,
'body': json.dumps('total_data_usage from Lambda!')
}
如果你打算做些调整,比如不使用SNS发送邮件提醒,可以注释第7行与第105行;如果你想要降低触发阈值,可以修改第103和第104行为:
if (int(quota) * 阈值) < int(total):
print("(quota * 阈值) < total, soforce close instance: {}".format(1))
这里阈值为小数表示,如希望在流量超过90%时关机,那可以改为:
if (int(quota) * 0.9) < int(total):
print("(quota *0.9) < total, soforce close instance: {}".format(1))
当Python脚本设置好后,点击Deploy部署即可。
修改Lambda的运行配置和权限配置
修改运行配置很简单,编点击编辑器上面的配置选项卡,选择常规配置,然后照着教程修改配置即可。教程推荐的修改的配置是1024MB内存和10分钟超时时间,对于这段脚本来说其实资源是过多的,改成512MB内存与3分钟超时时间也没有问题。
如果你不使用SNS通知那可以直接看下一段。首先打开SNS的页面,找到之前创建的主题,复制它的ARN。然后在刚刚的Lambda函数的配置选项卡下选择环境变量。在其中创建键为SNS_TOPIC,值为刚才复制的ARN的环境变量。
接下来选择左侧的权限,然后点击角色名称下面的蓝色角色名称连接。在点击权限策略区域的添加权限,然后选择创建内联策略,参考如下内容修改:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"lightsail:GetInstance",
"lightsail:GetInstanceMetricData",
"lightsail:GetInstances",
"lightsail:StopInstance"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "sns:Publish",
"Resource": "粘贴刚才的sns arn到这里"
}
]
}
当然,不用邮件通知的话,那么
{
"Effect": "Allow",
"Action": "sns:Publish",
"Resource": "粘贴刚才的sns arn到这里"
}
和它们上一行的逗号都去掉就可以了。接下来点击下一步,给权限配置取个名字,然后完成即可。
设置定时触发
回到Lambda的页面,然后选择配置选项卡中的触发器,点击添加触发器。触发器的源选择EventBridge (CloudWatch Events) ,然后选择创建新规则,为规则取一个名字。接下来在规则类型中选择计划表达式,使用cron或rate格式填写触发时间。比如想让他每15分钟触发一次,那填写cron(0/15 * * * ? *),最后点击添加即可。
设置SNS通知邮箱
再次回到之前的SNS页面,选择刚刚创建的主题,在订阅选项卡中点击创建订阅。在创建页面中,协议选择电子邮件,终端节点则填写你想要接收通知的邮箱。当创建订阅后,之前填入的邮箱会收到一封确认邮件,确认之后就设置好了。
Comments NOTHING