命名空间对照 · network_mode 决策 · 反代 WebSocket · allowedOrigins · 六步 Runbook
在 VPS 上用 Compose 跑 OpenClaw 的自建维护者经常遇到一种错觉:docker compose ps 全绿、Gateway 日志也在刷,但本机或旁路容器里的 openclaw CLI 就是握手超时或 502。根因几乎总是落在网络命名空间与监听地址的组合上,而不是模型密钥本身。本文给谁遇到什么问题一句话定位,用三层症状树把「进程在跑」与「真可达」分开;中间用对照表收敛 bridge、host 与 network_mode: service;再用六步可复现 Runbook把端口发布、回环绑定、反代 WebSocket 与 allowedOrigins 串成闭环;最后附可引用参数事实与决策矩阵。Compose 基线与资源上限请交叉阅读 Docker Compose 生产基线,多实例隔离见 同机多实例防串台,Gateway 加固清单见 生产加固清单;需要节点与出口稳定性可走 订购页。
Compose 的 healthcheck 往往只探针容器内回环或进程存活,它不自动证明「从 CLI 容器到 Gateway 服务名」这条路径在 DNS、iptables、用户态代理三层都成立。下面五条在工单里总是一起出现,把它们拆开,你的排障日志会立刻变薄。
监听在 127.0.0.1:Gateway 只绑回环时,同 bridge 的其他服务用服务名访问会得到连接拒绝;症状像随机超时,实则从未出本网络命名空间。
CLI 跑在宿主机却抄了容器内地址:把 openclaw-gateway:18789 写进宿主机 shell 配置,解析与路由立刻错位。
反代只转 HTTP 没转 Upgrade:浏览器或 CLI 走 WSS 时在边缘返回 400 或静默断流,应用日志却显示 Gateway 已就绪。
allowedOrigins 与实际来源不一致:生产域名、内网别名、Tailscale MagicDNS 名称混用,握手在应用层被拒绝,网络抓包却「看起来都通」。
network_mode: service 升级竞态:共享栈的服务重启顺序变化后,下游仍连旧 IP 或旧端口映射,表现为间歇性成功。
把下一节的矩阵打印成评审页:每次架构变更只允许改其中一格,并在变更单附上「同一命名空间内的 curl 与外层对照 curl」两组输出,可显著降低回归成本。
再补一层「时间维度」:Compose 在滚动更新时会短暂出现新旧容器并存,DNS 缓存与应用连接池若未对齐,就会表现为第一次成功、随后几分钟失败。此时不要急着调大超时,应先在发起方执行解析刷新与连接复用对照,确认拿到的上游地址与 docker inspect 里当前端点一致。若你还叠了用户态代理或公司透明代理,记得把CONNECT 隧道目标与直连目标分开记录,避免把代理层的 407 当成应用鉴权失败。
最后一条容易被忽略:MTU 与分片在跨云或跨运营商链路里会放大成「偶发超时」;若只有大包失败而小包健康检查永远绿色,应把抓包窗口收窄到单次 WS 帧大小与 TLS 记录层边界,而不是先改业务代码路径。
当你把上述信号都记录在变更单后,再打开 openclaw logs 与边缘访问日志做时间对齐,通常能在半小时内把问题从「玄学网络」收敛到单一配置字段;这也是对外求助时最小复现包应当包含的上下文厚度。
选择网络模型时,请同时写下谁发起连接、解析到什么地址、经过哪些 NAT 层。没有这张表,团队会在「改 ports」「改 extra_hosts」「改反代上游」之间来回横跳。
| 模型 | 典型监听写法 | 同文件其他服务访问 | 宿主机进程访问 |
|---|---|---|---|
| 默认 bridge + 端口映射 | 容器内 0.0.0.0 或具体端口发布 | 用 Compose 服务名与内部端口 | 用 127.0.0.1:映射端口 或宿主机网卡 IP |
| host 网络 | 与宿主机共享栈,常等价于绑宿主机可见地址 | 同项目其他容器若不回退 bridge 则不能再用服务名互访旧路径 | 与容器并行时优先核对端口冲突与防火墙 INPUT 链 |
| network_mode: service:gateway | 与网关共享 netns,回环视角一致 | 辅助容器可直接打 127.0.0.1:网关端口 | 宿主机仍需走发布端口或反代,不自动继承 |
真可达的定义是:在发起方相同的网络命名空间里,用同一组主机名、端口与 TLS 参数重复得到一致响应;而不是「我在笔记本上 curl 通了一次」。
下列顺序刻意把「最便宜的观测」放在最前;任何一步失败都应停止向下猜测并保存输出。与 Gateway 安装语言不一致时,回到 安装与 doctor 清单 对照字段名。
确认发起方位置:标注命令运行在宿主机、网关侧车容器还是完全独立的 CLI 容器;输出 hostname 与 ip route 摘要。
HTTP 层探活:在发起方 netns 内对目标主机名与端口执行 TLS 或明文 GET,核对状态码与响应体前缀,排除纯 DNS 问题。
WebSocket 探针:用已知可升级路径发起握手,记录边缘与应用返回头;与应用日志时间戳对齐。
核对监听矩阵:在网关容器内检查监听地址是否为 127.0.0.1;若是且需要跨服务访问,改为 0.0.0.0 或引入共享 netns 并同步文档。
反代四元组:校验 upstream 指向的是容器 IP 还是 published port、是否透传 Connection 与 Upgrade、超时是否过短导致长连接被切断。
应用 origins 收敛:列出浏览器、CLI、CI 三类真实 Origin 或等效来源串,与配置逐项打勾;缺一项就视为未完成发布。
docker compose ps
docker compose exec cli sh -lc 'getent hosts openclaw-gateway; curl -fsS -o /dev/null -w "%{http_code}\n" http://openclaw-gateway:18789/health || true'
curl -fsS -o /dev/null -w "%{http_code}\n" http://127.0.0.1:18789/health || true
docker compose logs --no-color --tail=200 openclaw-gateway
提示:将服务名与端口替换为你仓库中的真实值;若健康路径不同,保持「同一 URL 在两层 netns 各跑一次」的方法不变。
这一节只收录可在代码与配置里点名的事实,避免「感觉 CDN 有问题」这类不可审计表述。需要 mem_limit 与日志轮转口径时回到 Compose 生产基线。
ports: 映射在宿主机创建 DNAT 规则;若宿主机本地防火墙策略与 Docker 链交互顺序理解错误,会出现「容器互通但宿主机不通」或反向现象。注意:不要在未记录基线的情况下同时改反代上游、Gateway 监听与 CLI 配置;三角变更会让回滚无法二分。
把「CLI 与 Gateway 是否必须共享回环视角」写成布尔字段,再决定要不要 network_mode: service: 或 host。矩阵用于评审而不是口号。
| 约束 | 更稳妥默认 | 关键验收 | 主要风险 |
|---|---|---|---|
| CLI 与 Gateway 同 compose 文件 | bridge + 明确监听 0.0.0.0 | 服务名解析与内部端口 curl 一致 | 忘记同步防火墙与 published 端口文档 |
| 必须共享 localhost 语义 | sidecar 使用 network_mode: service:gateway | 侧车重启不引入旧连接池 | 升级顺序与卷挂载权限耦合 |
| 宿主机已有成熟反代 | 仅 published loopback + 反代 TLS 终结 | 边缘到上游的 WS 升级抓包一致 | allowedOrigins 未覆盖 CLI 使用的 URL 形态 |
长期靠「多叠一层隧道脚本」或「手工改 hosts 救急」会把 MTTR 绑在个人记忆上;一旦上游证书或内网 DNS 变更,排障会退化成全员会议。相较之下,把可重复命令、监听矩阵与 origins 列表版本化更像生产系统。
常见误区:看到 502 就轮换模型密钥;多数时候应先完成第三节 HTTP 与 WS 两层探活再动密钥。
纯脚本临时打通端口而不写清单,在审计与合规场景下很难证明「默认拒绝、显式放行」;当 OpenClaw 需要与固定出口、主机名与 mTLS 策略一并归档时,自建 VPS 上的 ad-hoc 网络层往往缺少可签字的变更单。对于要把 iOS 构建、桌面接力与 Agent 常驻放在独占、地区与网络档位可预期环境、并希望减少「宿主机与容器 netns 来回猜」的团队,VpsMesh 的 Mac Mini 云端租赁通常是更优解:独占节点便于固化监听与 ACL 叙述,并与 团队私网 Runbook 使用同一套语言描述边界;价格与规格见 价格页,连接与帮助见 帮助中心。