什么是 Docker 网络
每个容器启动后都有自己独立的网络栈(IP 地址、网卡、路由表),容器之间以及容器与宿主机、外部网络之间默认是隔离的。Docker 网络就是 Docker 提供的一套机制,用来管理这些容器的网络连接与隔离策略。
为什么需要 Docker 网络?
如果没有网络机制,容器之间就是完全孤立的黑盒。现实中的应用往往由多个容器协作完成,例如:
- Web 服务容器需要访问数据库容器
- 多个微服务之间需要互相调用
- 容器需要对外暴露端口提供服务
仅靠 IP 地址硬编码来通信既脆弱又难以维护——容器重启后 IP 可能变化。Docker 网络提供了按名称寻址的能力,让容器可以用容器名直接互相访问,而不依赖具体 IP。
Docker 网络能做什么?
- 容器间通信:同一网络内的容器可以直接通过容器名互访。
- 网络隔离:不同网络中的容器默认不能互通,实现服务间的安全隔离。
- 端口映射:将容器内端口暴露到宿主机,供外部访问。
- 多网络接入:一个容器可以同时加入多个网络,充当不同服务组之间的桥梁。
默认网络模式
bridge
bridge 是 Docker 的默认网络模式,容器启动时如果不指定网络,就会自动接入名为 bridge 的默认桥接网络。
工作原理:
Docker 在宿主机上创建一个虚拟网桥 docker0,每个接入 bridge 网络的容器都会分配一个虚拟网卡(veth 对),一端连接容器,一端挂在 docker0 上。容器之间通过这个虚拟网桥转发流量,容器访问外网则通过宿主机的 NAT 出去。
宿主机
├── docker0(虚拟网桥,如 172.17.0.1)
│ ├── veth1 ←→ 容器 A eth0(172.17.0.2)
│ └── veth2 ←→ 容器 B eth0(172.17.0.3)
└── eth0(物理网卡)→ 外网
端口映射:
bridge 模式下容器的端口对外不可见,需要通过 -p 显式映射到宿主机端口:
# 将容器的 80 端口映射到宿主机的 8080 端口
docker run -d -p 8080:80 nginx
外部访问 宿主机IP:8080 即可到达容器的 80 端口。
默认 bridge 网络的局限:
默认 bridge 网络中的容器不支持按容器名互访,只能通过 IP 通信(或使用已废弃的 --link)。要实现按名寻址,需要使用自定义 bridge 网络(见第 3 节)。
host
host 模式下,容器直接共享宿主机的网络栈,不再拥有独立的网卡和 IP,容器内的网络配置与宿主机完全相同——包括 IP 地址、网卡、端口空间和路由表。在容器内执行 ip addr 看到的结果与宿主机上完全一致。
docker run -d --network host nginx
此时 nginx 监听的是宿主机的 80 端口,无需 -p 映射,外部直接访问 宿主机IP:80 即可。
优点: 网络性能最好,没有 NAT 和 veth 的额外开销,适合对网络延迟敏感的场景。
缺点:
- 容器与宿主机端口共享,容易产生端口冲突。
- 网络不再隔离,容器可以直接操作宿主机网络接口,安全性较低。
- 仅在 Linux 上生效,macOS / Windows 上 Docker 运行在虚拟机内,host 模式连接的是虚拟机网络而非真实宿主机网络。
none
none 模式下,容器拥有独立的网络命名空间,但不配置任何网络接口,只有一个本地回环 lo,完全无法与外部通信。
docker run -d --network none alpine
容器内执行 ip addr 只会看到:
1: lo: <LOOPBACK,UP,LOWER_UP> ...
适用场景: 对安全隔离要求极高、不需要任何网络访问的任务,例如纯计算类批处理作业,或需要手动配置网络的高级场景。
container
container 模式让一个容器直接共享另一个容器的网络栈,两者拥有相同的 IP、网卡和端口空间。
# 先启动一个容器
docker run -d --name app nginx
# 新容器共享 app 的网络
docker run -it --network container:app alpine
此时在第二个容器里执行 ip addr,看到的 IP 和 app 容器完全一样,两个容器可以通过 localhost 直接互访。
典型应用场景: Kubernetes 的 Pod 就是基于这个原理实现的——Pod 内所有业务容器共享同一个 pause 容器的网络命名空间,因此 Pod 内容器之间可以直接用 localhost 通信。
自定义 bridge 网络
前面提到,默认 bridge 网络不支持按容器名互访。解决办法是创建一个自定义 bridge 网络,加入同一自定义网络的容器可以直接用容器名作为域名互相访问,Docker 内置了 DNS 解析。
创建自定义网络:
最简单的创建方式:
docker network create my-net
Docker 默认使用 bridge 驱动,并自动分配一个子网(如 172.18.0.0/16)。也可以通过参数完整指定网络配置:
docker network create \
--driver bridge \
--subnet 192.168.100.0/24 \
--gateway 192.168.100.1 \
--ip-range 192.168.100.128/25 \
my-net
各参数说明:
--driver bridge:网络驱动类型,默认就是bridge,也支持overlay(跨主机)、macvlan等。--subnet:指定网络使用的子网段,容器 IP 从这个范围内分配。--gateway:网关 IP,通常是子网的第一个地址。--ip-range:在 subnet 内进一步限制自动分配的 IP 范围,避免与手动指定的 IP 冲突。上例中只有192.168.100.128~192.168.100.255这段会被自动分配。
启动容器并加入自定义网络:
docker run -d --name db --network my-net mysql:8
docker run -d --name app --network my-net nginx
此时 app 容器可以直接用 db 这个名字访问 MySQL,无需关心 IP:
# 在 app 容器内
ping db # 可以通
mysql -h db ... # 可以连
与默认 bridge 网络的对比:
| 默认 bridge | 自定义 bridge | |
|---|---|---|
| 容器名互访 | 不支持 | 支持(内置 DNS) |
| 网络隔离 | 所有容器共用 | 每个网络独立隔离 |
| 推荐程度 | 不推荐用于多容器协作 | 推荐 |
一个容器加入多个网络:
容器创建后也可以动态加入其他网络:
docker network connect other-net app
这样 app 同时处于 my-net 和 other-net 中,可以充当两个网络之间的桥梁。
容器间通信
容器之间通信主要有以下几种方式,按推荐程度从高到低排列:
方式一:自定义 bridge 网络 + 容器名(推荐)
最常用、最简洁的方式。把相关容器加入同一个自定义网络,直接用容器名互访:
docker network create app-net
docker run -d --name db --network app-net mysql:8
docker run -d --name web --network app-net nginx
# web 容器内
curl http://db:3306
方式二:网络别名(network-alias)
一个容器可以在网络中拥有多个别名,便于服务伪装和多版本共存:
docker run -d --name db1 --network app-net --network-alias mysql --network-alias database mysql:8
此时其他容器既可以用 db1,也可以用 mysql 或 database 访问它。
方式三:跨网络通信
不同网络的容器默认隔离。如果确实需要跨网络通信,可以让一个容器同时连接两个网络:
docker network connect other-net web
方式四:通过宿主机端口映射
容器对外暴露端口后,其他容器可以通过 宿主机IP:端口 访问。这种方式绕过了 Docker 网络,主要用于让外部客户端访问容器服务,不推荐用于内部容器通信。
docker run -d -p 3306:3306 mysql:8
# 其他容器通过 宿主机IP:3306 访问
方式五:container 模式共享网络栈
如前所述,多个容器共享同一个网络命名空间,通过 localhost 互访。适合 sidecar 模式。
管理命令
Docker 网络的管理命令都在 docker network 子命令下,用法与 docker volume 类似。
docker network create
创建一个自定义网络(前面已详细介绍):
docker network create my-net
docker network ls
列出所有网络:
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
1a2b3c4d5e6f bridge bridge local
7g8h9i0j1k2l host host local
3m4n5o6p7q8r none null local
9s0t1u2v3w4x my-net bridge local
bridge、host、none 是 Docker 内置的三个默认网络,无法删除。
docker network inspect
查看某个网络的详细信息,包括子网、网关、连接的容器列表:
$ docker network inspect my-net
[
{
"Name": "my-net",
"Driver": "bridge",
"IPAM": {
"Config": [
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1"
}
]
},
"Containers": {
"abc123...": {
"Name": "db",
"IPv4Address": "172.18.0.2/16"
},
"def456...": {
"Name": "web",
"IPv4Address": "172.18.0.3/16"
}
}
}
]
排查容器网络问题时,这个命令是首选——能直接看到哪些容器接入了网络,分别是什么 IP。
docker network connect / disconnect
将容器加入或移出某个网络:
# 让 web 容器同时加入 other-net
docker network connect other-net web
# 将 web 容器从 other-net 中移除
docker network disconnect other-net web
底层发生了什么?
每次 connect 一个网络,Docker 都会在容器内新增一块虚拟网卡(如 eth1、eth2),并分配该网络对应子网中的一个 IP。容器同时拥有多个网卡,每张网卡对应一个网络。
举个例子,一个 web 容器同时加入 app-net 和 db-net 后,进入容器执行 ip addr 会看到:
eth0: 172.18.0.3/16 # app-net
eth1: 172.19.0.4/16 # db-net
典型用途:跨网络桥接
不同的自定义网络之间默认是隔离的,互相不能访问。当某个容器需要访问多个网络中的服务时(例如一个 API 网关既要连接前端网络,又要连接后端数据库网络),就用 docker network connect 把它接入多个网络,让它充当两个网络之间的桥梁。
前端网络 frontend-net 后端网络 backend-net
┌───────┐ ┌────────┐
│ web │ │ db │
└───┬───┘ └────┬───┘
│ │
└─────────┬─────────────────────┘
│
┌───┴───┐
│ api │ ← 同时连接两个网络
└───────┘
disconnect 则相反,会移除该网卡,容器立即失去对应网络的访问能力。整个过程无需重启容器,可以在运行时动态调整。
docker network rm
删除一个或多个自定义网络:
docker network rm my-net
注意: 仍有容器连接的网络无法删除,需要先停止或断开相关容器。内置的 bridge、host、none 也无法删除。
docker network prune
批量删除所有未被任何容器使用的自定义网络:
docker network prune
执行前会有确认提示,常用于清理开发环境中遗留的废弃网络。
