⚠️ AI 生成标识 · 本文由 AI Agent 协助撰写,人类作者审核发布。
一、事故现象
某天早上,我自建的 New API 网关(多上游 LLM 负载均衡)所有渠道集体失灵:在控制台点击任意一个模型的「测试」按钮,都返回:
1
status_code=500, upstream error: do request failed
奇怪的是——早上某个时间点还能用,用着用着突然就全部 500 了。渠道配置没动过,API Key 没过期,额度也充足。排障者的第一反应通常是怀疑渠道本身,但我逐个渠道检查后发现全部渠道无一幸免,这几乎不可能是上游的问题。
于是打开 New API 的容器日志,看到了所有渠道共同的错误:
1
2
do request failed: Post "https://api.deepseek.com/v1/chat/completions":
dial tcp: lookup api.deepseek.com on 127.0.0.11:53: server misbehaving
关键在最后的 server misbehaving。127.0.0.11 是 Docker 容器内置 DNS,它负责把外部域名解析转发给宿主机 DNS。server misbehaving 意味着宿主机 DNS 服务器本身在报错,而不是域名不存在。
二、第一层:宿主机 DNS 被 Tailscale 接管
顺着 DNS 链路往下查,宿主机 /etc/resolv.conf 长这样:
1
2
3
4
5
# resolv.conf(5) file generated by tailscale
# DO NOT EDIT THIS FILE BY HAND
nameserver 100.100.100.100 # Tailscale MagicDNS
nameserver fd7a:115c:a1e0::53
search tail14ff36.ts.net
宿主机的 DNS 被 Tailscale 接管了(accept-dns=true),系统 DNS 变成 Tailscale 的 MagicDNS(100.100.100.100)。Docker 容器(New API)继承宿主机的 DNS,所以它的外部域名解析最终都指向 Tailscale MagicDNS。
在宿主机上直接解析一下,症状立刻显现:
1
2
3
4
$ getent hosts api.deepseek.com
# 返回 ERR: Name or service not known
$ getent hosts qq.com
# 也是 ERR: Name or service not known
但有意思的是内网 Tailscale 域名完全正常:
1
2
$ getent hosts marlin-server
100.72.204.6 # ✅ 正常解析
这就解释了为什么「个别东西还能用、但 New API 全挂」——所有走外部公网域名的服务全废,走 Tailscale 内网域名的服务照常。
三、第二层:tailscaled 的致命日志
线索指向 Tailscale 的 DNS resolver,翻它的日志,真相浮出水面:
1
tailscaled: dns: resolver: forward: no upstream resolvers set, returning SERVFAIL
还有一条反复出现的:
1
dns: resolver: forward: no upstream resolvers set, returning SERVFAIL
翻译过来就是:tailscaled 的 MagicDNS 没有配置任何上游(upstream)DNS 服务器。当外部公网域名被交给它解析时,它手里没有任何上游 DNS 可转发,直接返回 SERVFAIL。
用 tailscale dns status 确认:
1
2
3
4
MagicDNS configuration:
Resolvers (in preference order):
(no resolvers configured, system default will be used)
Split DNS Routes: (空)
一切真相大白。
四、为什么是「突然」不能用?
排查到这里,最关键的问题是:为什么早上还能用,突然就不行了? New API 的渠道配置没有任何变化,那是什么触发了这次瘫痪?
tailscaled 的 journal 日志给了精确答案:
1
2
3
4
5
6
[08:12:38] trample: resolv.conf changed from what we expected.
did some other program interfere? current contents: ""
[08:12:38] dns: resolve.conf was trampled, setting existing config again
[08:12:38] dns: Set: {DefaultResolvers:[] Routes:{...}}
[08:12:38] dns: Resolvercfg: {Routes:{.:[] ...}} # ← root 上游变空
[08:12:58] dns: resolver: forward: no upstream resolvers set, returning SERVFAIL
完整的时间线:
| 时间 | 事件 |
|---|---|
| 更早之前 | Tailscale 持有 root resolver 169.254.169.240(某个 link-local 上游),外网能解析 |
| 05:12 | tailscaled 从控制面收到一次 netmap 配置更新 |
| 08:12:38 | 宿主机上某个「其他程序」把 /etc/resolv.conf 清空了(current contents: "") |
| 08:12:38 | tailscaled 检测到踩踏(trample),重新应用它手里那份「没有上游」的坏配置 |
| 08:12:58 | 从这一刻起,所有外部域名解析全面 SERVFAIL |
所以「突然用不了」其实是两层因素叠加:
- 深层原因:Tailscale MagicDNS 里没配置任何全局上游 DNS(root resolver 是空的),tailscaled 手里就是一份「无上游」的配置。
- 直接触发:08:12 某个程序把
/etc/resolv.conf清空,逼 tailscaled 把这份坏配置重新写回系统,坏状态从「潜在」变成「板上钉钉」。
也就是说:Tailscale 一直「接管」着这台机器的 DNS,但因为缺全局上游,它本身就是一个隐患;那个清空 resolv.conf 的程序成了「压死骆驼的最后一根稻草」,把隐患瞬间引爆成事故。
五、修复:给 MagicDNS 补上全局上游
真相清楚后,修复就很简单了——给 Tailscale 配置 Global Nameservers(全局上游 DNS)。
在 Tailscale Admin Console 的 DNS 设置里可以手动加,也可以用 API 更优雅地配。我用 Tailscale API Key 直接调用:
1
2
3
4
5
POST https://api.tailscale.com/api/v2/tailnet/-/dns/nameservers
{
"dns": ["223.5.5.5", "119.29.29.29", "114.114.114.114"],
"magicDNS": true
}
这三个是国内可靠公共 DNS(阿里、腾讯、114),遵循「国内环境预配国内 DNS」的原则。
保存后 tailscaled 自动从控制面同步新配置,无需重启任何服务:
1
2
3
4
5
$ tailscale dns status
Resolvers (in preference order):
- 223.5.5.5
- 119.29.29.29
- 114.114.114.114
验证:
1
2
$ getent hosts api.deepseek.com
116.140.43.136 # ✅ 恢复解析
再到 New API 控制台点「测试」,所有渠道全部恢复正常。
六、这次踩坑的 3 条教训
1. MagicDNS 必须配全局上游
Tailscale 启用 MagicDNS(accept-dns=true)后,系统 DNS 会被它全面接管。如果此时 Tailscale 控制台没有配置 Global Nameservers,那么:
- 内网 Tailscale 域名(
xxx.ts.net)→ 正常(本地映射表,不需要上游) - 所有外部公网域名 → 全部解析失败(没有上游可转发)
这种「内网通、外网挂」的症状极具迷惑性,很容易误判成是渠道、网络或防火墙的问题,实际上根子在 DNS 上。
2. 「突然不行」往往有触发事件
「早上还能用,突然不行」几乎不可能是偶然。遇到这类突变,一定要去翻时间点附近的日志:tailscaled 的 journal、系统 service 重启记录、配置变更记录。本次正是靠 journal 里 08:12:38 的 trample 事件精确定位了触发点。
3. 排查 DNS 用这套「三板斧」
1
2
3
4
5
6
7
8
9
# ① 看当前 resolv.conf 被谁接管
cat /etc/resolv.conf
# ② 测系统域名解析(看是不是全挂/部分挂)
getent hosts api.deepseek.com
# ③ 看 tailscaled 的 DNS 配置与日志
tailscale dns status
journalctl -u tailscaled | grep -i "upstream resolvers\|trample"
4. 谁是「清空 resolv.conf 的程序」?
虽然这次配好 Global DNS 后问题已根治(即使再被清空,重新应用时也有上游可用了),但那个「清空 resolv.conf 的程序」仍是个未解之谜(8/24 和 8/25 各发生一次)。如果你的机器也有类似情况,值得在踩踏时间点附近排查一下是否有服务、脚本或 systemd 单元在动 /etc/resolv.conf。
一句话总结:New API 全渠道 500 不是 New API 的锅,也不是某个渠道的锅,而是 Tailscale MagicDNS 接管了宿主机 DNS 却忘了配置全局上游,导致所有外部域名解析静默瘫痪。给自己的新环境配 Tailscale,别忘了这一步——配好 Global Nameservers,能省掉很多「排查一整晚」的麻烦。