Docker 网络是容器之间通信的基础设施,为容器提供安全、可控的互联能力。在 Docker 中,每个容器都可以加入一个或多个网络,并通过这些网络进行通信,其方式与物理机或虚拟机在传统网络中的通信类似。
在学习各种网络类型之前,我们先来看看 Docker 中常用的网络相关命令。
docker network ls
docker network create [options] ${network_name}
可选的 options 有:
-d,--driver:指定驱动类型,默认是bridge,即单机上的自定义桥接网络。支持overlayDocker Swarm / 集群下跨主机网络,host共享宿主机网络(一般不通过 create 创建)
--subnet:指定网段docker network create --subnet 172.20.0.0/16 my-net
--ip-range:容器使用的 IP 范围docker network create \ --subnet 172.20.0.0/16 \ --ip-range 172.20.1.0/24 \ my-net...
docker network inspect ${network_name}
docker network connect ${network-name} ${container-name}
docker network disconnect ${network-name} ${container-name}
docker network rm ${network-name}
docker network prune
Bridge 网络使用 Docker 默认的 bridge 网络驱动。当你创建一个容器而不指定网络时,它会自动连接到默认的 bridge 网络。Bridge 网络在单机环境下使用非常广泛,它通过软件网桥实现容器间的通信。
安装 Docker 时,会自动在宿主机上创建一个名为 docker0 的虚拟网桥:
ifconfig
它就像一个虚拟交换机。默认的 bridge 网络一般使用 172.17.0.0/16 网段(若该网段已被占用,Docker 会自动选择其他可用网段,如 172.18.0.0/16)。每当启动一个容器,Docker 会为其创建一对 veth(虚拟以太网)设备:一端连接到容器内部作为 eth0 网卡,另一端连接到 docker0 网桥上。这样,所有连接到同一网桥的容器就处于同一个二层网络中,可以通过 IP 地址互相通信。容器访问外部网络时,流量经由 docker0 网桥转发,并通过宿主机的 NAT(iptables)规则进行地址转换后出站。
让我们先来看看默认 bridge 网络的行为。
我们先构建一个装有 ping、curl 等工具的 nginx 镜像,方便我们在容器内观察网络行为。首先构建镜像:
docker build -t my-nginx nginx
查看默认 bridge 网络信息:
docker network inspect bridge
启动两个容器,分别为 container1、container2:
docker run -d --name container1 my-nginx docker run -d --name container2 my-nginx
查看容器的网络配置:
docker inspect container1 -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
docker inspect container2 -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
请记下上面输出的
container2的 IP 地址,下一步会用到。
在容器 container1 中,尝试通过 IP 访问容器 container2(将 ${container2_ip} 替换为上一步查到的实际 IP):
docker exec -it container1 curl http://${container2_ip}
Docker 默认的 bridge 网络存在通信限制:容器间只能通过易变的 IP 地址互相访问,无法使用固定的容器名称作为域名进行通信。例如,下面的命令会报错 Domain name not found:
docker exec -it container1 curl http://container2
这种 IP 依赖会导致服务地址变更时需要人工调整配置,增加维护成本。通过创建自定义 bridge 网络,容器可通过稳定的名称直接互访,这种自动化的服务发现机制正是 Docker Compose 实现容器编排的基础——编排工具会自动创建专用网络,使多容器应用能够通过服务名称维持稳定的通信链路。
这一节内容,我们看看自定义 bridge 网络的优势。
首先创建自定义网络:
docker network create --driver bridge --subnet=172.20.0.0/16 --gateway=172.20.0.1 my-bridge-network
连续启动两个容器,连接到自定义网络 my-bridge-network 中:
docker run -d --name custom-container1 --network my-bridge-network my-nginx docker run -d --name custom-container2 --network my-bridge-network my-nginx
现在试试通过容器名称访问:
docker exec -it custom-container1 curl http://custom-container2
可以看到,在自定义 bridge 网络中,容器之间可以直接通过容器名称通信,不再需要依赖 IP 地址。
为什么自定义 bridge 网络支持容器名称解析,而默认 bridge 网络不行?
核心原因在于 DNS 服务的启用机制不同。自定义 bridge 网络会启用 Docker 内置的 DNS 服务器(127.0.0.11),容器可通过名称直接解析其他容器的 IP 地址;而默认的 bridge 网络(即 docker0 虚拟网桥)不提供容器名称解析功能,容器间只能通过 IP 地址通信。
我们可以通过查看容器内的 /etc/resolv.conf 来验证这一点。
自定义 bridge 网络中的容器:
docker exec custom-container1 cat /etc/resolv.conf
nameserver 127.0.0.11 options ndots:0
DNS 指向 Docker 内置 DNS 服务器
127.0.0.11,所以支持容器名称解析。
默认 bridge 网络中的容器:
docker exec container1 cat /etc/resolv.conf
nameserver xx.xx.xx.xx options ndots:0
DNS 直接使用宿主机的 DNS 配置,没有 Docker 内置 DNS 的支持,因此无法通过容器名称互访。
这个案例展示了在实际应用中如何使用自定义 bridge 网络。
首先,创建一个简单的 Web 应用。构建 web-app 镜像:
docker build -t web-app web-app
然后创建网络 web-app-network:
docker network create web-app-network
运行 Redis 容器:
docker run -d --name redis-server --network web-app-network redis:alpine
运行 Web 应用容器:
docker run -d --name web-app --network web-app-network -p 5000:5000 web-app
web-app应用中通过容器名称redis-server连接 Redis(详见web-app/app.py),这正是自定义 bridge 网络提供 DNS 解析的体现。
现在可以测试应用了:
# 访问应用
curl http://localhost:5000
# 多次访问,观察计数器增加
curl http://localhost:5000
curl http://localhost:5000
# 查看 Redis 中的数据
docker exec -it redis-server redis-cli get hits
清理环境,删除本案例创建的容器、网络和镜像。
Host 网络移除了容器和 Docker 主机之间的网络隔离,直接使用主机的网络。
特点:
接下来,我们使用 Host 网络运行 Nginx 服务器。注意这里不需要 -p 端口映射,因为 host 网络下容器直接使用宿主机的网络栈:
docker run -d --name nginx-host --network host my-nginx
对比 bridge 网络下需要
-p 80:80才能从宿主机访问容器服务,host 网络下容器的端口直接绑定在宿主机上。
直接在宿主机上访问 Nginx 服务(默认监听 80 端口):
curl http://localhost:80
由于容器与宿主机共享网络,端口冲突是 host 网络下需要特别注意的问题。当我们再启动一个同样监听 80 端口的 Nginx 容器时,会因为端口被占用而启动失败:
docker run -d --name nginx-host2 --network host my-nginx docker ps -a docker logs nginx-host2
清理环境:
docker rm -f nginx-host nginx-host2
None 网络完全禁用了容器的网络功能,容器只有 lo(loopback)接口,没有任何外部网络连接。
特点:
启动一个使用 none 网络的容器,验证其网络状态:
docker run --rm --network none alpine ifconfig
可以看到输出中只有
lo接口,没有eth0,说明容器没有外部网络连接。
尝试在容器内访问外部网络,例如上面默认 bridge 网络中 container1 可以通过 IP 访问 container2,但在 none 网络下,连最基本的网络请求都无法发出:
docker run --rm --network none my-nginx curl http://www.cnb.com
命令会报错,说明容器完全没有外部网络能力。
Overlay 网络是 Docker 用于实现跨主机容器通信的网络驱动,主要用于 Docker Swarm 集群环境。它通过在不同主机的物理网络之上创建虚拟网络,使用 VXLAN 技术在主机间建立隧道,从而实现容器间的透明通信。在 Overlay 网络中,每个容器都会获得一个虚拟 IP,容器之间可以直接通过这个 IP 进行通信,而不需要关心容器具体运行在哪个主机上。
特点:
Overlay 网络需要 Docker Swarm 集群环境才能使用,单机环境下无法演示。以下仅展示基本命令供参考。
初始化 Swarm 集群并创建 Overlay 网络:
# 初始化 Swarm(在管理节点上执行)
docker swarm init
# 创建 Overlay 网络
docker network create --driver overlay my-overlay-network
# 查看网络详情
docker network inspect my-overlay-network