文章总结: 本文针对红队基础设施中抢占式实例易被回收的问题,提出一种通用自动监控与恢复方案。通过定期TCP探测SSH端口判断实例状态,避免依赖云厂商API,并利用Terraform声明式特性自动补齐实例。实践表明该方案能在5分钟内完成恢复,有效保障了业务连续性。 综合评分: 93 文章分类: 红队,实战经验,安全建设,云安全,安全开发
抢占式实例的自动监控与恢复
原创
r0fus0d r0fus0d
ffffffff0x
2026年3月12日 00:46 北京
这个问题的背景
搞红队基础设施的都知道,云上资源开销是个绕不过去的问题。一个代理池场景随随便便就是好几台 ECS,按量计费一天下来费用不低。后来我们在 RedC 里把阿里云和 AWS 的代理场景全部切到了抢占式实例(Spot Instance),成本直接砍掉 60%~90%,效果立竿见影。
但抢占式实例有个众所周知的问题:它随时可能被云厂商回收。
实际遇到
之前开了个某个云的代理池场景,几十台抢占式 ECS,跑着跑着其中部分被回收了。直到发现代理可用率掉了一半才上控制台看——原来是被释放了。
这就引出了两个很现实的问题:
- 怎么知道实例被回收了? 云厂商的中断通知机制各不相同,有的要接 API,有的要轮询元数据服务,而且只能在实例内部获取。对于一个管理多个场景、跨多个云厂商的工具来说,逐一对接太碎片化了。
- 知道了之后怎么办? 手动上去重新开?那还不如用按量的。能不能自动把被回收的实例补回来?
思路:不依赖云厂商通知,从外部探测
一开始想的是去对接各个云厂商的中断通知 API,但想了想放弃了。原因:
- 阿里云的抢占式实例中断事件可以通过 CloudMonitor 事件订阅拿到,但需要额外配置事件规则、回调地址
- AWS 可以用 EventBridge 监听 Spot 中断警告,也挺麻烦
- 腾讯云、火山引擎各有各的接口
这些方案对一个本地运行的 GUI 工具来说,太重了。
换个角度想——抢占式实例被回收之后,最直观的表现是什么?就是机器没了,连不上了。
所以最终选了一个最简单粗暴的方案:定期 TCP 探测 SSH 端口。每隔一段时间,对所有运行中的 Spot 场景的公网 IP 做一次 22 端口探测。连不上,就说明实例大概率已经被回收了。
这个方案的好处是:
- 不依赖任何云厂商的特定 API
- 不需要在实例内部装 agent
- 对 RedC 已有的场景管理结构完全兼容
坏处也有——不够实时,探测间隔决定了你最晚多久能发现。不过对我们的场景来说,3 分钟的延迟完全可以接受。
具体怎么做的
[整体架构]
监控模块作为一个后台常驻 goroutine 运行,启动后按固定间隔执行扫描。流程大概是这样:
启动监控 → 等待 60s(让应用初始化完)→ 每 3 分钟扫描一轮
↓
遍历所有运行中的场景
↓
检查 .tf 文件是否包含 spot 相关配置(spot_strategy / market_type / is_spot_instance)
↓
从 terraform output 中提取公网 IP
↓
对每个 IP 做 TCP :22 探测(3 次重试,每次超时 10s,间隔 15s)
↓
连不上 → 标记为已回收 → 发送通知 → 触发自动恢复
[怎么判断一个场景是 Spot 实例]
不是所有场景都用了抢占式实例,所以第一步是识别。做法很简单——扫描场景目录下的 .tf 文件,看有没有这几个关键字:
spotPatterns := []string{
`spot_strategy`, // 阿里云
`market_type`, // AWS
`is_spot_instance`, // 腾讯云
}
阿里云的 Spot 实例在 Terraform 里长这样:
resource "alicloud_instance" "instance" {
instance_type = "ecs.n1.tiny"
spot_strategy = "SpotWithPriceLimit"
// ...
}
AWS 的则是:
resource "aws_instance" "pte_node" {
instance_type = "t4g.nano"
instance_market_options {
market_type = "spot"
}
// ...
}
通过静态文本匹配就能判断,不需要调用云 API。
[SSH 探测的细节]
探测逻辑做了一些容错处理:
func (m *SpotMonitor) probeSSH(ip string) bool {
addr := net.JoinHostPort(ip, "22")
for attempt := 0; attempt < 3; attempt++ {
conn, err := net.DialTimeout("tcp", addr, 10*time.Second)
if err == nil {
conn.Close()
return true
}
if attempt < 2 {
select {
case <-time.After(15 * time.Second):
case <-m.stopCh:
return true // 正在停止,避免误报
}
}
}
return false
}
一次连不上不算数,连续 3 次都连不上才判定为回收。每次超时 10 秒,重试间隔 15 秒。这样单个 IP 的判定周期大约是 1 分钟,能有效过滤掉网络抖动导致的误报。
另外一个细节是 alerted 机制——已经报过警的 IP 不会重复报。用 caseID:ip 作为 key 记录,避免每一轮扫描都重复告警同一个已挂的实例。
[自动恢复]
检测到实例被回收后,如果用户开启了自动恢复选项,会触发 Terraform 的 plan + apply 操作。Terraform 的声明式特性在这里很好用——你声明了要 2 台实例,现在只剩 1 台,apply 一下自然会把缺的那台补回来。
但这里踩了一个坑。
RedC 里,场景启动后状态是 running,而 TfApply() 方法内部有一个状态检查:如果当前状态已经是 running,会直接拒绝执行并返回错误”场景正在运行中”。这个检查本来是防止用户误操作的,但在自动恢复场景下就有点碍事。
解法是绕过上层封装,直接调用底层的 TfPlan() 和 TfApply() 函数:
func (m *SpotMonitor) attemptRecover(c *redc.Case, downIPs []string) {
// 直接调用底层函数,绕过 Case.TfApply() 的状态检查
if err := redc.TfPlan(c.Path, c.Parameter...); err != nil {
// 恢复失败,发通知
return
}
if err := redc.TfApply(c.Path, c.Parameter...); err != nil {
// 恢复失败,发通知
return
}
// 如果之前标记了 terminated,改回 running
if c.State == redc.StateTerminated {
c.StatusChange(redc.StateRunning)
}
// 清除该场景的告警记录,让新 IP 可以被监控
m.ResetAlert(c.Id)
}
恢复成功后,还要做一件事:清除该场景的告警记录(ResetAlert)。因为恢复后实例的 IP 会变,旧 IP 的告警记录已经没意义了,而新 IP 需要能被正常监控。
实现效果如下
开启 spot 实例监控
开个场景,验证一下,这里 一台 214,一台 222
手动把 214 释放掉
过一会,会自动把 214 那台重新开启,222 未受到影响,场景恢复
[通知集成]
- 系统通知:macOS / Windows / Linux 原生桌面通知,适合人在电脑前的场景
- Webhook 推送:支持 Slack、钉钉、飞书、Discord、企业微信,适合团队协作或不在电脑前的场景
通知内容包括场景名、被回收的 IP、是否全部回收、自动恢复结果等。
[做成可选项]
因为 Spot 监控需要持续做 TCP 探测,对于不用抢占式实例的用户来说是没必要的开销。所以把它做成了设置页面里的一个开关,默认关闭,用的时候再打开。自动恢复也是单独的开关,可以只开监控不开恢复。
配置存在本地的 gui_settings.json 里,重启不丢失。
实际效果
目前这套方案,有几个实际的体验:
- 阿里云的抢占式 ECS 被回收后,大约 3 分钟内能检测到并触发恢复,新实例通常在 2 分钟内就起来了。整个中断窗口大约 5 分钟。
- AWS 的 Spot Instance 同理,探测 + 恢复的总时间差不多。
- 代理池场景里如果 2 台实例只回收了 1 台,Terraform apply 只会补那 1 台,不会影响另一台正在运行的实例,代理可用率不会归零。
对于红队基础设施的场景来说,这个恢复速度基本够用。
当然如果场景可用性要求特别高(比如不能断超过 1 分钟),那还是老老实用用按量实例吧。
小结
- 不去对接各云厂商的中断通知,用 TCP 端口探测作为统一的健康检查手段
- 利用 Terraform 的声明式特性,apply 一下就能把被关闭的实例补回来
- 通知做多层覆盖,确保不会错过
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:ffffffff0x r0fus0d r0fus0d《抢占式实例的自动监控与恢复》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。











评论