一、背景
Amazon Lightsail轻量服务器,算是在众多大厂服务器中比较划算的一款,3.5美刀一个月就可以获得这样的配置:
1 2 3 4
| 512 MB Memory 2 vCPUs Processing 20 GB SSD Storage 1 TB Transfer
|
还可以通过吃码的方式获取更优惠的价格。
但是超出流量限额后,每GB流量0.12美金,加上Lightsail的带宽最高可达4Gpbs,如果不注意流量超出限额后继续使用就会很容易产生巨额的账单。
所以,需要找一个方式,超过流量限额后,停止机器的使用,以免产生巨额账单。
二、大致思路
利用Amazon的Lambda函数计算(100W次以内免费),配合Amazon提供的官方Lightsail API,设置定时任务,每10分钟获取当前流量限额和已使用流量,进行对比,如果达到限额的95%,则关闭Lightsail实例:
- 利用 Lightsail 的 API 接口:
get_instance
,获取账号下在当前区域里的所有 Lightsail 实例。
- 根据 Lightsail 实例的类型,获取每个实例每个月的网络流量配额。
- 根据实例的创建时间,计算出每个实例在当前这个计费周期内的流量配额。
- 通过 API 接口:
get_instance_metric_data
,获取每个实例已经使用的入站和出站流量总量。
- 如果流量超出当前计费周期的配额的95%,并关闭对应的 Lightsail 实例。
- 通过 EventBridge 以 cron job 的方式定时触发 Lambda,运行此检查逻辑。
三、过程
1. 创建Lambda函数
在AWS控制台进入Lambda函数页面,创建新函数:
![01](2024-03-02-AWS Lightsail 流量限额使用完毕自动关机/01.jpg)
![02](2024-03-02-AWS Lightsail 流量限额使用完毕自动关机/02.jpg)
Python脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
| import json import boto3 import calendar import os from datetime import datetime, date, time,timedelta 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') 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 ) 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) * 0.95) < int(total): print("(quota * 0.95) < total, soforce close instance: {}".format(1)) stop_instance(i) return { 'statusCode': 200, 'body': json.dumps('total_data_usage from Lambda!') }
|
2. 修改函数运行配置
在之前创建的Lambda函数页面,进入配置——常规配置——编辑:
![03](2024-03-02-AWS Lightsail 流量限额使用完毕自动关机/03.jpg)
调大内存和超时时间:
![04](2024-03-02-AWS Lightsail 流量限额使用完毕自动关机/04.jpg)
3. 赋予Lambda权限
在之前创建的Lambda函数页面,进入配置——权限——点击链接跳转至IAM权限管理页面:
![05](2024-03-02-AWS Lightsail 流量限额使用完毕自动关机/05.jpg)
创建新的策略:
![06](2024-03-02-AWS Lightsail 流量限额使用完毕自动关机/06.jpg)
选择JSON,粘贴数据,保存
![07](2024-03-02-AWS Lightsail 流量限额使用完毕自动关机/07.jpg)
JSON数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| { "Version": "2012-10-17", "Statement": [ { "Action": [ "lightsail:GetInstance", "lightsail:GetInstanceMetricData", "lightsail:GetInstances", "lightsail:StopInstance" ], "Effect": "Allow", "Resource": "*" } ] }
|
策略创建完成后关闭IAM权限页面,回到Lambda函数页面,测试是否成功:
![08](2024-03-02-AWS Lightsail 流量限额使用完毕自动关机/08.jpg)
4. 创建定时任务触发Lambda函数
在Lambda函数页面点击添加触发器:
![09](2024-03-02-AWS Lightsail 流量限额使用完毕自动关机/09.jpg)
选择EventBridge,增加定时任务,保存:
表达式:cron(0/10 * * * ? *)
![10](2024-03-02-AWS Lightsail 流量限额使用完毕自动关机/10.jpg)
这时就已经基本完成配置了。
Lambda函数每10分钟执行一次,符合条件就会自动关闭实例。
5. 查看历史执行记录
在Lambda函数页面,选择监控,点击查看CloudWatch Logs跳转至日志页面。
![11](2024-03-02-AWS Lightsail 流量限额使用完毕自动关机/11.jpg)
选择日志就可以看到日志的详细信息了:
![12](2024-03-02-AWS Lightsail 流量限额使用完毕自动关机/12.jpg)
这样就可以查看历史的执行结果了。
后续可以再加入Amazon的订阅通知服务,关机时自动发送邮件给指定邮箱。