以下是正文(内容有点长,自己按需修改)
AWS Lightsail 流量自动守护系统 · 完整手册
适用对象:AWS Lightsail |区域:东京 ap-northeast-1|实例:aws-jp|套餐:5 美元/月/1TB 流量
本手册所有 API 名称、IAM 权限项均对照 AWS 官方文档核对,可放心照做。
一、系统做了什么事(先用 30 秒理解全局)
流量<80% → 北京时间 10:00 发日报邮件(含入/出/总计)
流量≥80% → 北京时间 10:00 + 20:00 各发一封告警邮件
流量≥920GB → 立即强制关机 + 打标签 TrafficShutdown=true + 发告警邮件
次月 1 日 00:10 → 仅自动开机"因流量超限关机"的实例 + 清标签 + 发通知
手动关机的实例 → 次月不会被误开机
四根支柱:SNS(发邮件)+ Lambda ①(监控/关机)+ Lambda ②(次月开机)+ EventBridge Scheduler(4 个定时任务)
二、准备工作(一次性)
控制台右上角区域选择器 切到 亚太地区(东京)ap-northeast-1 ,后续所有操作都在此区域
准备好你要接收告警的邮箱
记下你的 AWS 账号 ID(12 位数字,在右上角头像处可见)
三、Step 1 — 创建 SNS 主题(邮件通道)
搜 SNS → 进入
左侧 主题 → 创建主题
类型 :标准
名称 :LightsailTrafficAlert
其他默认 → 创建主题
进入该主题 → 创建订阅
去邮箱点击确认链接(不点收不到任何邮件 )
📌 创建后复制页面上的 主题 ARN ,长得像 arn:aws:sns:ap-northeast-1:123456789012:LightsailTrafficAlert,后续要用
四、Step 2 — 创建 Lambda ①(流量监控 + 自动关机)
4.1 建函数
搜 Lambda → 创建函数
从头开始创作
函数名称:LightsailMonitor
运行时:Python 3.12
权限:选"创建新的执行角色"(向导自动建一个基础角色,下一步我们会替换为最小权限)
创建函数
4.2 粘贴完整代码
清空 lambda_function.py,完整复制 以下内容:
import os
import boto3
from datetime import datetime, timezone, timedelta
REGION = os.environ['REGION_NAME' ]
lightsail = boto3.client('lightsail' , region_name=REGION)
sns = boto3.client('sns' , region_name=REGION)
INSTANCE = os.environ['INSTANCE_NAME' ]
SNS_ARN = os.environ['SNS_TOPIC_ARN' ]
QUOTA_GB = float (os.environ['MONTHLY_QUOTA_GB' ])
WARN_80 = float (os.environ.get('WARN_80' , 80 ))
THRESH_GB = float (os.environ['THRESHOLD_GB' ])
TAG_KEY = os.environ.get('TAG_KEY' , 'TrafficShutdown' )
TAG_VAL = os.environ.get('TAG_VAL' , 'true' )
def get_monthly_traffic (inst ):
"""本月1号0点UTC ~ 现在,NetworkIn + NetworkOut,Bytes"""
now = datetime.now(timezone.utc)
start = datetime(now.year, now.month, 1 , tzinfo=timezone.utc)
total = 0
brk = {}
for m in ('NetworkIn' , 'NetworkOut' ):
resp = lightsail.get_instance_metric_data(
instanceName=inst,
metricName=m,
period=86400 ,
unit='Bytes' ,
statistics=['Sum' ],
startTime=start,
endTime=now
)
b = sum (dp['sum' ] for dp in resp.get('metricData' , []))
brk[m] = b
total += b
return total, brk
def gb (b ):
return round (b / 1024 **3 , 2 )
def lambda_handler (event, context ):
now = datetime.now(timezone.utc)
ub, brk = get_monthly_traffic(INSTANCE)
used = gb(ub)
pct = round (used / QUOTA_GB * 100 , 1 )
in_gb = gb(brk['NetworkIn' ])
out_gb = gb(brk['NetworkOut' ])
if pct < 60 :
status_emoji, status_text = "🟢 " , "安全"
elif pct < 80 :
status_emoji, status_text = "🟡 " , "注意"
elif pct < 95 :
status_emoji, status_text = "🟠 " , "预警"
else :
status_emoji, status_text = "🔴 " , "高危"
print (f"[{INSTANCE} ] {now} UTC | In={in_gb} Out={out_gb} "
f"Total={used} GB ({pct} %) | thresh={THRESH_GB} quota={QUOTA_GB} " )
if used >= THRESH_GB:
msg = (
f"⚠ ️ [{INSTANCE} ] 流量 {used} GB({pct} %) 已超 {THRESH_GB} GB\n"
f"明细: In={in_gb} GB, Out={out_gb} GB\n"
f"执行强制关机,并打 tag {TAG_KEY} ={TAG_VAL} "
)
print (msg)
lightsail.tag_resource(
resourceName=INSTANCE,
tags=[{'key' : TAG_KEY, 'value' : TAG_VAL}]
)
lightsail.stop_instance(instanceName=INSTANCE, force=True )
sns.publish(
TopicArn=SNS_ARN,
Subject=f'[{INSTANCE} ] 流量超限关机' ,
Message=msg
)
return {'statusCode' : 200 , 'body' : msg}
level = event.get('notify_level' , '' )
if pct >= WARN_80:
should = True
else :
should = (level == 'normal' )
if should:
bj_time = datetime.now(timezone.utc).astimezone(
timezone(timedelta(hours=8 ))
).strftime('%Y-%m-%d %H:%M:%S' )
msg = f"""
========================================
{status_emoji} Lightsail 每日流量报告 [{INSTANCE} ]
========================================
📈 核心数据:
已用流量:{used} GB ({pct} %) - {status_text}
剩余可用:{round (QUOTA_GB - used, 2 )} GB
----------------------------------------
📥 入站流量:{in_gb} GB
📤 出站流量:{out_gb} GB
📦 本月总计:{used} GB
----------------------------------------
⚙ ️ 阈值配置:
月度配额:{QUOTA_GB} GB
告警线:{WARN_80} % (达标后每日双通知)
关机线:{THRESH_GB} GB (达标后自动关机)
========================================
⏰ 报告时间:{bj_time} (北京时间)
"""
sns.publish(
TopicArn=SNS_ARN,
Subject=f'[{INSTANCE} ] 每日流量周报' ,
Message=msg
)
print ("通知已发" )
else :
print ("未达通知条件 / 非发送班次,跳过邮件" )
return {'statusCode' : 200 , 'body' : f'{used} GB ({pct} %) ok' }
点 部署 。
4.3 配置环境变量
配置 → 环境变量 → 编辑 → 添加 ,共 7 个:
Key
Value
INSTANCE_NAME
aws-jp
REGION_NAME
ap-northeast-1
SNS_TOPIC_ARN
arn:aws:sns:ap-northeast-1:你的账号ID:LightsailTrafficAlert
MONTHLY_QUOTA_GB
1024
WARN_80
80
THRESHOLD_GB
920
TAG_KEY
TrafficShutdown
保存。
4.4 调整为最小权限(替换向导自动给的角色)
配置 → 权限 ,点击蓝色角色名链接 → 跳转到 IAM
在 IAM 页面 添加内联策略 → JSON:
{
"Version" : "2012-10-17" ,
"Statement" : [
{
"Effect" : "Allow" ,
"Action" : [
"lightsail:GetInstanceMetricData" ,
"lightsail:GetInstance" ,
"lightsail:StopInstance" ,
"lightsail:TagResource"
] ,
"Resource" : "*"
} ,
{
"Effect" : "Allow" ,
"Action" : [ "sns:Publish" ] ,
"Resource" : "arn:aws:sns:ap-northeast-1:你的账号ID:LightsailTrafficAlert"
}
]
}
这四个 Lightsail 动作均为 AWS 官方 IAM 文档确认的合法动作
策略命名 LightsailMonitorPolicy → 创建。
五、Step 3 — 创建 Lambda ②(次月自动开机)
5.1 建函数
同 4.1,名称改为 LightsailMonthlyBoot,其余一致。
5.2 完整代码
import os
import boto3
from datetime import datetime, timezone, timedelta
REGION = os.environ['REGION_NAME' ]
lightsail = boto3.client('lightsail' , region_name=REGION)
sns = boto3.client('sns' , region_name=REGION)
INSTANCE = os.environ['INSTANCE_NAME' ]
SNS_ARN = os.environ['SNS_TOPIC_ARN' ]
TAG_KEY = os.environ.get('TAG_KEY' , 'TrafficShutdown' )
TAG_VAL = os.environ.get('TAG_VAL' , 'true' )
def beijing_now ():
return datetime.now(timezone.utc) + timedelta(hours=8 )
def lambda_handler (event, context ):
bj = beijing_now()
print (f"当前北京时间: {bj.strftime('%Y-%m-%d %H:%M:%S' )} " )
if bj.day != 1 :
print (f"今天北京是 {bj.day} 号,不是 1 号,跳过" )
return {'statusCode' : 200 , 'body' : 'not the 1st, skip' }
print ("今天是北京 1 号,开始检查是否需要开机..." )
try :
resp = lightsail.get_instance(instanceName=INSTANCE)
except Exception as e:
print (f"获取实例失败: {e} " )
return {'statusCode' : 500 , 'body' : str (e)}
inst = resp['instance' ]
tags = {t['key' ]: t['value' ] for t in inst.get('tags' , [])}
if tags.get(TAG_KEY) != TAG_VAL:
print (f"实例无 {TAG_KEY} ={TAG_VAL} tag,不自动开机(可能是手动关机的)" )
return {'statusCode' : 200 , 'body' : 'no tag, skip' }
msg = f"✅ [{INSTANCE} ] 次月 1 日自动开机(因上月流量超限被关)"
print (msg)
lightsail.start_instance(instanceName=INSTANCE)
lightsail.tag_resource(
resourceName=INSTANCE,
tags=[{'key' : TAG_KEY, 'value' : 'false' }]
)
sns.publish(
TopicArn=SNS_ARN,
Subject=f'[{INSTANCE} ] 次月自动开机' ,
Message=msg
)
return {'statusCode' : 200 , 'body' : msg}
部署。
5.3 环境变量
同 Lambda ① 的前 3 个 + TAG_KEY/TAG_VAL,共 5 个:INSTANCE_NAME、REGION_NAME、SNS_TOPIC_ARN、TAG_KEY、TAG_VAL
5.4 最小权限 IAM 策略
{
"Version" : "2012-10-17" ,
"Statement" : [
{
"Effect" : "Allow" ,
"Action" : [
"lightsail:GetInstance" ,
"lightsail:StartInstance" ,
"lightsail:TagResource"
] ,
"Resource" : "*"
} ,
{
"Effect" : "Allow" ,
"Action" : [ "sns:Publish" ] ,
"Resource" : "arn:aws:sns:ap-northeast-1:你的账号ID:LightsailTrafficAlert"
}
]
}
StartInstance 和 TagResource 都是 AWS 官方文档确认的合法 Lightsail IAM 动作
六、Step 4 — EventBridge Scheduler 创建 4 个定时任务
💡 关键改进:EventBridge Scheduler 原生支持时区 ,所以我们可以直接选 Asia/Shanghai ,再也不用手算 UTC
进入 EventBridge → 调度器 → 创建计划 ,4 个任务按下表填:
任务 1:巡逻兵(每 15 分钟)
项目
值
名称
Lightsail-Patrol
计划类型
固定频率
频率
15 minutes
时区
Asia/Shanghai
目标
AWS Lambda 函数 → LightsailMonitor
输入
默认(空)
任务 2:早班车(北京 10:00)
项目
值
名称
Lightsail-Morning
计划类型
Cron 计划
Cron
0 10 * * ? *
时区
Asia/Shanghai
目标
Lambda → LightsailMonitor
输入
常量 JSON:{"notify_level": "normal"}
任务 3:晚班车(北京 20:00)
项目
值
名称
Lightsail-Evening
计划类型
Cron 计划
Cron
0 20 * * ? *
时区
Asia/Shanghai
目标
Lambda → LightsailMonitor
输入
常量 JSON:{"notify_level": "extra"}
任务 4:次月开机(北京 00:10)
项目
值
名称
Lightsail-Monthly-Boot
计划类型
Cron 计划
Cron
10 0 1 * ? *
时区
Asia/Shanghai
目标
Lambda → LightsailMonthlyBoot
输入
默认(空)
说明:EventBridge Scheduler 支持在指定时区评估 cron 表达式,因此我们直接用北京时间 10 0 1 * ? * 表示每月 1 日 00:10,Lambda ② 内部无需再判断日期,逻辑更简洁
每个任务创建时,Scheduler 会自动创建一个执行角色来调用 Lambda,无需手动建 IAM 角色。
七、Step 5 — 验证测试
7.1 验证 SNS 通道
SNS 主题页 → 发布消息 → 随便写点内容 → 发布 → 邮箱 1 分钟内收到 = 通道正常。
7.2 验证 Lambda ① 三个测试事件
Lambda → LightsailMonitor → 测试 → 新建事件:
Patrol-Empty:事件 JSON {}
Morning-10am:事件 JSON {"notify_level": "normal"}
Evening-8pm:事件 JSON {"notify_level": "extra"}
分别测试,预期:
Patrol-Empty → 日志有数据,邮箱不收到 (<80% 且非班次时间)
Morning-10am → 邮箱收到美化版日报
Evening-8pm → <80% 时不发;≥80% 时发
7.3 验证关机逻辑
临时把 THRESHOLD_GB 改为 1
部署后测试 Patrol-Empty
预期:日志显示关机 → Lightsail 控制台 aws-jp 状态变 Stopped → 邮箱收到"流量超限关机"邮件
测试完必须改回 920 并部署 ,然后去 Lightsail 控制台手动开机
7.4 验证开机逻辑
手动停止 aws-jp
给 aws-jp 打 tag:TrafficShutdown = true
Lambda → LightsailMonthlyBoot → 测试 {}
预期:实例开机 → tag 变为 false → 邮箱收到开机通知
测试完代码无需改动(生产代码本就是 if bj.day != 1:)
八、上线后日常运维
8.1 每周巡检
邮箱确认日报正常到达
Lightsail 控制台确认 TrafficShutdown 标签为 false
8.2 调整阈值(如改 80% 为 70%)
Lambda ① → 配置 → 环境变量 → 改 WARN_80=70 → 保存 → 部署
8.3 暂停/恢复通知
EventBridge 调度器 → 对应规则 → 禁用/启用
8.4 完全卸载
EventBridge 调度器:删除 4 个计划
Lambda:删除 2 个函数
IAM → 角色:删除 2 个 Lambda 角色
SNS → 主题:删除 LightsailTrafficAlert
九、成本说明
整套方案完全在 AWS 免费额度内:
Lambda:每月调用约 3000 次,远低于 100 万次免费额度
EventBridge Scheduler:调度次数免费额度充足
SNS:每月 1000 封免费邮件
CloudWatch 日志:免费额度内
实际只产生 Lightsail 5 美元套餐本身的费用,自动化系统本身 0 成本。