中午吃饭前网络稍有卡顿,没有在意,以为是偶发的临时情况,就吃饭了。
下午一点半左右吃饭回来,发现梯子掉了,服务全挂(AxonHub掉了,导致AI也用不了)。以为是线路问题,想着稍等一会肯定就好了。最终还是只能再次使出丝滑小连招:1、用ikuuu免费机场。2、本地跑AxonHub。算是有的用了。
到了晚上看到还是不可用状态,决定处理一下。让 ds4p 和 gpt5.5 分别作为 Builder 和 Reviewer 排查问题。经过多轮beat,最终有了本文。
但是最终其实并没有定位到具体问题,只能归因于RackNerd本身 hypervisor层的问题。还是直接在 RackNerd 后台直接reboot解决问题。之前确实没有遇到过此类问题,所以做个记录。
Quick Known
先用 ssh -B en0 拿到不受本地 TUN 污染的观测路径,再看 uptime 判断机器是否刚重启过,然后读 journalctl -b -1 收敛到 DNS / lookup 错误;如果没有 OOM killer / Kernel panic 证据,就不要继续猜重启机制。
现象
- 本地直接执行
ssh luck@142.171.154.61失败,表现为Connection closed by remote host。 - 本地如果开启 Clash TUN,几乎整机没有网络;关闭后,直连网络部分恢复。
- 远端 VPS 上的
sing-box用户侧表现异常,代理链路不可用。 - 后续通过
ssh -B en0 luck@142.171.154.61成功登录,发现远端uptime只有几分钟,说明机器刚刚重新启动过。
最终站得住的结论
这次排查结束后,只有下面三条结论可以当成已证实结论保留:
- 本地 TUN 污染了 SSH 观测路径,默认
ssh走的是本地代理链路,不是干净直连。 - 远端存在 DNS / 解析层异常,同时打坏了两条链路:
Caddy -> axonhubsing-box -> REALITY handshake dest (www.bing.com)
- 远端在持续内存压力背景下发生了一次异常重启,但重启机制未证实。
同样需要明确保留的是否定结论:
- 没有证据证明这次是
OOM killer。 - 没有证据证明这次是
kernel panic。 - 没有证据证明这次是
systemd watchdog触发的重启。
相关配置锚点
下次需要从配置层继续看时,优先从下面几个位置回到代码:
modules/nixos/vps/singbox-server.nix:12handshakeServer = "www.bing.com"
hosts/nixos-vps/default.nix:62services.resolved.settings.Resolve.FallbackDNS = nameservers
hosts/nixos-vps/default.nix:78modules.systemd.manager.watchdog.enable = true
快速排查流程
下次再遇到类似问题,优先按下面顺序排查,不要一上来就改配置或重启服务。
1. 先确认本地是不是被 TUN 污染了
先判断默认 SSH 路径是不是被本地代理链路拦截。
route -n get 142.171.154.61
ssh -vvv -o ConnectTimeout=10 luck@142.171.154.61
如果看到目标 IP 走的是 utun*,或者默认 ssh 的现象明显像是“本地代理立即断开”,就不要再拿默认 ssh 的结果当远端真相。
这一步的核心不是修本地网络,而是先把“观测路径是否被污染”这个问题分离出来。
2. 用物理网卡绕过本地 TUN,拿到干净的远端现象
在 macOS 上直接绑定物理网卡,例如:
ssh -B en0 -o ConnectTimeout=10 luck@142.171.154.61
ssh -B en0 luck@142.171.154.61 "hostname; uptime; whoami"
如果 ssh -B en0 能登录,而默认 ssh 不能,优先判定为本地代理链路问题,而不是远端 SSH 本身问题。
这次事故里,ssh -B en0 成功拿到了远端:
hostnameuptimewhoami
并确认机器刚刚重启恢复。
3. 拿远端服务状态,不要先猜是 sing-box 还是线路
登录远端后先看最小状态面:
ssh -B en0 luck@142.171.154.61 \
"systemctl is-active sing-box tailscaled systemd-resolved; \
ss -tlnp | grep -E '22|80|443|8443|9443|11443|8500|10443' || true"
目标不是立刻下结论,而是回答三个问题:
sing-box进程在不在tailscaled在不在- 关键监听端口在不在
如果服务都在,但用户面仍然坏,就继续查上一轮 boot 的日志,不要把“服务 active”误判成“链路健康”。
4. 直接看上一轮 boot 的故障窗口
优先读上一轮 boot 的用户态日志:
ssh -B en0 luck@142.171.154.61 \
"journalctl -b -1 -u sing-box -u tailscaled -u systemd-resolved --no-pager | tail -n 200"
这一步重点找三类线索:
sing-box是否有lookup/TLS handshake/REALITY相关错误tailscaled是否有Failed with result 'exit-code'或wgengine watchdog timeoutsystemd-resolved是否有Under memory pressure
这次事故里,真正有价值的日志有三条:
Caddy从14:07开始出现lookup axonhub: i/o timeoutsing-box在16:18明确报lookup www.bing.com: context deadline exceededtailscaled.service在16:18:29退出
5. 查内核级证据,但不要把“没有日志”自动解释成某一种根因
继续看上一轮 boot 的内核日志:
ssh -tt -B en0 luck@142.171.154.61 \
"journalctl -k -b -1 --no-pager | tail -80"
注意:
-tt需要真实终端。- 如果是在非交互环境、脚本或 CI 里执行,这里改用
ssh -T或直接去掉-tt。
以及必要时做关键词过滤:
ssh -B en0 luck@142.171.154.61 \
"journalctl -b -1 --no-pager | rg 'oom-killer|Out of memory|Killed process|Kernel panic|soft lockup|watchdog|BUG:' || true"
解释原则要保守:
- 如果有
OOM killer/Killed process,才能说有 OOM 证据。 - 如果有
Kernel panic/BUG:/soft lockup,才能说有内核级崩溃证据。 - 如果这些都没有,只能说“重启机制未证实”,不能反向推出某个你更喜欢的解释。
6. 额外确认 DNS 绑定关系
如果怀疑 tailscale0 把解析链路带偏,查:
ssh -B en0 luck@142.171.154.61 "resolvectl status tailscale0"
这次事故里可以确认:
tailscale0挂了100.100.100.100systemd-resolved在故障窗口里确实持续报Under memory pressure
但这仍然不能单独证明“全部 DNS 故障都由 tailscaled 首先引起”。
事件时间线
下面这张表比按类型列证据更适合快速重建事件链。
| 时间 | 事件 |
|---|---|
14:05:26 | systemd-resolved 开始对 100.100.100.100 报 Using degraded feature set |
14:07:40 | Caddy 首次出现 lookup axonhub: i/o timeout |
16:18:05 | sing-box 开始明确报 lookup www.bing.com: context deadline exceeded |
16:18:29 | tailscaled.service 退出,Failed with result 'exit-code' |
17:35:00 | systemd-resolved 首次出现 Under memory pressure |
17:35 之后 | Under memory pressure 持续反复出现,说明系统长时间处于内存压力状态 |
21:34 左右 | 新 boot 开始,说明上一轮运行已经异常结束并重新启动 |
对这张表的解释要保持克制:
- 它能证明故障链是先出现 DNS / 解析层异常,再出现更明显的服务退化与内存压力。
- 它不能单独证明最终重启机制。
关键证据
| 证据 | 现象 | 能支撑什么 |
|---|---|---|
route -n get 142.171.154.61 指向 utun* | 默认 SSH 经过本地 TUN | 默认 ssh 结果被本地代理链路污染 |
ssh -B en0 可登录 | 绕过本地 TUN 后直连远端成功 | 远端并非完全不可达 |
uptime 只有几分钟 | 远端刚刚启动 | 远端此前发生过一次异常重启 |
lookup axonhub: i/o timeout 从 14:07 开始出现 | Caddy -> axonhub 解析失败 | 远端 DNS / 解析层异常至少从 14:07 已存在 |
lookup www.bing.com: context deadline exceeded | sing-box REALITY 握手目标解析失败;对应配置见 modules/nixos/vps/singbox-server.nix:12 | sing-box 明确被 DNS 异常拖坏 |
tailscaled.service: Failed with result 'exit-code' 出现在 16:18:29 | tailscaled 的确在故障窗口里退出 | tailscaled 是加剧因素候选,但不能直接定成首因 |
systemd-resolved: Under memory pressure 大量出现 | 系统处于持续内存压力;fallback 配置在 hosts/nixos-vps/default.nix:62 | 内存压力被证实存在,但 fallback DNS 本身并未阻止这次故障 |
无 OOM killer / Kernel panic / soft lockup 日志 | 没有直接内核级崩溃证据 | 不能把重启直接定成 OOM 或 panic |
当前 boot 报 Failed to open any watchdog device,且无 /dev/watchdog* | 底层没有可用 watchdog 设备;watchdog 配置在 hosts/nixos-vps/default.nix:78 | 不能把 systemd watchdog 当成这次重启的高可信主结论 |
这次最重要的误判,以及 reviewer 怎么纠偏
这部分比原始时间线更重要,因为它决定下次排障时会不会再走错路。
误判 1:把 tailscaled 退出当成首因
错误写法:
tailscaled崩溃 -> DNS 雪崩 -> 其他服务一起坏
为什么不成立:
Caddy对axonhub的lookup ... i/o timeout从14:07就开始了tailscaled.service: Failed with result 'exit-code'是16:18:29
修正后结论:
tailscaled退出是已证实事件- 但它最多是加剧因素,不能被直接认定为故障起点
误判 2:把 memory pressure 直接等同于 OOM / panic
错误写法:
systemd-resolved和journald报 memory pressure,所以最后一定是 OOM 或 kernel panic
为什么不成立:
memory pressure只证明系统很吃紧- 但没有看到
OOM killer、Killed process、Kernel panic、soft lockup
修正后结论:
- 只能说机器在持续内存压力背景下发生了异常重启
- 不能把重启机制写死成 OOM 或 panic
误判 3:把 watchdog 配置存在等同于 watchdog 机制实际生效
错误写法:
仓库里启用了
modules.systemd.manager.watchdog.enable = true,所以这次很可能是 watchdog 重启
为什么不成立:
modules.systemd.manager.watchdog.enable = true只是要求 PID 1 在有设备时使用 watchdog- 当前系统明确报
Failed to open any watchdog device - 当前系统也没有
/dev/watchdog或/dev/watchdog0
修正后结论:
- 这是一条已写入配置但底层并未实际生效的机制
- 不能把它当成此次重启的核心解释
下次再遇到时的决策树
情况 1:默认 ssh 不通,但 ssh -B en0 能通
优先处理本地观测路径污染:
- 查本地
utun* - 查本地 Clash / sing-box / TUN 路由
- 默认
ssh的结论全部降权
情况 2:ssh -B en0 也不通,但 22 端口可建立 TCP
优先怀疑远端 SSH 或系统状态异常:
- 看
uptime - 看
systemctl is-active sshd sing-box tailscaled - 看上一轮 boot 的日志
情况 3:远端服务都 active,但用户面仍然坏
优先看解析层:
journalctl -b -1 -u sing-box -u tailscaled -u systemd-resolvedresolvectl status- 查是否存在
lookup ... timeout或context deadline exceeded
情况 4:日志里只有 memory pressure,没 OOM / panic
不要继续猜。
直接把结论写成:
- 系统处于内存压力下
- 发生了一次异常重启
- 重启机制未证实
然后把下一步转给 provider / hypervisor 侧证据。
仍未解决的问题
到这一步为止,下面这些问题依然是 unresolved:
- 远端最终为什么重启
- 这次异常重启是不是 RackNerd DC02 宿主机层问题
- 5/22 迁移到 DC03 后是否还会复现
后续建议
1. 下次优先拿 provider / hypervisor 层证据
用户态和内核态日志都已经不够继续推进时,不要再在机器里兜圈子,优先查:
- RackNerd 面板事件
- hypervisor / power 侧日志
- 控制台是否有异常 reset 记录
2. 迁移后重点观察是否复现
如果迁移到 DC03 后问题自然消失,这会显著提高“DC02 宿主机或网络环境异常”的解释权重。
如果迁移后仍复现,则更应回到本机解析层、tailscale0、systemd-resolved 和 sing-box 配置本身继续查。
3. 长期上可考虑减少 sing-box 对外部运行时 DNS 的脆弱依赖
这不是本次事故里已经验证过的 fix,但它是合理的后续优化方向。例如:
- 评估是否把
handshakeServer从单个域名改成更稳的预解析目标方案 - 评估是否给
sing-box相关出站或握手路径使用更独立的 DNS 解析策略,减少对系统公共解析链路的耦合
一句话版 SOP
下次再遇到“VPS 上 sing-box 不可用 + SSH 现象诡异 + 本地开 TUN 后整机几乎断网”时,不要先改配置,也不要先重启远端服务;先用 ssh -B en0 拿干净观测路径,再按 上一轮 boot 日志 -> DNS / lookup 错误 -> 内核级证据 -> provider 侧事件 这条链路收敛问题。