目录
最近在学习、折腾 Docker,除了把容器跑起来,也想弄清楚它背后的网络:容器为什么能访问外网?两个容器应该通过什么地址通信?为什么有时需要映射端口,有时又可以直接用容器 IP 访问?
跟着 NetworkChuck 的视频研究了一遍,把几种网络模式和容易混淆的地方整理在这里,方便以后配置和排查时查阅。
下面主要以 Linux 上的 Docker Engine 和 IPv4 网络为背景。Docker Desktop 中还有一层虚拟机,不能把原生 Linux 上的网络行为直接套用过去。
一、先理清:Docker 网络模式决定了什么?#
容器中的进程需要网卡、IP、路由和端口,才能与其他进程通信。
在常见的独立容器网络中,Docker 通过 Linux 的 Network Namespace,也就是网络命名空间,为容器提供独立的网络环境,再通过不同的网络驱动,把它连接到宿主机或外部网络。
理解各种模式,可以先抓住三个问题:
- 容器拥有独立的网络栈,还是共享宿主机的网络栈?
- 容器 IP 属于 Docker 内部子网,还是现有局域网?
- 数据包通过网桥、路由,还是跨主机隧道到达目标?
另外,“网络驱动”和“网络实例”需要分开看:
docker network ls通常会看到:
NAME DRIVER
bridge bridge
host host
none nullNAME 是网络名称,DRIVER 是底层驱动。默认的 bridge 网络和自己创建的 Bridge 网络,都使用 bridge 驱动;IPvlan L2 和 L3,则是 ipvlan 驱动的不同工作模式。
二、Bridge:在宿主机内部连接容器#
Bridge 是最常见的 Docker 网络驱动。
不指定网络运行普通容器时,默认会连接名为 bridge 的网络。在 Linux 上,它通常对应宿主机上的 docker0 网桥。
Docker 通过一对虚拟网卡,也就是 veth pair,将容器连接到网桥上。可以把这个网桥理解为宿主机里的一台虚拟交换机:
容器 A:eth0 ── veth ──┐
├── docker0 ── 宿主机网络 ── 外部网络
容器 B:eth0 ── veth ──┘每个容器拥有自己的网络命名空间,使用独立的 IP、接口和路由。同一 Bridge 网络中的容器,默认可以互相通信。
1. 容器如何访问外网?#
默认情况下,Docker 会通过 NAT,让容器借助宿主机访问外网:
容器 IP → 宿主机进行源地址转换 → 外部网络因此,外部服务器看到的通常是宿主机的地址,而不是容器内部的地址。
容器 IP 由 Docker 的 IPAM 管理和分配,并不是向家里的路由器申请 DHCP 租约。
2. 外部如何访问容器?#
容器能访问外网,不代表局域网中的设备就能主动访问容器。
最常见的处理方式是发布端口:
docker run -d \
--name web \
-p 8080:80 \
nginx:alpine-p 8080:80 表示:
宿主机 8080 端口 → 容器 80 端口局域网设备访问:
http://宿主机IP:8080这里有几个容易混淆的点:
- 同一 Bridge 网络中的容器互访,通常不需要
-p。 - 镜像中的
EXPOSE是端口声明,不等于发布端口。 - 原生 Linux 宿主机通常可以直接访问 Bridge 容器 IP,不能将它与局域网中的其他设备混为一谈。
- 应用需要监听容器可访问的接口;如果只监听容器内的
127.0.0.1,发布端口也不能让请求正常进入应用。
以上讨论的是默认配置;另外配置直接路由、防火墙规则时,访问方式可以不同。Bridge 官方文档
3. 为什么更适合使用自定义 Bridge?#
默认 Bridge 能用,但实际组织应用时,我会优先创建自定义网络:
docker network create app-net启动 Web 容器:
docker run -d \
--name web \
--network app-net \
-p 8080:80 \
nginx:alpine从同一网络中的临时容器访问它:
docker run --rm \
--network app-net \
busybox wget -qO- http://web这里直接用 web 作为地址,因为自定义 Bridge 提供容器名称和网络别名的自动解析。
默认 bridge 不具备同样的自动容器名解析能力,通常只能使用 IP,或依赖旧的 --link 等方式。
自定义网络还可以把不同项目分开。Web、API、数据库属于同一个项目,就放进同一个网络;不相关的应用使用各自的网络。
对普通单机应用,自定义 Bridge 是一个很合适的起点。默认与自定义 Bridge 的区别
三、Host:直接共享宿主机网络#
Host 模式的思路更直接:
docker run -d \
--name web-host \
--network host \
nginx:alpine容器共享宿主机的网络命名空间,不再拥有独立的容器网络 IP。
容器中的 Nginx 监听 80 端口,相当于直接在宿主机网络中监听 80。因此不需要端口映射,-p 也不会起作用。
这减少了 Bridge 和 NAT 带来的网络处理,但也有两个直接影响:
- 网络隔离减少,容器可以使用宿主机的网络环境。
- 端口需要统一管理,不能与宿主机上的其他服务冲突。
例如,宿主机上已有服务绑定同一地址的 80 端口,再启动这个 Nginx 就可能失败。
这里共享的是网络命名空间,不代表文件系统、进程等其他隔离也全部取消。
本文中的 Host 用法以原生 Linux 为前提,不直接适用于当时的 Docker Desktop for Mac、Windows 环境。Host 官方文档
四、Macvlan:让容器像一台局域网设备#
Bridge 模式通常通过“宿主机 IP + 映射端口”提供服务。
Macvlan 则可以让每个容器拥有自己的 MAC 地址和局域网 IP。从其他设备的视角看,它就像一台直接接入物理网络的设备。
例如:
路由器: 192.168.1.1
宿主机: 192.168.1.10
容器 A: 192.168.1.201
容器 B: 192.168.1.202局域网设备可以直接访问容器 IP,不需要通过宿主机发布端口。
1. 创建一个 Macvlan 网络#
假设物理网卡为 eth0,局域网为 192.168.1.0/24:
docker network create -d macvlan \
--subnet=192.168.1.0/24 \
--gateway=192.168.1.1 \
--ip-range=192.168.1.192/27 \
-o parent=eth0 \
lan-macvlan几个参数的含义:
| 参数 | 作用 |
|---|---|
--subnet | 容器接入的子网 |
--gateway | 该子网的网关 |
--ip-range | Docker 自动分配地址的范围 |
parent | 承载这个网络的宿主机接口 |
启动容器:
docker run -d \
--name web-lan \
--network lan-macvlan \
--ip 192.168.1.201 \
nginx:alpine配置正确后,局域网设备可以直接访问:
http://192.168.1.201示例中的网卡名和地址需要按实际环境修改。用于容器的地址范围,也要从局域网 DHCP 地址池中排除,并确认没有其他设备使用。
2. Macvlan 容易踩的几个坑#
上游网络需要允许一个端口出现多个 MAC。
多个容器的 MAC 会通过同一个物理接口进入交换机。端口安全策略、虚拟交换机和某些云网络可能限制这种行为。在虚拟机里实验时,还可能需要调整混杂模式等设置。
宿主机默认不能直接访问自己的 Macvlan 容器。
这是 Linux 内核机制带来的限制。出现“其他电脑能访问,宿主机自己却访问不了”的情况,不一定是应用有问题。
需要互访时,可以增加宿主机侧的 Macvlan 接口并配置路由,或者给容器再连接一个 Bridge 网络。
Docker 不会自动与路由器协调地址。
--ip-range 只约束 Docker 的地址分配,不会通知路由器预留地址。地址冲突仍然需要自己避免。Macvlan 官方文档
3. Macvlan 与 802.1Q VLAN#
视频还介绍了通过 VLAN 子接口连接 Macvlan 的方式。
例如,父接口使用:
eth0.20对应 VLAN 20;使用 eth0.30,则对应 VLAN 30。
这允许多个容器网络通过一条物理链路接入不同 VLAN。不过,宿主机配置只是其中一环,上游交换机端口也必须配置 Trunk,并允许对应 VLAN 通过。
另外,Macvlan 的 bridge 工作模式,与 Docker 的 bridge 网络驱动是两个不同概念。Macvlan 802.1Q 配置说明
五、IPvlan L2:保留独立 IP,共享 MAC#
IPvlan L2 和 Macvlan 的使用目标比较接近,都可以让容器直接接入现有网络。
最直观的区别是 MAC 地址:
| 对比项 | Macvlan | IPvlan L2 |
|---|---|---|
| 容器 IP | 各自独立 | 各自独立 |
| 对外使用的 MAC | 每个容器不同 | 共享父接口的 MAC |
| 上游交换机看到的情况 | 一个端口后有多个 MAC | 多个 IP 对应同一个 MAC |
如果交换机限制单个端口后面的 MAC 数量,IPvlan L2 就值得考虑。
创建示例:
docker network create -d ipvlan \
--subnet=192.168.1.0/24 \
--gateway=192.168.1.1 \
--ip-range=192.168.1.224/27 \
-o parent=eth0 \
-o ipvlan_mode=l2 \
lan-ipvlan这里同样要提前预留地址范围。
共享 MAC 并不代表共享 IP,也不能省去网段、VLAN 和地址规划。IPvlan 同样存在容器与宿主机默认隔离的行为,不能假定它会自动解决宿主机互访的问题。IPvlan 官方文档
六、IPvlan L3:通过路由连接容器子网#
IPvlan L3 把关注点从二层接入转向了三层路由。
容器可以使用独立于物理局域网的子网,由宿主机承担连接容器网络与外部网络的路由角色。
假设:
物理局域网: 192.168.1.0/24
Docker 宿主机: 192.168.1.10
容器子网: 10.20.0.0/24创建网络:
docker network create -d ipvlan \
--subnet=10.20.0.0/24 \
-o parent=eth0 \
-o ipvlan_mode=l3 \
routed-net启动容器:
docker run -d \
--name web-routed \
--network routed-net \
--ip 10.20.0.5 \
nginx:alpine网络虽然创建好了,局域网中的其他设备却不一定知道如何到达 10.20.0.5。
通常需要在上游路由器中增加:
目标网段:10.20.0.0/24
下一跳: 192.168.1.10它表达的意思是:访问这个容器子网的流量,交给 Docker 宿主机。
IPvlan L3 不在容器网络中转发二层广播和组播,也不会自动提供 Bridge 默认出网时的 NAT。因此,访问互联网还需要考虑回程路由,以及由哪个上游设备执行必要的 NAT。
这一模式下,“创建成功”和“网络连通”之间,还差完整的路由设计。排查时必须同时看数据包怎么出去、怎么回来。IPvlan L3 官方说明
七、Overlay:连接不同宿主机上的容器#
前面的讨论主要围绕单台 Docker 宿主机。Overlay 解决的是跨宿主机通信。
它在底层网络之上建立逻辑网络,让分布在不同宿主机上的容器能够通过这个网络通信。
Docker 内置的 Overlay 网络依赖 Swarm。创建允许独立容器加入的网络,可以使用:
docker network create \
-d overlay \
--attachable \
app-overlay这需要在已经初始化的 Swarm 管理节点上执行。相关宿主机也要加入同一个 Swarm,并满足节点之间的端口和网络要求。
暂时只有单机部署需求时,先把 Bridge 理解清楚就够了。Overlay 可以等到需要多机部署时再深入。Overlay 官方文档
八、None:不连接外部网络#
None 模式很简单:
docker run --rm \
--network none \
alpine ip addr容器保留独立的网络命名空间,但默认只有回环接口 lo,没有正常连接外部网络的接口。
它适合不需要联网的任务,也可以作为手动配置容器网络的起点。这里限制的是网络连接,不能直接等同于完整的安全沙箱。None 官方文档
九、选型时先看需求#
| 需求 | 优先考虑 |
|---|---|
| 单机部署 Web、API、数据库等应用 | 自定义 Bridge |
| 需要直接共享宿主机网络 | Host |
| 容器需要独立局域网 IP 和 MAC | Macvlan |
| 容器需要独立局域网 IP,希望减少 MAC 数量 | IPvlan L2 |
| 容器使用独立子网,通过路由接入 | IPvlan L3 |
| Swarm 环境中的跨主机通信 | Overlay |
| 不需要外部网络连接 | None |
对我目前学习和单机部署的需求,自定义 Bridge 可以作为默认选择。有了独立局域网 IP、共享宿主机网络或者跨主机通信的明确需求,再考虑其他模式。
十、留作回查的命令和排查顺序#
查看网络:
docker network ls
docker network inspect app-net查看容器使用的网络模式和地址:
docker inspect -f '{{.HostConfig.NetworkMode}}' web
docker inspect -f '{{json .NetworkSettings.Networks}}' web查看发布的端口:
docker port web给运行中的容器连接或断开自定义网络:
docker network connect app-net 容器名
docker network disconnect app-net 容器名在 Linux 宿主机中查看接口和路由:
ip addr
ip route如果容器镜像中带有相应工具,也可以进入容器检查:
docker exec 容器名 ip addr
docker exec 容器名 ip route遇到网络不通,按下面的顺序检查:
- 确认访问路径。 访问的是容器 IP,还是宿主机发布的端口?
- 确认网络归属。 两个容器是否在同一网络?跨网络通信是否有相应配置?
- 区分 DNS 与连通性。 名称解析失败和 IP 不通是不同的问题。
- 确认应用监听。 服务是否启动?监听的是哪个地址和端口?
- 检查转发路径。 端口发布、防火墙、去程和回程路由是否完整?
- 检查物理网络。 使用 Macvlan、IPvlan 时,再看父接口、VLAN、地址冲突及上游设备限制。
这次学习最值得记下来的,是沿着数据包的路径理解配置。只要能说清楚一个请求从哪里出发、经过哪些接口、目标又如何返回,Docker 网络里的这些名称就容易对应到实际问题上。

