Docker 网络原理:bridge/host/overlay + namespace,彻底搞懂容器通信
前言
你有没有想过这个问题:
你的手机和电脑连的是同一个 WiFi,它们之间可以互相访问。但你的电脑上同时跑着十几个 Docker 容器,它们是怎么"认识"彼此的?为什么容器 A 能 ping 通容器 B,却不能直接被外网访问?
答案就藏在 Linux 网络 namespace 和虚拟网桥里。
一、先从生活类比说起
你家里有一台路由器,手机、电脑、平板都连在上面。路由器给每个设备分配了一个局域网 IP(比如 192.168.1.x),设备之间可以互相通信,但对外网来说,它们都躲在路由器后面,外网只能看到路由器的公网 IP。
Docker 的 bridge 网络做的事情完全一样:
- Docker 在宿主机上创建一个虚拟路由器(
docker0网桥) - 每个容器分配一个虚拟局域网 IP(
172.17.0.x) - 容器之间通过
docker0互相通信 - 容器访问外网时,由宿主机做 NAT 转发,就像路由器帮你"出去"一样
二、容器网络隔离的底层:Linux namespace
在聊三种网络模式之前,必须先理解一个概念——Linux namespace。
namespace 是 Linux 内核提供的一种隔离机制,Docker 就是靠它实现"每个容器以为自己是独立的系统"这个效果的。一共有 6 种 namespace:
| namespace | 隔离的内容 |
|---|---|
net |
网络设备、IP、端口、路由表 |
pid |
进程 ID,容器内看不到宿主机进程 |
mnt |
文件系统挂载点 |
uts |
主机名和域名 |
ipc |
进程间通信 |
user |
用户和用户组 ID |
网络 namespace(net ns)是本文的核心。
每个容器启动时,Docker 都会给它创建一个独立的 net namespace,相当于给容器一张"独立的网卡"。容器内部看到的 eth0 是虚拟的,和宿主机的物理网卡完全隔离。
验证容器网络隔离:
首先查看当前运行中的容器:
bash
docker ps
找到两个运行中的容器名后(没有容器可以自己创建几个,文章下面创建自定义 bridge 网络部分也有容器创建的指令),分别进入查看网络接口:
bash
# 将 <容器名A> 和 <容器名B> 替换为 docker ps 查询出来的实际容器名
docker exec -it <容器名A> ip addr
docker exec -it <容器名B> ip addr
你会看到每个容器内只有两个网卡:
lo(loopback 回环网卡):固定 IP 127.0.0.1,只用于容器自己和自己通信。比如程序里写 localhost 访问自身服务就走这里,数据不经过任何网络直接回到自己。
eth0(容器虚拟网卡):Docker 分配的局域网 IP,比如 172.23.0.17。对比两个容器的 eth0 IP 会发现各不相同——这就是 net namespace 隔离的直观体现,每个容器都有自己独立的网络视图。eth0@if46 里的 if46 是 veth pair 在宿主机一端的编号,两端绑定,数据从这头进那头出。
⚠️ Windows 用户注意:Docker Desktop 运行在 WSL2 虚拟机内,
docker0网桥存在于 WSL2 里而非 Windows 层,所以ipconfig看不到它。这是正常现象,不影响容器网络正常工作。
三、bridge 模式(默认)
原理:虚拟网桥 + veth pair
bridge 是 Docker 默认的网络模式。它的底层实现依赖两个关键组件:
① docker0 网桥
Docker 启动时会在宿主机上创建一个名为 docker0 的虚拟网桥,默认 IP 是 172.17.0.1。所有使用 bridge 模式的容器都连接到这个网桥上,就像把网线插进同一台交换机。
② veth pair(虚拟网卡对)
每启动一个容器,Docker 就创建一对虚拟网卡(veth pair)——一端放进容器里叫 eth0,另一端留在宿主机上连接到 docker0 网桥。
数据从容器 eth0 发出 → 经过 veth 对端 → 到达 docker0 网桥 → 转发给目标容器。
容器 A (172.17.0.2) 容器 B (172.17.0.3)
eth0 eth0
| |
veth0a ---- docker0 网桥 ---- veth0b
|
宿主机 eth0(物理网卡)
|
外网
容器访问外网怎么走?
靠 iptables 的 NAT 规则。容器发出的包到达 docker0 后,iptables 把源 IP 从 172.17.0.x 替换成宿主机的公网 IP,再发出去。响应包回来时再反向替换。这就是为什么外网看不到容器 IP,只能看到宿主机 IP。
什么是 iptables?
iptables 是 Linux 内核内置的防火墙/数据包处理工具,可以对经过网络的数据包做拦截、修改、转发、丢弃等操作。Docker 启动时会自动往 iptables 里写一批规则,你不需要手动配置,但理解它能帮你搞清楚数据包的流向。
什么是 NAT?
NAT(Network Address Translation,网络地址转换)就是把数据包里的 IP 地址替换掉。分两种:
- SNAT(源地址转换):把发出去的包的源 IP 替换成宿主机 IP,让外网以为是宿主机在发请求,容器 IP 对外不可见。
- DNAT(目标地址转换):把进来的包的目标 IP 替换成容器 IP,这就是
-p 8080:80端口映射的底层实现——外网访问宿主机 8080,iptables 把目标地址改成容器的172.17.0.x:80,转发进去。
所以 -p 端口映射的底层就是 iptables DNAT,不是什么魔法。
常用命令:
bash
# 查看所有网络
docker network ls
# 创建自定义 bridge 网络
docker network create my-network
# 删除网络
docker network rm my-network
# 删除所有未使用的网络
docker network prune
# 运行容器并加入指定网络(-d 后台运行)
docker run -d --network my-network --name app nginx
# 停止并删除容器
docker stop app && docker rm app
# 将已有容器加入网络
docker network connect my-network <容器名>
# 将容器从网络中移除(容器还在,只是退出这个网络)
docker network disconnect my-network <容器名>
# 查看网络详情(能看到哪些容器在里面)
docker network inspect my-network
实战:用自定义网络控制容器通信
下面用一个例子说明如何通过网络实现容器隔离——同一网络内的容器可以互相访问,不同网络的容器完全隔离:
bash
# 第一步:创建两个自定义网络
docker network create net-a
docker network create net-b
# 第二步:启动三个容器,container1 和 container2 在 net-a,container3 在 net-b
docker run -d --name container1 --network net-a nginx
docker run -d --name container2 --network net-a nginx
docker run -d --name container3 --network net-b nginx
# 第三步:进入 container1,测试通信
docker exec -it container1 curl http://container2 # 能通,同在 net-a
docker exec -it container1 curl http://container3 # 不能通,不同网络
# 清理
docker stop container1 container2 container3
docker rm container1 container2 container3
docker network rm net-a net-b
这个例子说明了三件事:
- 自定义网络内支持容器名 DNS 解析,直接用名字访问不需要知道 IP
- 不同网络之间默认完全隔离,无法互相访问
- 网络隔离是天然的安全边界——比如数据库容器只加入后端服务的网络,前端容器根本访问不到数据库
💡 实际开发建议:不要用默认的
docker0,自己创建 bridge 网络。自定义 bridge 网络支持容器名 DNS 解析,这也是 Docker Compose 里DB_HOST=mysql能生效的原因。默认docker0不支持这个特性。
四、host 模式
原理:跳过 namespace,直接共享宿主机网络栈
host 模式下,容器不会创建独立的 net namespace,而是直接使用宿主机的网络接口:
bash
docker run -d --network host nginx
这时容器内的网络接口和宿主机完全一样,容器监听的端口就是宿主机的端口,不需要 -p 端口映射。
适合什么场景?
- 对网络性能要求极高的场景(省去了 NAT 转发开销)
- 需要监听宿主机所有网络接口的工具类容器
缺点:
- 没有网络隔离,容器可以看到宿主机所有网络接口
- 端口冲突风险高,两个容器不能监听同一个端口
- 仅支持 Linux,Windows/Mac 上的 Docker Desktop 底层是虚拟机,host 模式共享的是虚拟机的网络栈而非真正的宿主机网络,效果和预期不同
五、overlay 模式
原理:跨主机容器网络,VXLAN 封装
bridge 和 host 都是单机模式,overlay 是用来解决多台宿主机上的容器互相通信的问题。
场景:你有三台服务器,每台上跑着几个容器,怎么让不同机器上的容器像在同一个局域网里一样通信?
overlay 的做法是在物理网络之上构建一层虚拟网络,用 VXLAN 协议把容器的数据包封装进 UDP 包,通过物理网络传输,到达目标机器后再解封装交给目标容器。对容器来说,感觉像直接通信,底层的跨机器传输是透明的。
bash
机器 A 机器 B
容器1 (10.0.0.2) 容器2 (10.0.0.3)
| |
VXLAN 封装 VXLAN 解封装
| |
eth0 ——— 物理网络 ————————— eth0
什么时候用?
- Docker Swarm 集群
- Kubernetes 的 CNI 网络插件(Flannel、Calico 底层都用了类似思路)
overlay 的详细原理会在后面 K8s 网络那篇深入讲,这里先有个印象。
六、三种模式对比
| bridge | host | overlay | |
|---|---|---|---|
| 网络隔离 | 有 | 无 | 有 |
| 跨主机通信 | 不支持 | 不支持 | 支持 |
| 性能 | 中(有 NAT 开销) | 高(无 NAT) | 中低(有封装开销) |
| 端口映射 | 需要 -p |
不需要 | 不需要 |
| 适用场景 | 日常开发、单机部署 | 高性能工具容器 | 多机集群 |
| DNS 服务名解析 | 自定义网络支持 | 不支持 | 支持 |
七、结合 Docker Compose 看网络
回到我们上一篇的项目,docker-compose.yaml 里所有服务都在同一个 Compose 项目下,Docker 会自动给它们创建一个自定义 bridge 网络(名字是 项目名_default)。
这就是为什么:
yaml
environment:
- DB_HOST=mysql # 直接写服务名就能找到 mysql 容器
- REDIS_HOST=redis # 同理
服务名就是 DNS 名,Compose 的自定义 bridge 网络内置了 DNS 解析,容器之间用服务名通信,Docker 自动帮你解析成对应的容器 IP。
总结
| 概念 | 作用 |
|---|---|
| net namespace | 给每个容器独立的网络视图 |
| veth pair | 连接容器 net namespace 和宿主机网桥的虚拟网线 |
| docker0 / 自定义 bridge | 容器间通信的虚拟交换机 |
| iptables SNAT | 容器访问外网时替换源 IP |
| iptables DNAT | -p 端口映射的底层实现 |
| overlay / VXLAN | 跨主机容器网络的封装协议 |
下一篇:Docker Volume 数据持久化——容器删了数据为什么还在,挂载原理是什么 🚀
📌 觉得有帮助点个赞!有问题欢迎评论区交流 👇
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)