Spot EC2收到2分钟终止预警时如何通知SQS轮询停止新任务调度?
Spot/ASG缩容场景下EC2拦截SQS新任务拉取的实现方案
核心实现逻辑
整个流程不需要改动SQS队列本身的配置,所有拦截动作都在EC2本地的轮询程序层完成,不会影响其他正常实例的任务消费:
- 在EC2本地维护一个全局的
is_terminating布尔标记,默认值为false - 所有来自AWS的终止信号被捕获后,第一时间把这个标记置为
true - 轮询程序在每次发起SQS拉取请求前,先检查这个标记状态:如果为
true,立刻停止调用SQS的ReceiveMessage接口,不再拉取新任务
第一步:全覆盖捕获所有终止触发信号
要避免漏接终止通知,需要覆盖所有可能触发实例终止的信号源:
- Spot实例的提前终止通知:在实例上运行一个轻量后台进程,按固定间隔(比如5秒)请求本地实例元数据接口
http://169.254.169.254/latest/meta-data/spot/termination-time,一旦接口返回200状态码(代表实例会在2分钟后被终止),立刻触发状态标记变更。 - Auto Scaling Group的缩容终止信号:在轮询程序里捕获系统层面的SIGTERM关机信号,不管ASG是直接发关机指令终止实例,还是通过生命周期钩子触发缩容流程,只要系统层面发出关机信号,就触发本地状态标记置位。
- 系统层面的强杀兜底:为轮询程序配置对应的systemd服务规则,设置
TimeoutStopSec为120秒,匹配Spot的2分钟通知窗口,避免系统在收到终止信号后立刻发送SIGKILL杀掉轮询进程,留足收尾时间。
第二步:收尾阶段的任务处理逻辑
状态标记置位后,除了停止拉新任务,还要处理已经在本地的任务,避免任务丢失或重复执行:
- 已经从SQS拉取、还未开始执行的待处理任务:如果剩余的终止窗口不足以跑完整个任务,不要强行执行,把这些消息对应的
VisibilityTimeout调整为大于剩余窗口的时长,等实例被终止后,SQS会自动将这些消息放回队列,由其他健康实例接手执行,不会出现任务丢单。 - 已经开始执行的任务:优先等待当前执行单元完成,除非任务预估剩余执行时长超过终止窗口,否则不要中途强制中断,避免产生脏数据。
生产环境常见踩坑提醒
- 别想着靠SQS端的过滤逻辑实现拦截,比如给每个队列消息加运行实例的标签匹配,这种做法不仅会让你每次拉取都要做额外的标签校验,增加API调用成本,还容易因为ASG的标签同步存在延迟,导致实例已经进入终止流程了,还在正常拉取任务,拦截时机完全不对。
- 别把ASG的健康检查结果作为拦截触发信号:ASG检测到实例不健康、把实例从服务集群摘除的流程存在固定延迟,等这个流程走完,Spot预留的2分钟终止窗口可能已经用掉一半,这时候再停拉新任务已经晚了,很容易在终止窗口的最后时刻拉到一个跑不完的长任务。
内容的提问来源于stack exchange,提问作者Heena Patel
相关产品推荐
相关产品推荐

