AWS 如何利用 EventBridge 和 Lambda 实现 EC2 自动定时关机 (基于标签)

作为一名开发者,最心痛的时刻莫过于月底收到 AWS 账单时,发现几台测试用的 EC2 实例在下班后和周末一直开着空跑。

为了省钱,我们通常希望实现:“每天下班后(如 18:00)和凌晨(如 02:00)自动关机”

虽然 EventBridge Scheduler 可以直接调用 EC2 关机接口,但它要求硬编码 Instance ID。一旦你新增了服务器,又得去改计划,非常麻烦。本文将介绍一种更优雅、更具扩展性的方案:利用标签 (Tag) 管理实例,配合 Lambda 脚本实现自动化。

方案架构

  1. EventBridge Scheduler: 也就是“闹钟”,负责在指定时间(支持本地时区)触发任务。
  2. AWS Lambda: 也就是“执行者”,运行一段 Python 脚本,查找带有特定标签的实例并关闭它们。
  3. EC2 Tags: 给需要管理的机器打上标签(例如 AutoStop: true),实现“即打即用”。

第一步:创建 Lambda 函数 (执行大脑)

我们需要一个 Python 脚本来扫描并关闭服务器。

  1. 登录 AWS 控制台,进入 Lambda 服务。
  2. 点击 Create function
  3. Name: 输入 StopEC2ByTag
  4. Runtime: 选择 Python 3.9 (或更新版本)。
  5. 点击 Create function

1. 编写代码

Code source 区域,打开 lambda_function.py,粘贴以下代码:

import boto3

# --- 配置区域 ---
# 设置你要查找的标签 Key 和 Value
# 只有打上这个标签的机器才会被关机
TAG_KEY = 'AutoStop'
TAG_VALUE = 'true'

# 设置地区 (例如东京是 ap-northeast-1)
REGION = 'ap-northeast-1' 
# ----------------

def lambda_handler(event, context):
    ec2 = boto3.client('ec2', region_name=REGION)
    
    print(f"任务开始:正在查找标签为 {TAG_KEY}={TAG_VALUE} 的运行中实例...")
    
    # 1. 根据标签查找所有 "running" 状态的实例
    response = ec2.describe_instances(
        Filters=[
            {'Name': f'tag:{TAG_KEY}', 'Values': [TAG_VALUE]},
            {'Name': 'instance-state-name', 'Values': ['running']}
        ]
    )
    
    instance_ids = []
    for reservation in response['Reservations']:
        for instance in reservation['Instances']:
            instance_ids.append(instance['InstanceId'])
            
    # 2. 执行关机操作
    if len(instance_ids) > 0:
        print(f"发现目标实例,准备关闭: {instance_ids}")
        ec2.stop_instances(InstanceIds=instance_ids)
        return f"成功关闭了 {len(instance_ids)} 台实例"
    else:
        print("未发现符合条件的运行中实例,无需操作。")
        return "没有实例被关闭"

点击 Deploy 保存。

2. 赋予权限 (关键步骤)

默认情况下,Lambda 没有权限操作 EC2。

  1. 点击 Configuration (配置) -> Permissions (权限)
  2. 点击 Execution role 下方的角色名称(跳转到 IAM)。
  3. 点击 Add permissions -> Attach policies
  4. 搜索 EC2Full,勾选 AmazonEC2FullAccess 并添加。
    • 注:生产环境建议配置更精细的 Policy(仅允许 DescribeInstances 和 StopInstances),但在个人开发环境,FullAccess 最省事。

第二步:配置 EventBridge Scheduler (定时闹钟)

AWS 新出的 Scheduler 支持时区设置,我们再也不用痛苦地换算 UTC 时间了。

  1. 进入 EventBridge 服务,点击左侧的 Schedules
  2. 点击 Create schedule

1. 设置时间表

  • Schedule name: Daily-EC2-AutoStop
  • Schedule pattern: 选择 Recurring schedule (重复计划)。
  • Schedule type: 选择 Cron-based schedule
  • Timezone (重点): 下拉选择 Asia/Tokyo (或者你所在的 Asia/Shanghai)。
    • 这样上面的 2 和 18 就是指本地时间。

Cron expression:

0 2,18 * * ? *

(这代表在 2点 和 18点 的 0分触发)

2. 选择目标

  • Target API: 选择 AWS Lambda -> Invoke
  • Function: 选择刚才创建的 StopEC2ByTag
  • 一直点击 Next 直到完成创建。

第三步:测试与验证

现在配置已经完成,如何验证它是否有效?

  1. 给实例打标签:去 EC2 控制台,找一台测试机,添加标签 Key: AutoStop, Value: true
  2. 手动测试 Lambda:在 Lambda 页面点击 Test 按钮。
    • 如果代码显示 成功关闭了 1 台实例,且 EC2 真的关机了,说明脚本和权限没问题。
  3. 等待定时触发:到了设定的时间(18:00 或 02:00),去 Lambda -> Monitor -> View CloudWatch logs 查看日志。
    • 如果看到自动触发的日志,说明计划任务配置成功。

常见问题 (FAQ)

Q: 这个方案收费吗?

几乎完全免费。

  • EventBridge Scheduler: 每月前 1400 万次触发免费。
  • Lambda: 每月前 100 万次请求免费。
  • 你的用量(每天2次 = 每月60次)连免费额度的零头都不到。

Q: 为什么不直接用 Scheduler 里的 "EC2 StopInstances"?

Scheduler 原生支持的 StopInstances 需要你填入具体的 Instance ID(如 i-0123456)。
这意味着如果你新开了一台服务器,你必须记得回来修改定时计划,非常容易忘。
使用 Lambda + 标签 的方案,你只需要给新机器打个标签,它就会自动进入管理队列,实现“一劳永逸”。

Q: 我想加个早上自动开机怎么办?

原理一样!

  1. 复制一份 Lambda 代码,把 ec2.stop_instances 改成 ec2.start_instances,查找状态由 'running' 改为 'stopped'
  2. 在 Scheduler 里新建一个计划(比如早上 9 点),调用这个开机 Lambda 即可。