很多人跑 Docker 是"单机 `-p 端口:端口` 一把梭":映射一下宿主机端口就完事,容器间的连接全凭直觉。一旦容器多起来、还要跨两台 VPS 让他们互访,就会遇到一连串说不清的问题:为什么这个容器 ping 不通那个容器?Windows 下的 localhost 和 Linux 下为什么不一样?两台机器上的容器怎么像内网一样互通?这背后是 Docker 的"网络模型"在起作用,它在 bridge、overlay、host 等几种内置网络之间做选择。这篇把 Docker 网络讲透:默认的 bridge 是怎么工作的、端口映射和你以为的不一样在哪、容器间怎么通信,以及多主机跨机互访要用 overlay 是怎么回事。
一、Docker 默认网络:bridge 与你那个"不是一回事"的端口映射
单机装好 Docker,每个容器默认加入一个叫 `bridge` 的私有网络(Docker 会在宿主机里的虚拟网桥 docker0 上给每个容器发一个 172.17.x.x 的私有 IP)。通过 `-p 8080:80` 做的事,本质是"把宿主机 8080 端口的流量,转发给这个容器的 80 端口"——注意,它转发的是"宿主机的端口 → 容器 IP:端口",而不是"这个端口就等于容器"。
这个细节直接解释了很多新手困惑:比如容器内部的 80 端口,在容器外(宿主机/浏览器)并不存在,你得靠 `-p` 把它"发布"出来;又比如容器之间能不能直连,取决于它们是否在同一个网络里,而不是宿主机能不能通。想看清当前网络和容器的归属,用:
# 看有哪些网络、某个容器属于哪个网、容器分配到什么 IP
docker network ls
docker network inspect bridge
# 常用排查:在容器里看自己的 IP 和能否 ping 同伴
docker exec -it 容器名 ip addr
docker exec -it 容器名 ping 另一个容器名 # 同一自定义网络内可用名字
理解"网络"和"端口映射"是两回事,是解开一大堆 Docker 网络问题("为什么连不上""为什么只能 localhost")的钥匙。默认 bridge 有个特点:容器间用 IP 能通,但用容器名不一定能解析(默认 bridge 不做内建 DNS),这就是为什么"两个容器明明能 ping 通 IP,却不能按名字互访"。
二、自定义网络:让容器"用名字互相找"
解决"按名字互访"最标准的手法,是不要再依赖默认 bridge,而是自己创建一个自定义 bridge 网络,把相关容器都放进去。自定义网络自带 DNS,容器可以用"名字"作为主机名互相解析——这比记 IP 可靠得多,也是 compose 里服务之间能用 `服务名` 通信的根本原因。
# 建一个自定义网络,把两个容器放进去
docker network create mynet
docker run -d --network mynet --name app1 nginx
docker run -d --network mynet --name app2 nginx
# 现在 app1 里可以直接 ping app2(Docker 内建 DNS 解析名字)
docker exec app1 ping app2
这里有个使用习惯要说清:如果你用 `docker run` 分别建容器,忘加同一 `--network`,它们就隔在不同的默认 bridge 里、各抢各的映射、互相找不着。而当你用 docker-compose 编排一组服务时,compose 会自动为这个项目建一个网络、让服务按名互访——这也是为什么我前面写 compose 时总强调"用服务名做连接地址"。把"自定义网络 + 服务名 DNS"这套用起来,容器间的依赖关系就从"猜 IP"变成"声明式名字"。
三、host 网络与"对外发布"的取舍
除了 bridge,Docker 还有 `host` 网络——容器不单独拿虚拟 IP,而是直接借宿主机的网络栈,容器里的端口就是宿主机的端口。它的好处是几乎没有网络层开销、延迟低、也不需要端口映射;代价是丧失了隔离——容器不再有独立 IP 和网络命名空间,这和你想要的"几个服务各自端口清晰"的整洁感是冲突的。
# host 网络示例:容器直接用宿主机的 80 端口,不用 -p
docker run -d --network host --name web nginx
什么时候值得用 host?对"性能敏感、希望直接用宿主机端口、且不怕网络隔离变弱"的场景(比如一些代理、metrics 采集,像前面 Prometheus 里的 node-exporter 就常用 host 网络直接暴露宿主机指标)。对大多数常规应用,bridge + `-p` 反而更干净、更可管理。选择取决于你更在意"隔离整洁"还是"极致低开销",不要无脑全都 host。
四、跨主机互联:真正难点在 overlay 与"分布式覆叠网络"
单机内通信是基础,真正的进阶在"把两台 VPS 上的容器连成一张网"。跨主机的容器互通,Docker 提供 `overlay` 网络——它建立在节点间已有的底层网络之上,用 VXLAN 之类技术把跨机器的容器"加密地像在同一张内网里";每个自定义 overlay 网络内部,跨主机的容器也能用容器名互访。实操上,overlay 通常配合 Docker Swarm 或 Kubernetes 这类编排来用(Kubenernetes 里 CNI 那层就是类似思路),Docker 直接跨主机建 overlay 要通过 swarm 集群或路由方式组起来。
# 以 Docker Swarm 为例:把两台机器组成一个集群后,就能建跨主机 overlay
docker swarm init # 在 manager 上初始化
docker swarm join --token TOKEN IP # 在另一台执行加入
docker network create -d overlay --attachable myoverlay
# 之后跨主机的容器放进 myoverlay,即可按名字互访
把这两件事区分开很重要:前者(bridge + 端口映射)解决"单机内服务编排",后者(overlay + 编排)解决"跨机成集群"。对大多数自托管,一开始根本不用碰 overlay——先老老实实把单机的网络与容器通信捋顺、用 compose 编排好,等真的需要"多台机器像一台"时再上 overlay/编排,而不是开局就陷入网络模型的大坑里。
五、从网络配置想到的:别让"端口和容器"变成一团乱麻
把 Docker 网络用顺,本质是建立几个习惯:一是把"端口映射"和"网络归属"分开想,别一有问题就先怀疑是不是要加 `-p`;二是让相关容器进同一个自定义网络、用服务名互访,而不是散落并靠 IP;三是清楚 host 网络是以"牺牲隔离"换性能,按需而不是默认用;四是跨主机再考虑 overlay 和编排,单机组网先别过度设计。
最后补一个和之前一个主题呼应的提醒:网络配置和容器的数据一样,都是"状态"——把网络定义写进 compose/代码(声明式),而不是每次手工 `docker network create` 东建一个西建一个,才不会在时间长了之后出现"这个服务到底连的哪张网、为什么重启就连不上了"的谜题。网络和此前说起过的资源、卷一样,都该由"可复现的编排文件"来承载。
结语
Docker 网络的核心,是弄明白三种网络模型和"网络"与"端口映射"的区别:默认 bridge 提供单机隔离与 `-p` 发布,自定义网络带来容器"用名字互访"的 DNS,host 换隔离却换来低开销,跨主机的 overlay 则属于"多机组集群"的进阶范畴。落地收起三个习惯:用 `docker network` 排查归属、让相关容器同网并用服务名互访、把网络声明写进 compose。当你把"这个容器该连谁、端口该由谁的 `-p` 发布"都想清楚了,Docker 从"能跑"再往前走一步,就是在网络这一层变得可预测、可复现,而不是靠一遍遍踩 "为什么连不上" 的坑。


