Skip to content

About

云沙盒空闲暂停的判定与保活方案:内部活动无效、外部流量有效,实测 45 小时零冻结

Resources

Stars

22 stars

Watchers

0 watching

Forks

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 

Repository files navigation

instabox-keepalive

云沙盒空闲暂停的判定与保活方案。适用于 instabox 这类"一段时间不操作就把整台 VM 冻结"的沙盒平台。

实测结果:连续 45 小时零冻结(uptime 2 天,SUSPENDED_GAP 0 条)。


结论先行

做法 结果
沙盒内制造 CPU 负载 无效,照样被冻结
沙盒内定时网络请求 无效
从外部访问沙盒的端口映射地址 有效

判定发生在宿主机层面,沙盒内部做什么它都不看。要续命只能从外面碰它。


为什么内部方案注定失败

这不是实现细节问题,是层级问题。三条证据可以定性:

证据一:心跳在跑,机器照样被冻

沙盒内每分钟执行一次的心跳脚本从未缺席,但日志出现了断档:

04:17:44 → 04:18:49 → 04:19:54 → 04:20:59
SUSPENDED_GAP: 941s          ← 被冻结 15 分 41 秒
04:36:40 → 04:37:42 → 04:38:46

断档前后都是准点条目,说明心跳进程没死,是整台机器被冻住了。

证据二:平台自己的 agent 也被冻

沙盒内的 guest agent 每 5 分钟向宿主机上报一次(op=reclaim),在同一时间窗口也一起断档。连平台自己的组件都停了,说明冻结在 VM 层,不在应用层。

证据三:恢复时出现 VM 级动作

04:36:34  op=sync_clock       ← 重新校时
04:36:34  op=configure        ← 重新下发配置
04:36:35  op=configure_ipv6

只有 VM 挂起后恢复才会走这套流程。

三条同时成立,就可以确定:继续在沙盒内部研究"制造什么活动"是浪费时间。


agent 里的 activity 符号是误导

对 guest agent 做 strings,能看到这些符号:

internal/guestagent/activity.go
guestagent.(*Agent).activity
guestagent.activityTimeout
guestagent.ActivityProbes
guestagent.RunProbe / probeSignal / probeTimeout
probe output has no "busy"
systemctl is-active %s

看起来像是在检测 CPU busy、服务状态,很容易据此得出"造 CPU 负载就能保活"的结论。

但这是错的。 如果 CPU busy 真能续命,证据一里的 941 秒冻结就不可能发生。这些探针只是使用统计,不参与暂停决策。

教训:看到二进制里有 activity 相关符号,不等于找到了控制开关。必须用行为验证,不能靠符号推断。


快速开始

前提:拿到端口映射地址

平台通常把沙盒端口映射成 <端口>-<机器ID>.<通配域>。注意裸域往往不解析,只有子域解析到统一网关:

# 机器 ID(沙盒内执行)
cat /etc/instabox/identity | sed 's/^m_//'
# 输出形如 abc123def456ghi789jkl(25 位左右的小写字母数字)

# 验证网关
curl -sS -m 10 https://3000-<MACHINE_ID>.instabox.tech

返回 Nothing is listening on this port in the sandbox 说明网关可达、沙盒该端口空着。

这条 URL 是不需要登录态的外部探活入口,比控制台 API 容易自动化得多。

第 1 步:沙盒内起监听

curl -fsSL https://raw.githubusercontent.com/poisonhs/instabox-keepalive/main/scripts/sandbox-side.sh | sh -s -- 3000

脚本会装一个 systemd 服务,用 python3 在指定端口常驻监听。

验证:

systemctl is-active ka-http
ss -tlnp | grep ':3000'

第 2 步:外部机器装定时敲门

必须是另一台机器。沙盒自己访问自己不算外部流量,等于没做。

curl -fsSL https://raw.githubusercontent.com/poisonhs/instabox-keepalive/main/scripts/outside-side.sh | sh -s -- 3000-<MACHINE_ID>.instabox.tech

脚本装一个 5 分钟一次的 systemd timer,跑完自动判读首次结果。

第 3 步:可选加固 —— 长连接

curl -fsSL https://raw.githubusercontent.com/poisonhs/instabox-keepalive/main/scripts/hold-keepalive.py \
  -o /usr/local/bin/hold-keepalive.py
chmod +x /usr/local/bin/hold-keepalive.py
# 改文件里的 HOST 变量后再装 systemd 单元(见文件末尾注释)

与定时敲门的区别:定时任务每次新建连接、请求完就断;长连接一次 TLS 握手后复用同一 TCP,每 60 秒打一次请求,网关看到的是持续会话。

同时挂两条时无法区分哪条在起作用,验收只按整体判。


验收标准

不要凭"现在能通"下结论。 单次 200 什么都不能证明,必须等过了平台的空闲窗口再看:

指标 合格线
uptime 连续不少于 2 天
心跳日志 SUSPENDED_GAP 0 条
敲门日志 502 0 条

三条同时成立才算保活成立。


状态码读法

码 含义 动作
200 链路通,沙盒活着 正常
502 沙盒内该端口没服务 先查沙盒侧进程
429 请求太密,网关限流 间隔拉到 60 秒以上
000 DNS / 网络 / 超时 查域名拼写与 DNS

429 和 000 都不代表沙盒挂了,只有 502 指向沙盒侧。 报告问题时这点要说清楚,否则容易把限流当故障。


节奏参数

项 值 原因
定时敲门间隔 5 分钟 远低于平台空闲阈值
长连接复用节奏 60 秒 15 秒一次会吃 429
监听端口 任意空闲端口 3000 只是习惯

常见误区

误区 事实
看到二进制里有 activity 符号,就以为找到了保活开关 符号只说明有这功能,不说明它决定暂停。必须用行为验证
用 timeout 3 yes || timeout 3 yes 做兜底 timeout 超时退出码是 124(非零),会触发兜底分支,跑两遍
15 秒敲一次更保险 会吃网关 429。60 秒是安全下限
看到 429 以为沙盒挂了 只有 502 指向沙盒侧,429 是本地节奏太快
心跳脚本里加日志轮转 tail > tmp && mv 在 oneshot 里会偶发失败,直接 >> 追加即可

实测数据

部署后连续运行 45.6 小时:

指标 数值
连续运行 45.6 小时
系统 uptime 2 天 24 分
SUSPENDED_GAP 0 次
敲门成功 511 / 521
502 出现次数 0

对照组:只跑内部心跳时,停止操作 941 秒即被冻结。


常见问题

Q: 为什么不直接在沙盒里跑个 while 循环?

A: 因为冻结发生在宿主机,沙盒内部的进程连同 CPU 一起被挂起。你的循环在冻结期间根本不执行。

Q: 用沙盒自己 curl 自己的端口算吗?

A: 不算。流量没有离开机器,网关看不到。

Q: 定时任务必须放公网 VPS 吗?

A: 不必须,只要是一台常开且能访问互联网的机器即可。家里的树莓派、路由器 cron、甚至另一台云主机都行。关键是不能是沙盒自己。

Q: 平台更新后突然失效了?

A: 先看 502 是否出现。如果沙盒侧服务还在但外部访问不通,可能是网关或映射规则变了,重新确认端口映射地址。

Q: 会不会被平台判定为滥用?

A: 保持 5 分钟一次的频率、走正常 HTTPS 请求,就是普通访问。不要压到秒级,那会触发 429 甚至被当成攻击。


免责声明

本项目仅用于自己拥有或经授权管理的沙盒实例,目的是避免因平台策略导致正在运行的任务中断。请遵守所用平台的服务条款。


License

MIT

About

云沙盒空闲暂停的判定与保活方案:内部活动无效、外部流量有效,实测 45 小时零冻结

Resources

Stars

22 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages