Skip to main content

RackNerd VPS 上 sing-box 异常、DNS 故障与异常重启排查复盘

11 min read
TLDR

中午吃饭前网络稍有卡顿,没有在意,以为是偶发的临时情况,就吃饭了。

下午一点半左右吃饭回来,发现梯子掉了,服务全挂(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 只有几分钟,说明机器刚刚重新启动过。

最终站得住的结论

这次排查结束后,只有下面三条结论可以当成已证实结论保留:

  1. 本地 TUN 污染了 SSH 观测路径,默认 ssh 走的是本地代理链路,不是干净直连。
  2. 远端存在 DNS / 解析层异常,同时打坏了两条链路:
    • Caddy -> axonhub
    • sing-box -> REALITY handshake dest (www.bing.com)
  3. 远端在持续内存压力背景下发生了一次异常重启,但重启机制未证实。

同样需要明确保留的是否定结论:

  • 没有证据证明这次是 OOM killer
  • 没有证据证明这次是 kernel panic
  • 没有证据证明这次是 systemd watchdog 触发的重启。

相关配置锚点

下次需要从配置层继续看时,优先从下面几个位置回到代码:

  • modules/nixos/vps/singbox-server.nix:12
    • handshakeServer = "www.bing.com"
  • hosts/nixos-vps/default.nix:62
    • services.resolved.settings.Resolve.FallbackDNS = nameservers
  • hosts/nixos-vps/default.nix:78
    • modules.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 成功拿到了远端:

  • hostname
  • uptime
  • whoami

并确认机器刚刚重启恢复。

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 timeout
  • systemd-resolved 是否有 Under memory pressure

这次事故里,真正有价值的日志有三条:

  • Caddy14:07 开始出现 lookup axonhub: i/o timeout
  • sing-box16:18 明确报 lookup www.bing.com: context deadline exceeded
  • tailscaled.service16: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.100
  • systemd-resolved 在故障窗口里确实持续报 Under memory pressure

但这仍然不能单独证明“全部 DNS 故障都由 tailscaled 首先引起”。

事件时间线

下面这张表比按类型列证据更适合快速重建事件链。

时间事件
14:05:26systemd-resolved 开始对 100.100.100.100Using degraded feature set
14:07:40Caddy 首次出现 lookup axonhub: i/o timeout
16:18:05sing-box 开始明确报 lookup www.bing.com: context deadline exceeded
16:18:29tailscaled.service 退出,Failed with result 'exit-code'
17:35:00systemd-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 timeout14:07 开始出现Caddy -> axonhub 解析失败远端 DNS / 解析层异常至少从 14:07 已存在
lookup www.bing.com: context deadline exceededsing-box REALITY 握手目标解析失败;对应配置见 modules/nixos/vps/singbox-server.nix:12sing-box 明确被 DNS 异常拖坏
tailscaled.service: Failed with result 'exit-code' 出现在 16:18:29tailscaled 的确在故障窗口里退出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 雪崩 -> 其他服务一起坏

为什么不成立:

  • Caddyaxonhublookup ... i/o timeout14:07 就开始了
  • tailscaled.service: Failed with result 'exit-code'16:18:29

修正后结论:

  • tailscaled 退出是已证实事件
  • 但它最多是加剧因素,不能被直接认定为故障起点

误判 2:把 memory pressure 直接等同于 OOM / panic

错误写法:

systemd-resolvedjournald 报 memory pressure,所以最后一定是 OOM 或 kernel panic

为什么不成立:

  • memory pressure 只证明系统很吃紧
  • 但没有看到 OOM killerKilled processKernel panicsoft 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-resolved
  • resolvectl status
  • 查是否存在 lookup ... timeoutcontext 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 宿主机或网络环境异常”的解释权重。

如果迁移后仍复现,则更应回到本机解析层、tailscale0systemd-resolvedsing-box 配置本身继续查。

3. 长期上可考虑减少 sing-box 对外部运行时 DNS 的脆弱依赖

这不是本次事故里已经验证过的 fix,但它是合理的后续优化方向。例如:

  • 评估是否把 handshakeServer 从单个域名改成更稳的预解析目标方案
  • 评估是否给 sing-box 相关出站或握手路径使用更独立的 DNS 解析策略,减少对系统公共解析链路的耦合

一句话版 SOP

下次再遇到“VPS 上 sing-box 不可用 + SSH 现象诡异 + 本地开 TUN 后整机几乎断网”时,不要先改配置,也不要先重启远端服务;先用 ssh -B en0 拿干净观测路径,再按 上一轮 boot 日志 -> DNS / lookup 错误 -> 内核级证据 -> provider 侧事件 这条链路收敛问题。