在容器中调用华为云 OBS 时,请求在 DNS 解析成功后长时间没有响应,最终超时。相同 endpoint 从 Pod 的 host network 访问可以正常返回 403,因此问题集中在容器网络路径,而不是 OBS 服务本身。

先看服务端日志

失败容器的 runtime 日志有一条很重要的时间线:

Endpoint=https://obs.cn-north-4.myhuaweicloud.com:443/
internet host address:
  phoenix-autotest.obs.cn-north-4.myhuaweicloud.com/123.60.240.82,
  phoenix-autotest.obs.cn-north-4.myhuaweicloud.com/123.60.240.83

这说明 DNS 解析已经完成。之后没有 HTTP 状态码,也没有 OBS 的业务异常。因此,失败点位于 DNS 之后、HTTP 响应之前,更接近 TCP/TLS,而不是 bucket、路径或签名参数。

产品的华为云插件确实直接使用 OBS SDK:

this.obsClient = new ObsClient(
        settings.accessKeyId(),
        settings.secretAccessKey(),
        settings.endpoint());

目录操作会调用 listObjects。在本机使用相同的 OBS 配置和 SDK 3.25.10 重放,接口可以正常返回。因此没有证据表明 AK/SK 或配置错误。

对照不同网络路径

运行服务的 Pod 使用 DinD sidecar:

DOCKER_HOST: unix:///var/run/docker/docker.sock

Phoenix 容器位于 DinD 的 bridge 网络中。在同一个 Pod 中做只读 HTTPS 探测,结果非常明确:

网络位置OBS endpoint 结果
Pod/host network约 70~100 ms 返回 403
DinD bridge,MTU 1500TLS 握手超时
DinD bridge,MTU 1490TLS 握手超时
DinD bridge,MTU 1472TLS 握手超时
DinD bridge,MTU 1460正常返回 403
DinD bridge,MTU 1450正常返回 403

这里的 403 反而是成功信号:服务端已经收到请求,只是没有提供认证信息。超时则说明请求根本没有走到 OBS 的 HTTP 层。

MTU 为什么会造成 TLS 超时

当前 Pod 的网络接口是:

eth0 mtu 1450

这是 Kubernetes CNI 覆盖网络常见的 MTU。可是 DinD 中的 Docker bridge 默认是:

docker0 mtu 1500

Phoenix 容器的路径变成了:

Phoenix 容器 (1500)
  -> Docker bridge (1500)
  -> DinD Pod eth0 (1450)
  -> Kubernetes 节点和外部网络

容器发出的较大 TCP/TLS 报文经过 Pod 的 1450 MTU 链路时,需要分片或依靠 PMTU Discovery 降低报文大小。如果中间设备没有把 Fragmentation Needed 正确传回,或者相关 ICMP 报文被过滤,TCP 连接可能已经建立,但 TLS 握手在等待一个永远不会到达的报文,于是表现为“连接超时”。

这也解释了为什么 DNS 正常、TCP 看起来有连接、应用却一直收不到 OBS 响应。

为什么以前的 Podman 5.8.2 没有暴露这个问题

历史环境使用:

quay.io/podman/stable:v5.8.2-immutable

应用容器通过共享 Unix socket 访问 Podman 的 Docker 兼容 API:

DOCKER_HOST=unix:///var/run/podman/docker.sock
podman system service --time=0 unix:///var/run/podman/docker.sock

同时挂载了:

[containers]
log_driver = "json-file"
netns = "bridge"

Podman 5 使用 netavark 创建 bridge 网络。netavark 在未指定网络 MTU 时,会读取默认路由接口的 MTU,并将其用于容器 veth。也就是说,在 Pod 的默认网卡是 1450 时,Podman 创建的容器通常也会拿到 1450,而不是固定使用 Docker bridge 的 1500。

修复方式

最直接的修复是在 DinD daemon 启动时显式指定 MTU,并与 Pod 的实际接口保持一致:

args:
  - "--mtu=1450"

如果不同节点的 CNI MTU 不同,不应把 1450 当成永久常量。更稳妥的方案是让 DinD 启动脚本读取默认路由网卡 MTU,再生成 Docker daemon 配置,或者统一集群网络 MTU。

排查这类问题的顺序

遇到请求超时,可以按下面顺序切分:

DNS
  -> TCP connect
  -> TLS handshake
  -> HTTP status
  -> SDK 认证/业务

这次故障的最终结论是:华为云服务正常,Phoenix OBS 请求参数正常,失败发生在 DinD bridge 到 Kubernetes Pod 网络的 MTU 不匹配;修正 Docker bridge MTU 后,问题才真正消失。