Repository navigation
Releases: spot-probe/monitor
Release list
v1.11.13
修一个会让安装脚本整个中止的错。
1.11.12 里我在 systemd unit 的注释中写了一个美元号加变量名。那个 unit 是用一个未加引号分隔符的 heredoc 写的(故意的:unit 里的 $BIN、$INTERVAL 等都要展开),而代价就是它正文里每一个美元号都是代码——包括注释里的。于是在 set -u 之下,展开一个未设置的变量直接让脚本中止。在机器上重跑安装命令时就是这样挂的:
sh: 486: STATE_DIRECTORY: parameter not set
修法是把那句注释里的美元号去掉,意思不变,只是不再像一个变量引用。同时在那段 heredoc 上方写清了这条规矩和检查办法,因为这种事不能靠"小心写":改完之后跑一遍 awk 把 heredoc 正文里的美元号列出来,确认每一个都是真想展开的。
验证没有只看仓库里的文件。我按行号取出那份 unit 正文,在 set -eu 下真展开了一遍,确认它以 0 退出、输出 49 行,并且里面确实有 StateDirectory=monitor-agent。然后把本地 hub 实际发出的 /install.sh 拉下来又做了一遍同样的检查——因为真正被执行的永远是 hub 内嵌的那一份,而不是仓库里的那一份。
如果你刚才在机器上重跑安装命令时遇到这个报错:它死在第 486 行,也就是"开始写 unit"之前,所以原来的 unit 与配置应当完好;二进制也多半没被动过。升级到本版之后重新执行那条命令即可。
这一版包含 1.11.12 的全部内容,只是修掉这个安装期错误。
v1.11.12
这一版把"IP 质量检测的许可"收口,并加上流媒体解锁检测的第一期。
许可这条链路现在真的生效了。没允许检测的节点,hub 连一次本地查询都不做;更重要的是,一个在线请求都不会发出去。在此之前,只要风险开关和 key 配好,每一台节点的 IP 都会被发给第三方,而这个开关管的正是这件事。缺失这个标记时按"允许"处理,所以老节点不会因为升级而断掉。
流媒体解锁检测第一期只做 Netflix:agent 每 6 小时自己探一次,各台按自己的时点错开,并把上次探测的时间落盘——重启不会引发探测风暴,而 agent 崩溃重启循环时这一点尤其重要。结果上报后由 hub 校验并落库,时间戳由 hub 盖:一台不被担保的机器的时钟可以错,而那个时间正是判断结论新不新的依据。总览页「IP 质量」因此多了一列「节点实测」,每行显示那一台的结果,以及"多久之前测的";没有结果时会分清是"未允许检测"、"版本不支持"还是"还没测过"——这三件事的处置完全不同。
界面上还修了两处会误导人的地方。「IP 与归属」现在显示 hub 实际查询的那个地址,此前会出现"内网 10.x 旁边写着 Los Angeles"这样的并排显示。systemd unit 加了 StateDirectory:agent 的沙箱让整个文件系统只读,没有它 agent 写不了状态文件,只能降级在内存里并警告一次。
顺序上请注意:先升级 hub 到本版,再在有需要的机器上重跑一次安装命令(为了拿到那个可写目录),之后 agent 升到 1.1.9 就能用上新的能力。新的许可勾选框要求 agent 已经是 1.1.9:旧 agent 不认识这个参数,会因未知参数启动失败。
v1.11.11
这一版加了"IP 质量检测的许可",以及配套的界面。
起因是一个要求:IP 质量的检测应当是有限度的,不该对所有节点默认打开,而要可配置。做法照搬既有的「允许远程升级」——由那台机器在安装时决定,hub 只读不改。它管住两件事:hub 要不要为这台节点做本地查询,以及要不要把它的 IP 发给在线风险库。后者是这个开关存在的主要理由:在此之前,只要风险开关与 key 配好,每一台节点的 IP 都会发出去。
agent 需要升级到 1.1.8(已发布):它认识 --allow-ip-quality 并在 hello 里如实上报,对现有节点零行为改变。hub 侧新增 allow_ip_quality 列并迁移到 20,两道门分别掐住本地查询与对外发送。兼容性上,老 agent 不上报这个字段时按"允许"处理,老节点不会断,行为与升级前一致。
界面上,纳管节点时可以勾选「允许 IP 质量检测」,默认不勾,不勾时生成的命令与从前逐字相同。IP 质量表里,"没允许检测"与"允许了但还没查到"分开显示——这两件事的处置方式完全不同,混成一句会把人引到无关的排查上。
请注意:新的勾选框要求目标机器上的 agent 已经是 1.1.8。旧的 agent 不认识这个参数,会因未知参数启动失败。所以请先把节点升到 1.1.8,再使用这个勾选框。
这一版还包含总览页「IP 质量」视图的几处改动:四个复合列(节点、IP 与归属、地理位置、安全态势)、国旗、风险三档配色、Tor 只在"是"时高亮、筛选(只看有数据加搜索,并写明隐藏了几台),以及把已经失效的坐标域名换成 OpenStreetMap。
agent 需要 1.1.8 才能使用新的许可勾选框。
v1.11.10
这一版几乎全是界面,改动集中在总览页新加的那一栏「IP 质量」。
那一栏现在是一张能横向比较的表,四个复合列:节点、IP 与归属、地理位置、安全态势。每格上下两行——IP 下面写 ASN 与组织,城市下面写时区与坐标(坐标可以点开地图),风险徽标下面写 Tor 出口。
没有数据的格子不再是"未知"两个字,而是一个很淡的破折号,并且在节点名下面直接写出"暂无解析数据",不用把鼠标停上去才知道。
风险按低、较高、高三档配色。档位文字来自 hub,阈值只写在 hub 一处,面板不再自己判一遍。Tor 出口只在"是"时高亮,"否"很淡,不知道就写"不知道"——这是三种情况,混在一起就会让人以为其中一个错了。表下补了一句声明:档位文字由本面板给出,各家口径不同;四列数据来自三条不同的路,隐私与开关行为都不一样。
还加了筛选:一个「只看有数据」开关和一个搜索框,名称、IP、ASN、城市都能搜。筛选隐藏了内容时,表下会写明隐藏了几台,并给一个显示全部——不说的话,"3 台"看起来就像整个机队只有 3 台。
另外修了一个链接。坐标原本指向的域名如今会跳到 GitHub 上的一个 shell 脚本,已换成 OpenStreetMap 的永久链接。这个地址在表格与节点编辑框里各有一份,两处一起改了。
只有面板改动,agent 不用升级,也不需要重新签名。
v1.11.9
这次主要是界面。
总览页的「视图」多了一栏「IP 质量」,把节点质量从"逐台点开编辑框"搬到一个能横向比较的地方。一张表看完节点、IP、ASN 与组织、位置、坐标(可点开地图)、时区、在线风险和 Tor 出口。表里空着的格子一律写"未知",不留白——留白会被读成"没问题",那是两件事。
IP 数据库那一节的表单重排了。每一行现在是标题在左、状态在右,下面是整行输入框,说明与填入链接在底部;行与行之间有分隔线。底部那段长篇说明收进一个浅色说明槽,从一整段灰字变成四条能扫完的规则。页脚那个"填入示例地址"去掉了,因为每一行都有了自己的填入链接。
状态不再是"配了就亮"。hub 会报出每个库文件的大小、多久之前拉的,以及够不够新,所以徽标是三态:呼吸中的绿点表示确实在工作,静止的灰点表示配了但还没生效,而且分"等待首次拉取"与"已过期"两种说法——后者说明它成功过,和从没成功过是两种处境。
「IP 质量」页里还留了一块「节点实测」,目前只有一句说明:流媒体检测等待 agent 支持。那一类结论只能从节点发出探测才能得到,而且带时效,所以它会单独成一块,并显示什么时候测的。现在不预先编字段——探哪些服务、多久一次都还没定,先摆格子只会得到"看起来支持、其实空着"的假样子。
只有面板与 hub 的小改动,agent 不用升级,也不需要重新签名。
v1.11.8
这次主要是三件事:把两个 IP 数据库的配置理顺,把 API Key 当凭据对待,让 Tor 出口列表真正生效。
MaxMind 和 DB-IP 以前共用同一个设置键。填了 DB-IP 的地址,MaxMind 那两个来源会跟着去下同一个文件,再当成 .tar.gz 解压,日志里留下两条看不懂的警告。现在来源收敛成三条,落盘的文件名由内容决定,填哪家的地址都能认出来,也不必再为两家各配一个键。
在线风险库的 API Key 以前会原样回显在面板上。现在只显示"已设置/未设置",并且保存时留空不会把已经存好的 key 覆盖掉。这一点比看起来重要:覆盖了不会有任何提示,只是抓取悄悄失效。
在线查询的结果现在分三种:没去查、查到、查空。"查空"会覆盖旧结论,而"没去查"(开关关着,或者请求失败)什么都不动。以前两者混在一起,一台机器换了出口之后,面板上显示的还是上一个地址的风险分。
Tor 出口列表以前只是下载下来放着,没人读。现在 hub 会用它做本地判断:不外发节点地址,不需要开关,也不用往数据库里加列。
界面上,「通知」改成了「通用」,页内分成「通知」与「IP 信息」两个页签,后者放 IP 风险库和 IP 数据库两节。几个页面上的分段控件也统一了样式,字号收小了一点。
只有 hub 和面板改动,agent 不用升级,也不需要重新签名。
v1.11.7
清掉三笔已知项,其中第一笔会让你看到假信息
一、一个键、一个来源(MaxMind 与 DB-IP 不再抢同一个设置键)
此前两家共用 geo_city_url / geo_asn_url:你填 DB-IP 的 .mmdb.gz,
MaxMind 那两个来源也去下同一个文件,再把它当 .tar.gz 解 ——
日志里于是出现两条 cannot unpack 的 WARN。
无害(DB-IP 照常成功),但它把"配置正确"与"配置错误"变得长得一样。
现在来源收敛成 三条(城市 / ASN / Tor),落盘名由内容决定:
读头部认出包装,看到 gzip 再解压出几百字节看里面是不是 tar。
⇒ 你填哪家的地址都能认出来,也不必再为两家各配一个键。
二、在线风险库的 key 按凭据处理
- hub 侧:加进
SECRETS,只报"设没设",不再把值回显给面板; - 面板侧:按密码框显示(
••••••••),并且只在输入框非空时才提交这个键 ——
否则会把已保存的凭据覆盖成空串,而且看不出来(界面无变化、抓取静默失效); - 另加一个「清除 Key」——在此之前,用户其实没有正当途径把 key 删掉。
三、在线查询的返回值做成三态(没去查 / 查到 / 查空)
原本 None 同时表示两件完全不同的事,于是"查空"也走了"什么都不写",
旧结论会一直留着:一台机器换了出口之后,面板上显示的是上一个地址的风险分。
现在:
| 情形 | 行为 |
|---|---|
| 没去查(开关关着 / 没配 key) | 什么都不动 —— 不该把上次的结果抹掉 |
| 查到了 | 落库 |
| 查空(这家不认识这个 IP) | 如实落库覆盖旧值 |
| 请求失败 | 走"没去查" —— 一次网络抖动不该丢数据 |
判断抽成纯函数 outcome,并补上它此前缺失的单测(六条断言,
其中最要紧的一条就是"失败 ≠ 查空")。
顺带修掉一处会误导人的显示
成功日志此前按标签取文件大小,路径不存在 ⇒ 打出
geo: 城市库 updated (0 bytes) —— 任何人看到都会以为下载到了空文件。
现在按真实落盘名取。
验证
本地清空数据库文件、让 hub 自己认格式跑了一遍:文件按内容落成
dbip-city-lite.mmdb / dbip-asn-lite.mmdb,两条 WARN 消失,临时文件没有残留。
只有 hub 与面板改动;agent 无需升级,也无需重新签名。
v1.11.6
修一次生产事故:hub 被 OOM killer 反复杀掉,形成崩溃循环
现场(2026-10-11)
配好 DB-IP 地址后,hub 每 ~40 秒一轮:
启动 → 六台节点连上 → `geo: 4 of 5 databases configured` → **OOM** → 重启 → 循环
A process of this unit has been killed by the OOM killer.
Consumed 556ms CPU time, 257.8M memory peak.
restart counter is at 12
两个原因叠在一起
- 内存:127 MB 的压缩包整份读进内存,再在内存里解压(解压后几百 MB)——
一次就顶穿项目自己声明的 256M 包线(install-hub.sh写死的那条); - 重启:记录"上次什么时候拉的"只存在进程内,一重启就忘 ⇒
每次重启都重新下载 ⇒ 又 OOM ⇒ 又重启 ⇒ 死循环。
顺带解开一个困惑:为什么有的节点有质量、有的没有——
geo在启动后 30 秒把进程打 OOM,只有在那之前抢到过一次查询的节点留下了数据。
修法
- 下载边收边写盘(外加 512 MB 体积上限,防止 URL 指错指向巨物);
- 解压流式落盘;校验只读尾部 128 KB;原子替换的性质不变(坏内容绝不覆盖好文件);
- 新鲜度改看文件 mtime ⇒ 重启不再重下 ⇒ 崩溃循环不可能再发生;
- 临时下载文件必删(三条失败路径与成功路径都删——否则会慢慢攒满磁盘)。
实测(不是推算)
| 峰值内存 | 结果 | |
|---|---|---|
| 修复前(生产) | 257.8 M | 顶穿 256M ⇒ OOM ⇒ 崩溃循环 |
| 修复后(本地,一次 127 MB 的完整下载) | 15.8 M | 与文件大小脱钩 |
同时补上几处"沉默"
- 挑不出可查地址时,把
ip/ipv4/ipv6三个值原样打出来; - "没配 URL" 从
debug提到info,并且每轮报一次家底(geo: N of M databases configured); - 在线质量查询有了自己的门:此前它挂在"国家那套欠账门"上,
国家一查到那道门就永久关上 ⇒ 库下好了却从来没查过(那也是一次现场 bug)。
一个可能"什么都不做"的定时任务,必须周期性地说一句"我还在、我看到的是这样"——
否则运维看到的只是一个安静的系统,而安静既可能是正常,也可能是任务早死了。
⚠️ 升级后建议做一件事
本版把内存用量降回项目声明的 256M 之内,因此不再需要为它临时放宽上限。
若你此前加过 drop-in(例如 MemoryMax=1G),验证本版正常后请删掉它,回到默认的 256M:
rm /etc/systemd/system/monitor-hub.service.d/memory.conf
systemctl daemon-reload && systemctl restart monitor-hub
systemctl show -p MemoryPeak monitor-hub # 期望远低于 256M已知、尚未修(下一版)
MaxMind 与 DB-IP 目前共用同一个设置键:填 DB-IP 的 .mmdb.gz 会让 MaxMind 那两个来源
把它当 .tar.gz 解,日志里出现两条 cannot unpack 的 WARN。无害(DB-IP 照常成功),但很误导。
只有 hub 改动;agent 无需升级,也无需重新签名。
v1.11.5
修一个现场 bug:IP 数据库下载成功,但本地质量从来没被查询过
症状
数据目录里两个 .mmdb 都好好的(127 MB + 9.6 MB),而面板上永远显示"还没查到",
日志里没有任何报错 —— 系统看起来很安静,安静得像一切正常。
根因
"要不要查质量"被挂在了国家那套欠账门上:country_owed 在节点国家已知后返回 false,
而现场六台节点早就有国家了 —— 于是那道门永久关着,查询函数一次都没被调用。
所以"库下好了"与"有人去查"从来不在同一条路径上。
修法
- 质量有了自己的门:判据与
Node::quality判"过期"是同一条(结论属于当前地址就不欠)——
一处定义、两处使用,否则"该不该查"与"算不算数"迟早分叉; - 心跳里与国家那套并行、互不依赖(国家门关了也不影响它);
- 补了一条专盯此场景的单测:
quality_is_still_owed_when_the_country_is_already_known。
另外(可观测性)
geo任务现在每轮报一次家底:geo: N of M databases configured;- "没配 URL"从
debug提到info—— 一个可能"什么都不做"的定时任务,
必须说得出来"我还在、我看到的是这样",否则运维看到的只是一个安静的系统。
⚠️ 已知、尚未修(下一版)
MaxMind 与 DB-IP 目前共用同一个设置键:填 DB-IP 的 .mmdb.gz 会让 MaxMind 那两个来源
把它当 .tar.gz 解,日志里出现两条 cannot unpack 的 WARN。
无害(DB-IP 照常成功),但很误导。修法是按内容判断格式(gzip 魔数 + tar 的 ustar 标记 + 裸 mmdb)。
只有 hub 改动;agent 无需升级,也无需重新签名。