终版:AWS Lightsail 光帆 流量守护系统(邮件提醒、超流自动关机,次月1日自动开机)

exit 2026-07-22 11:17 1

昨天晚上折腾了一下,让AI修改/完善了相关功能:(还可以加手机短信预警但要收费)

应该是可以生产用了,经测试所有环节都跑通了。


每日10点邮件通知:



测试超流自动关机并邮件通知:



测试次月自动开机并邮件通知:



以下是正文(内容有点长,自己按需修改)

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 主题(邮件通道)



  1. SNS → 进入

  2. 左侧 主题创建主题

  3. 类型:标准

  4. 名称LightsailTrafficAlert

  5. 其他默认 → 创建主题

  6. 进入该主题 → 创建订阅

    • 协议:电子邮件

    • 终端节点:填你的邮箱



  7. 去邮箱点击确认链接(不点收不到任何邮件


📌 创建后复制页面上的 主题 ARN,长得像 arn:aws:sns:ap-northeast-1:123456789012:LightsailTrafficAlert,后续要用




四、Step 2 — 创建 Lambda ①(流量监控 + 自动关机)


4.1 建函数



  1. Lambda创建函数

  2. 从头开始创作

  3. 函数名称:LightsailMonitor

  4. 运行时:Python 3.12

  5. 权限:选"创建新的执行角色"(向导自动建一个基础角色,下一步我们会替换为最小权限)

  6. 创建函数


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']) # 1024
WARN_80 = float(os.environ.get('WARN_80', 80))
THRESH_GB = float(os.environ['THRESHOLD_GB']) # 920
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: # <80%
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 调整为最小权限(替换向导自动给的角色)



  1. 配置 → 权限,点击蓝色角色名链接 → 跳转到 IAM

  2. 在 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')}")

# 只有北京时间的 1 号才执行开机
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', [])}

# 只处理带 TrafficShutdown=true 的机器
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) # 官方 boto3 API
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_NAMEREGION_NAMESNS_TOPIC_ARNTAG_KEYTAG_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"
}
]
}

StartInstanceTagResource 都是 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 验证关机逻辑



  1. 临时把 THRESHOLD_GB 改为 1

  2. 部署后测试 Patrol-Empty

  3. 预期:日志显示关机 → Lightsail 控制台 aws-jp 状态变 Stopped → 邮箱收到"流量超限关机"邮件

  4. 测试完必须改回 920 并部署,然后去 Lightsail 控制台手动开机


7.4 验证开机逻辑



  1. 手动停止 aws-jp

  2. aws-jp 打 tag:TrafficShutdown = true

  3. Lambda → LightsailMonthlyBoot → 测试 {}

  4. 预期:实例开机 → tag 变为 false → 邮箱收到开机通知

  5. 测试完代码无需改动(生产代码本就是 if bj.day != 1:




八、上线后日常运维


8.1 每周巡检



  • 邮箱确认日报正常到达

  • Lightsail 控制台确认 TrafficShutdown 标签为 false


8.2 调整阈值(如改 80% 为 70%)


Lambda ① → 配置 → 环境变量 → 改 WARN_80=70 → 保存 → 部署


8.3 暂停/恢复通知


EventBridge 调度器 → 对应规则 → 禁用/启用


8.4 完全卸载



  1. EventBridge 调度器:删除 4 个计划

  2. Lambda:删除 2 个函数

  3. IAM → 角色:删除 2 个 Lambda 角色

  4. SNS → 主题:删除 LightsailTrafficAlert




九、成本说明


整套方案完全在 AWS 免费额度内:



  • Lambda:每月调用约 3000 次,远低于 100 万次免费额度

  • EventBridge Scheduler:调度次数免费额度充足

  • SNS:每月 1000 封免费邮件

  • CloudWatch 日志:免费额度内


实际只产生 Lightsail 5 美元套餐本身的费用,自动化系统本身 0 成本。




最新回复 (11)
  • bffb42 07-22 11:19
    1

    看完了觉得大佬牛皮,但是我选择用更简单的探针提醒 ^-^

  • wikWik 07-22 11:20
    2

    woc了 感谢🙏

  • seeworld 07-22 11:20
    3

    建议楼主搞个一键脚本,这样设置太累了!

  • isyv 07-22 11:21
    4

    东西是好东西但正常用也很难用的了这么多流量吧 ^-^

  • exit 楼主 07-22 11:21
    5

    @bffb42 #1 不,是AI牛皮。

    探针是在服务器上,这个用的是aws官方接口更准吧。DD了也不用重新搞

  • pan260 07-22 11:22
    6

    大佬牛皮

  • exit 楼主 07-22 11:25
    7

    @wikWik #2 @pan260 #6 哈哈,要感谢AI。


    @seeworld #3 好像没有脚本吧。啊


    @isyv #4 一般是用不了,但不怕一万就怕万一。至少超流自动关机蛮稳滴

  • taiguguai 07-22 11:30
    8

    @bffb42 #1 哈哈哈 我会你更简单,我选择探针。。我自己盯着。。。

  • exit 楼主 07-22 11:38
    9

    @taiguguai #8 还得靠自己 ^-^

  • Oldli031 07-22 17:38
    10

    bd

  • stevengao 07-22 19:03
    11

    mark一下

* 帖子来源NodeSeek
返回