Docker Registry Push 超时排查全记录:从网络栈到残留 veth 的真相
摘要在私有化部署 Docker Registry 时遇到宿主机 curl 容器映射端口超时的诡异问题。经历 iptables、rp_filter、bindv6only、抓包等深入排查后最终发现是 Docker 卸载残留的 veth 接口扰乱了内核包转发路径。本文完整记录排错过程与根因供同行参考。一、问题现象在宿主机192.168.0.146上使用docker run -d -p 5555:5000 registry:3启动官方 Registry 容器后发现容器内wget 127.0.0.1:5000/v2/正常返回200和{}。宿主机执行curl 127.0.0.1:5555/v2/超时Connection timed out。宿主机执行curl 172.17.0.2:5000/v2/同样超时。但宿主机ping 172.17.0.2却能通且延迟极低。二、环境信息OS: CentOS Stream 8 / 9Docker: 24.xRegistry 镜像registry:3启动命令docker run -d --name ags-registry -p 5555:5000 -v /data/...:/var/lib/registry registry:3三、第一阶段排查 iptables 与 Docker 代理检查端口监听ss -tlnp | grep 5555显示docker-proxy正在监听所有网络接口的 5555 端口状态正常。检查 NAT 规则iptables -t nat -L -v中发现DNAT规则将访问宿主机 5555 端口的数据包转发到172.17.0.2:5000看起来没有问题。验证 docker-proxy 是否工作用curl 127.0.0.1:5555和192.168.0.146:5555都超时说明流量进入了代理但未从容器返回。排查 filter 表FORWARD链默认ACCEPTDOCKER链中有明确允许到容器 5000 端口的规则未被拦截。初步结论流量成功送达容器但应用层未响应。四、第二阶段怀疑内核网络参数rp_filter 反向路径过滤rp_filter为 1可能导致容器回包被丢弃。临时关闭rp_filter后问题依旧排除此项。bridge-nf-call-iptables该参数为 1但关闭后依然超时且 ping 通 TCP 不通排除。conntrack 表连接跟踪表占用极低无table full日志排除。五、第三阶段深入应用层与 IPv6 陷阱确认容器内服务正常docker exec进容器用wget 127.0.0.1:5000/v2/成功证明 Registry 进程正常。跨容器测试docker run --rm --network bridge busybox wget http://172.17.0.2:5000/v2/同样超时这说明问题不是宿主机独有而是任何非容器本身的访问都失败。检查 Registry 监听地址日志中出现listening on [::]:5000表明 Registry 默认绑定到了 IPv6 通配符地址。虽然容器内net.ipv6.bindv6only0但实际测试发现来自桥接网络的 IPv4 流量虽然能完成 TCP 握手抓包可见但 HTTP 响应包永远不会发出。使用 Host 网络模式验证改用--network host并设置REGISTRY_HTTP_ADDR0.0.0.0:5555后curl 127.0.0.1:5555/v2/立刻成功。由此基本确认Bridge 网络下 IPv4 到 IPv6 监听套接字的映射存在缺陷导致应用层无声丢弃连接。六、转折点发现残留 veth 接口尽管 Host 模式解决了问题但我们注意到ip neigh show中仍有旧容器的 IP172.17.0.2和 MAC 地址02:42:ac:11:00:02。进一步检查/sys/class/net/docker0/brif/发现残留的 veth 接口vethfa82f9f且没有对应的容器进程。该残留 veth 曾在之前的 Docker 卸载/重装中未被清理其 MAC 地址恰好与新创建的 Registry 容器的 IP 相同。当新容器连接到 docker0 时内核 ARP 缓存可能将流量错误地导向这个僵尸 veth导致正常数据包被丢弃而 ICMP 响应由内核直接处理不受影响从而解释了“ping 通 TCP 不通”的现象。七、最终修复清理残留 veth 接口baship link del vethfa82f9f ip neigh flush dev docker0彻底重启 Docker推荐或重新创建容器bashsystemctl restart docker强制 Registry 监听 IPv4 地址重新创建容器时添加环境变量bashdocker run -d --name ags-registry \ -p 5555:5000 \ -e REGISTRY_HTTP_ADDR0.0.0.0:5000 \ registry:3验证bashcurl -v http://127.0.0.1:5555/v2/ # 200 OK docker push hub.ags.local:5555/myimage # 成功八、总结现象真正原因ping 通但 TCP 超时残留 veth 导致非 ICMP 流量被导向无效端点容器内访问正常容器内走 lo 接口不受 veth 干扰Host 网络下正常绕过了 docker0 桥接避免残留接口影响Registry 日志显式 IPv6双栈绑定在纯净网络下无问题但与残留 veth 共存时触发内核 bug经验教训卸载 Docker 时应使用yum remove并手动清理/var/lib/docker和/run/docker必要时重启。遇到类似“网络半通”故障时检查桥接接口下的 veth 残留往往比反复调整 iptables 更高效。容器化服务应显式绑定0.0.0.0而非依赖通配符避免 IPv6 兼容性踩坑