前几天在服务器上同时跑两个 Docker 容器:一个是 s-ui(sing-box 面板),一个是 msedge(带浏览器的容器)。msedge 需要通过 socks5 代理访问外网,而这个 socks5 端口正好由 s-ui 容器里的 sing-box 提供。
直觉做法很简单:msedge 里写 socks5://192.168.1.5:10808,指向宿主机 IP。但死活连不上。折腾了两个小时才发现,问题根本不在防火墙,而在 Docker 的网络模型上。
默认 bridge:容器之间没有 DNS
Docker 装好之后会自动创建一个叫 bridge 的网络(就是 docker network ls 里那个默认的 bridge)。这个网络有两个关键特性:
第一,容器之间可以互通,但只能靠 IP。你启动了两个容器,想从 A 访问 B,得先 docker inspect B 拿到它的 IP,然后写在配置里。
第二,这个默认 bridge 网络没有内置 DNS。容器里解析不了另一个容器的名字。你在 A 容器里 ping s-ui,直接报 bad address。
这就导致一个很尴尬的局面:容器重启之后 IP 会变,配置文件里写死的 IP 立刻失效。每次重启都要重新 inspect、重新改配置、重新启动。我刚开始就是这么干的,第三天就放弃了。
自定义 bridge:名字就是 DNS
正确做法是创建一个自定义网络:
docker network create sing-net
然后把需要互通的容器都加进去:
docker run -d --name s-ui --network sing-net ...
docker run -d --name msedge --network sing-net ...
自定义 bridge 网络和默认 bridge 的区别,就在一个地方:它内置了一个 DNS 服务器。这个 DNS 会自动维护容器名到 IP 的映射。
也就是说,msedge 容器里可以直接这样写代理地址:socks5://s-ui:10808。s-ui 不需要在 hosts 文件里写,不需要手动解析,Docker 内部 DNS 直接帮你转成容器的当前 IP。容器重启了?IP 变了?没关系,DNS 记录自动更新,配置一行都不用改。
host 模式的坑:127.0.0.1 陷阱
中间我还试过 --network host。host 模式下容器直接共享宿主机的网络栈,看起来最省事——端口不用映射,服务起来就是宿主机端口。
但这里有个致命陷阱:很多服务在 host 模式下会绑定到 127.0.0.1,而不是 0.0.0.0。
原因很简单:在默认桥接模式下,服务监听 0.0.0.0,Docker 会通过端口映射把宿主机端口转发进来。但在 host 模式下,服务直接暴露在宿主机网络上,很多程序的默认配置是只监听本机回环地址,出于安全考虑。
s-ui 就是这样。host 模式启动后,面板端口 2095 只绑定在 127.0.0.1:2095,你从局域网另一台电脑访问 192.168.1.5:2095,直接连接被拒绝。我当时卡了快半小时才反应过来——不是没启动,是它压根没监听外网网卡。
更坑的是,sing-box 的 socks5 入站也是默认绑定 127.0.0.1:10808。在 host 模式下这个端口只接受本机连接。你必须在配置里把监听地址改成 0.0.0.0,别的容器才能连进来。
三种网络模式对比
默认 bridge:容器间无 DNS,只能写 IP;需要 -p 映射端口;适合单机单容器,不需要互通的场景。
自定义 bridge:内置 DNS,容器名即主机名;需要 -p 暴露给外部;多容器互通最常用的模式。
host:直接共享宿主机网络栈,没有端口隔离;不需要 -p;性能敏感场景用,但前提是服务绑定 0.0.0.0。
一套标准做法
现在我跑多个容器的标准流程是:
- 先建网络:
docker network create my-net - 每个需要互通的容器都加
--network my-net - 容器之间通信一律用容器名,不写 IP
- 需要暴露给局域网的端口用 -p 映射
- 入站服务配置里监听 0.0.0.0,不要绑 127.0.0.1
这一套下来,容器随便重启、随便重建,配置不用动。再也不用每次重启都去 docker inspect 查 IP 了。
说到底,Docker 默认 bridge 网络是为了简单而设计的——它能跑,但不适合多容器协作。一旦你有两个以上容器需要互相访问,老老实实干件事:docker network create,然后用容器名通信。这不是什么高级技巧,是 Docker 官方文档里写了但很多人跳过的一节。









暂无评论内容