深夜运维实录:从 dsh.want.biz 失联到三个 AI 同仓打工
记录 2026 年 9 月初的一个深夜排障:一条 Cloudflare Tunnel 的静默失联,牵出一段 2024 年就埋下的守护失效;随后顺藤摸瓜研究了智谱 ZCode 的 CLI 与计费通道机制;最后发现自家仓库里,三个 AI 正在同一条 git 工作区里并行打工。
一、dsh.want.biz 打不开了
一切从一句"本机 cloudflare 是不是连不上了,dsh.want.biz 打不开了"开始。
排查的第一步永远是把链路拆开看。dsh.want.biz 的链路是:浏览器 → Cloudflare 边缘(Access 认证)→ Cloudflare Tunnel → 本机 3080 端口。逐段验证后发现一个反直觉的现象:
curl https://dsh.want.biz返回 302,跳转到 Cloudflare Access 登录页——边缘、DNS、认证全部正常;- 本机
127.0.0.1:3080返回 200——源站服务正常; - 但
ps aux | grep cloudflared—— 进程不存在。
Cloudflare Access 的登录跳转发生在边缘,不需要源站在线。所以"302 能通"完全可能是假象:登录之后的请求要经隧道回源,隧道断了,用户看到的就是打不开。
cloudflared tunnel info 一锤定音:MacMini_home 隧道的 CONNECTIONS 列是空的——隧道离线。而同一账号下 NAS 的隧道有 4 个连接在线。
二、根因:一次自动更新 + 一段两年前的历史遗留
翻日志(~/.cloudflared/nohup.out),死因写得明明白白:
ERR Initiating shutdown error="cloudflared has been updated to version 2026.8.3"
cloudflared 自动更新到新版后自行退出,时间凌晨 02:13。这本不该是致命的——launchd 守护本该在几秒内把它拉起来。但翻开 ~/Library/LaunchAgents/com.cloudflared.tunnel.plist,发现了真正的深坑:
这个 plist 在 2024 年 5 月就被替换成了一段 60 字节的 bash 脚本内容(nohup cloudflared tunnel run &),原始 plist 被改存为 .plist.save。launchd 根本无法加载一个不是 XML 的 plist。也就是说,近一年多来 cloudflared 一直是 nohup 裸跑的,进程一死就无人拉起,静默失联。
两层故障叠加:直接死因是自动更新退出,深层死因是守护层早已名存实亡。
三、修复:升级为系统级 LaunchDaemon
应急很简单:手动重启隧道,连接器重新注册到边缘。根治才是正戏——把守护从用户域迁到系统域(LaunchDaemon,root 运行,开机即跑,不依赖用户登录):
<!-- /Library/LaunchDaemons/com.cloudflared.tunnel.plist 关键设计 -->
<key>ProgramArguments</key>
<string>/opt/homebrew/bin/cloudflared</string>
<string>tunnel</string>
<string>--config</string>
<string>/Users/ygs/.cloudflared/config.yml</string>
<string>run</string>
<key>KeepAlive</key><true/>
<key>ThrottleInterval</key><integer>10</integer>
三个关键设计:
- 显式
--config:root 用户的 HOME 在/var/root,不指定路径就找不到 ingress 配置(dsh.want.biz → http://127.0.0.1:3080的映射); KeepAlive=true:针对"自动更新后退出"这个根因的自愈——进程退出 10 秒内 launchd 自动重拉;- 系统域:LaunchDaemon 归 root,Mac 重启后无需登录即恢复。
验证自愈能力的方法很暴力:手动 kill 掉进程,14 秒后观察——launchd 自动拉起新进程,隧道重新注册。完事。
顺带清了两处历史遗留:一个 token 指向早已删除隧道的死配置 plist(归档为 .disabled-oldtoken),以及用户域那个被改坏的 plist(退役为 .userdomain-retired)。
经验:macOS 上任何"应该常驻"的进程,用 nohup 裸跑都是定时炸弹。launchd 的 LaunchDaemon + KeepAlive 才是正解;写进 plist 的必须是二进制本身而不是包一层 bash 脚本,否则 KeepAlive 盯的是秒退的脚本,毫无守护意义。
四、插曲:ZCode 研究与一次"忍住"
排障间隙研究了下智谱的 ZCode(GLM 系的编程 Agent)。两个发现:
其一,zcode-cli 还没正式发布。 npm 包已占位(0.0.1),但 --help 里自己写着 "This package name is reserved. The full CLI is under active development.",唯一的 chat 命令标注 coming soon。完整能力目前仍锁在桌面 App 里。
其二,"夜间免费"的判定机制藏在配置文件里。 ZCode 的 config.json 按 provider 组织渠道,每个渠道独立的 baseURL 就是计费分流点:
zcode.z.ai/api/v1/zcode-plan/anthropic—— ZCode 专属通道(夜间 23:00–次日 9:00 用 GLM-5.3-Flash 额度消耗为 0)open.bigmodel.cn/api/anthropic—— 普通 Coding Plan 通道(同期额度 ×2)
判定三要素:请求打到哪个 baseURL + 账号订阅资格(服务端 entitlement,配置里那句 coding_plan_not_entitled 就是证据)+ 时间窗口。三项满足自动免单——这也解释了免费额度为何"锁死在 ZCode 里导不出来"。
至于"把 ZCode 的通道提取出来配到其他工具蹭免费"这个念头,认真评估后放弃了:凭证就在本地配置里,技术上五分钟就能配,但服务端大概率校验客户端特征,社区已有 Coding Plan 风控封号的先例。为两周的限时活动押上付费主账号,期望值是负的。 夜间 ×2 的官方补偿已经够用——识别哪些便宜不能占,也是运维素养的一部分。
五、意外的惊喜:三个 AI 在同一仓库里打工
检查 ZCode 交付时,git 工作区给了个大惊喜——除了 10 章书稿,还有 16 个文件、694 行的代码改动。一度以为是工具越界,对质之后发现真相更有趣:
- ZCode 在写《GLM突围记》(智谱编年史书稿):BRIEF → 大纲 → 素材库 → 逐章 8000–10000 字,先冲刺后精修;
- dsh(自研 Harness)在做 repl 功能开发:
/rename会话命名命令、doctor 自检、剪贴板图片等一整套——设计提案来自 Gemini; - Ubuntu-R86S 上的文件监控助手持续推送变更通知,成了两个 Agent 的"心电图"——通知还在来,就说明都活着。
三个模型、一台 Mac、同一条 git 工作区,各司其职。两个实践心得:
- 双 Agent 并行前先提交一次。工作区是共享的,开工前各自的基线干净,出事才能秒判"谁的改动"——否则只能靠文件 mtime 考古。
- Agent 的实现笔记值得读。
.agents/notes/里的功能笔记带着 "Alternatives considered"——为什么拒绝伪造附件引用、为什么拒绝传文件路径代替图片块。这些"不做什么"的记录,比"做了什么"更有长期价值。
六、写在最后
这一晚的完整链路:一条失联的隧道 → 一段埋了两年多的坏配置 → 一次守护架构升级 → 一场 ZCode 机制侦察 → 一次"忍住不蹭"的决策 → 三个 AI 的同仓协作。
运维的乐趣大抵如此:每一个"打不开了"的背后,都藏着一串值得刨到底的为什么。
标签:运维, cloudflare-tunnel, launchd, macOS, ZCode, AI-Agent, 多智能体协作