如 docker network ls 命令结果所示,Docker 网络驱动程序具有 范围 的概念。网络范围是驱动程序的作用域,可以是本地范围或 Swarm 集群范围。本地范围驱动程序在主机范围内提供连接和网络服务(如 DNS 或 IPAM)。Swarm 范围驱动程序提供跨群集的连接和网络服务。集群范围网络在整个群集中具有相同的网络 ID,而本地范围网络在每个主机上具有唯一的网络 ID。
$ docker network ls NETWORK ID NAME DRIVER SCOPE 1475f03fbecb bridge bridge local e2d8a4bd86cb docker_gwbridge bridge local 407c477060e7 host host local f4zr3zrswlyg ingress overlay swarm c97909a4b198 none null local
Docker 远程网络驱动
以下社区和供应商创建的远程网络驱动程序与 CNM 兼容,每个都为容器提供独特的功能和网络服务。
驱动
描述
**contiv**[4]
由 Cisco Systems 领导的开源网络插件,为多租户微服务部署提供基础架构和安全策略。Contiv 还为非容器工作负载和物理网络(如 ACI)提供兼容集成。Contiv 实现了远程网络和 IPAM 驱动。
Linux 内核具有非常成熟和高性能的 TCP/IP 网络栈实现(不仅是 DNS 和 VXLAN 等其他原生内核功能)。Docker 网络使用内核的网络栈作为低级原语来创建更高级别的网络驱动程序。简而言之,Docker 网络就是Linux 网络。
现有 Linux 内核功能的这种实现确保了高性能和健壮性。最重要的是,它提供了跨许多发行版和版本的可移植性,从而增强了应用程序的可移植性。
Docker 使用了几个 Linux 网络基础模块来实现其原生 CNM 网络驱动程序,包括 Linux 网桥,网络命名空间,veth 和 iptables。这些工具的组合(作为网络驱动程序实现)为复杂的网络策略提供转发规则,网络分段和管理工具。
Linux 网桥
Linux 网桥是第 2 层设备,它是 Linux 内核中物理交换机的虚拟实现。它通过检视流量动态学习 MAC 地址,并据此转发流量。Linux 网桥广泛用于许多 Docker 网络驱动程序中。Linux 网桥不应与 Docker 网络驱动程序 bridge 混淆,后者是 Linux 网桥的更高级别实现。
网络命名空间
Linux 网络命名空间是内核中隔离的网络栈,具有自己的接口,路由和防火墙规则。它负责容器和 Linux 的安全方面,用于隔离容器。在网络术语中,它们类似于 VRF,它将主机内的网络控制和数据隔离。网络命名空间确保同一主机上的两个容器无法相互通信,甚至无法与主机本身通信,除非通过 Docker 网络进行配置。通常,CNM 网络驱动程序为每个容器实现单独的命名空间。但是,容器可以共享相同的网络命名空间,甚至可以是主机网络命名空间的一部分。主机网络命名空间容纳主机接口和主机路由表。此网络命名空间称为全局网络命名空间。
通常在使用其他网络驱动程序时,每个容器都被放在其自己的 网络命名空间 (或沙箱)中,以实现彼此间完全的网络隔离。使用 host 驱动程序的容器都在同一主机网络命名空间中,并使用主机的网络接口和 IP 堆栈。主机网络中的所有容器都能够在主机接口上相互通信。从网络角度来看,它们相当于在没有使用容器技术的主机上运行的多个进程。因为它们使用相同的主机接口,所以任意两个容器都不能够绑定到同一个 TCP 端口。如果在同一主机上安排多个容器,可能会导致端口争用。
#Create containers on the host network $ docker run -itd --net host --name C1 alpine sh $ docker run -itd --net host --name nginx
#Show host eth0 $ ip add | grep eth0 2: eth0: mtu 9001 qdisc mq state UP group default qlen 1000 inet 172.31.21.213/20 brd 172.31.31.255 scope global eth0
#Show eth0 from C1 $ docker run -it --net host --name C1 alpine ip add | grep eth0 2: eth0: mtu 9001 qdisc mq state UP qlen 1000 inet 172.31.21.213/20 brd 172.31.31.255 scope global eth0
#Contact the nginx container through localhost on C1 $ curl localhost !DOCTYPE html>
#Create a busybox container named "c1" and show its IP addresses host $ docker run -it --name c1 busybox sh c1 # ip address 4: eth0@if5: mtu 1500 qdisc noqueue link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff inet 172.17.0.2/16 scope global eth0 ...
容器接口的 MAC 地址是动态生成的,并嵌入 IP 地址以避免冲突。这里 ac:11:00:02 对应于 172.17.0.2。
如主机路由表中所示,全局网络命名空间中的 IP 接口现在包括 docker0。主机路由表提供了外部网络上 docker0 和 eth0 之间的连接,完成了从容器内部到外部网络的路径。
host $ ip route default via 172.31.16.1 dev eth0 172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.42.1 172.31.16.0/20 dev eth0 proto kernel scope link src 172.31.16.102
默认 bridge 网络是唯一支持遗留链路[14]的网络。默认 bridge 网络不支持基于名称的服务发现和用户提供的 IP 地址。
用户自定义 bridge 网络
除了默认网络,用户还可以创建自己的网络,称为用户自定义网络,可以是任何网络驱动类型。用户定义的 bridge 网络,相当于在主机上设置新的 Linux 网桥。与默认 bridge 网络不同,用户定义的网络支持手动 IP 地址和子网分配。如果未给出赋值,则 Docker 的默认 IPAM 驱动程序将分配私有 IP 空间中可用的下一个子网。
$ docker network create -d bridge --subnet 10.0.0.0/24 my_bridge $ docker run -itd --name c2 --net my_bridge busybox sh $ docker run -itd --name c3 --net my_bridge --ip 10.0.0.254 busybox sh
brctl 现在显示主机上的第二个 Linux 网桥。这一 Linux 网桥的名称 br-4bcc22f5e5b9 与 my_bridge 网络的网络 ID 匹配。my_bridge 还有两个连接到容器 c2 和 c3 的 veth 接口。
$ brctl show bridge name bridge id STP enabled interfaces br-b5db4578d8c9 8000.02428d936bb1 no vethc9b3282 vethf3ba8b5 docker0 8000.0242504b5200 no vethb64e8b8
$ docker network ls NETWORK ID NAME DRIVER SCOPE b5db4578d8c9 my_bridge bridge local e1cac9da3116 bridge bridge local ...
列出全局网络命名空间接口,可以看到已由 Docker Engine 实例化的 Linux 网络。每个 veth 和 Linux 网桥接口都显示为其中一个 Linux 网桥和容器网络命名空间之间的链接。
$ ip link
1: lo: mtu 65536 2: eth0: mtu 9001 3: docker0: mtu 1500 5: vethb64e8b8@if4: mtu 1500 6: br-b5db4578d8c9: mtu 1500 8: vethc9b3282@if7: mtu 1500 10: vethf3ba8b5@if9: mtu 1500 ...
#Creation of MACVLAN network "mvnet" bound to eth0 on the host $ docker network create -d macvlan --subnet 192.168.0.0/24 --gateway 192.168.0.1 -o parent=eth0 mvnet
#Creation of containers on the "mvnet" network $ docker run -itd --name c1 --net mvnet --ip 192.168.0.3 busybox sh $ docker run -it --name c2 --net mvnet --ip 192.168.0.4 busybox sh / # ping 192.168.0.3 PING 127.0.0.1 (127.0.0.1): 56 data bytes 64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.052 ms
#Creation of macvlan10 network in VLAN 10 $ docker network create -d macvlan --subnet 192.168.10.0/24 --gateway 192.168.10.1 -o parent=eth0.10 macvlan10
#Creation of macvlan20 network in VLAN 20 $ docker network create -d macvlan --subnet 192.168.20.0/24 --gateway 192.168.20.1 -o parent=eth0.20 macvlan20
#Creation of containers on separate MACVLAN networks $ docker run -itd --name c1--net macvlan10 --ip 192.168.10.2 busybox sh $ docker run -it --name c2--net macvlan20 --ip 192.168.20.2 busybox sh
创建 Docker 服务时,会自动实例化内部负载均衡。在 Docker Swarm 集群中创建服务时,会自动为它们分配一个虚拟 IP(VIP),该虚拟 IP 是服务网络的一部分。解析服务名称时返回 VIP。到该 VIP 的流量将自动发送到 overlay 网络上该服务的所有健康任务。这种方法避免了任何客户端负载均衡,因为只有一个 IP 返回给客户端。Docker 负责路由并在健康的服务任务中平均分配流量。
要查看 VIP,请运行 docker service inspect my_service,如下所示:
# Create an overlay network called mynet $ docker network create -d overlay mynet a59umzkdj2r0ua7x8jxd84dhr
# Create myservice with 2 replicas as part of that network $ docker service create --network mynet --name myservice --replicas 2 busybox ping localhost 8t5r8cr0f0h6k2c3k7ih4l6f5
# See the VIP that was created for that service $ docker service inspect myservice ...
启动服务后,您可以为应用程序创建外部 DNS 记录,并将其映射到任何或所有 Docker Swarm 节点。您无需担心容器的运行位置,因为群集中的所有节点都与路由网状路由功能一样。
#Create a service with two replicas and export port 8000 on the cluster $ docker service create --name app --replicas 2 --network appnet -p 8000:80 nginx
可以使用 UCP、CLI 或 Docker API 手动配置容器 IP 地址和网络子网。地址请求通过所选的驱动程序,然后决定如何处理请求。
子网大小和设计在很大程度上取决于给定的应用程序和特定的网络驱动程序。下一节将详细介绍每种 网络部署模型[20] 的 IP 地址空间设计。端口映射,overlay 和 MACVLAN 的使用都会影响 IP 寻址的安排。通常,容器寻址分为两个部分。内部容器网络(bridge 和 overlay)使用默认情况下在物理网络上无法路由的 IP 地址的容器进行寻址。MACVLAN 网络为物理网络子网上的容器提供 IP 地址。因此,来自容器接口的流量可以在物理网络上路由。值得注意的是,内部网络(bridge,overlay)的子网不应与物理底层网络的 IP 空间冲突。重叠的地址空间可能导致流量无法到达目的地。
$ docker run -it --env "DB=db" --net petsBridge --name Web -p 8000:5000 chrch/docker-pets:1.0 Starting Web container e750c649a6b5 * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
如果未指定 IP 地址,则会在主机的所有接口上公开端口映射。在这种情况下,容器的应用程序在 0.0.0.0:8000 上发布。如要使用特定 IP 地址,需要提供额外的标志 -p IP:host_port:container_port。可以在 Docker 文档[23] 中找到更多暴露端口的选项。
应用程序本地发布在主机所有接口上的 8000 端口。还设置了 DB=db,提供后端容器的名称。Docker Engine 的内置 DNS 将此容器名称解析为 db 的 IP 地址。由于 bridge 是本地驱动程序,因此 DNS 解析的范围仅限于单个主机。
在以下示例中,手动配置每个服务的位置,模拟外部服务发现。db 服务的位置通过 DB 环境变量传递给 web。
#Create the backend db service and expose it on port 8500 host-A $ docker run -d -p 8500:8500 --name db consul
#Display the host IP of host-A host-A $ ip add show eth0 | grep inet inet 172.31.21.237/20 brd 172.31.31.255 scope global eth0 inet6 fe80::4db:c8ff:fea0:b129/64 scope link
#Create the frontend Web service and expose it on port 8000 of host-B host-B $ docker run -d -p 8000:5000 -e 'DB=172.31.21.237:8500' --name Web chrch/docker-pets:1.0
web 服务现在应该在 host-B IP 地址的 8000 端口上提供其网页。
在此示例中,我们没有指定要使用的网络,因此会自动选择默认的 Docker bridge 网络。
当我们在 172.31.21.237:8500 配置 db 的位置时,我们正在创建某种形式的服务发现。我们静态配置 web 服务的 db 服务的位置。在单主机示例中,这是自动完成的,因为 Docker Engine 为容器名称提供了内置的 DNS 解析。在这个多主机示例中,我们需要手动执行服务发现。
#Display the nodes participating in this swarm cluster that was already created $ docker node ls ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS a8dwuh6gy5898z3yeuvxaetjo host-B Ready Active elgt0bfuikjrntv3c33hr0752 * host-A Ready Active Leader
#Create the backend service and place it on the dognet network host-A $ docker service create --network petsOverlay --name db consul
#Create the frontend service and expose it on port 8000 externally host-A $ docker service create --network petsOverlay -p 8000:5000 -e 'DB=db' --name Web chrch/docker-pets:1.0
host-A $ docker service ls ID NAME MODE REPLICAS IMAGE lxnjfo2dnjxq db replicated 1/1 consul:latest t222cnez6n7h Web replicated 0/1 chrch/docker-pets:1.0
与在单主机 bridge 驱动示例中一样,我们将 DB=db 作为环境变量传递给 web 服务。overlay 驱动程序将服务名称 db 解析为容器的 overlay IP 地址。web 和 db 之间的通信仅使用 overlay IP 子网进行。
web 服务暴露在 8000 端口上,路由网络在 Swarm 集群中的每个主机上公开端口 8000。通过在浏览器中转到:8000 或:8000 来测试应用程序是否正常工作。
Overlay 的好处及使用场景
无论规模大小,多主机直接的连通性都很容易实现。
不需要额外配置或组件,就能实现服务发现和负载均衡。
有助于通过加密叠加层实现东西方微分段。
路由网格可用于在整个群集中发布服务。
练习应用:MACVLAN 网桥模式
在某些情况下,应用程序或网络环境要求容器具有可作为底层子网一部分的可路由 IP 地址。MACVLAN 驱动程序实现了此功能。如 MACVLAN 体系结构[28] 部分所述,MACVLAN 网络将自身绑定到主机接口。这可以是物理接口,逻辑子接口或绑定的逻辑接口。它充当虚拟交换机,并在同一 MACVLAN 网络上的容器之间提供通信。每个容器接收唯一的 MAC 地址和该节点所连接的物理网络的 IP 地址。
本例中,Pets 应用部署在 host-A 和 host-B 上。
#Creation of local macvlan network on both hosts host-A $ docker network create -d macvlan --subnet 192.168.0.0/24 --gateway 192.168.0.1 -o parent=eth0 petsMacvlan host-B $ docker network create -d macvlan --subnet 192.168.0.0/24 --gateway 192.168.0.1 -o parent=eth0 petsMacvlan
#Creation of db container on host-B host-B $ docker run -d --net petsMacvlan --ip 192.168.0.5 --name db consul
#Creation of Web container on host-A host-A $ docker run -it --net petsMacvlan --ip 192.168.0.4 -e 'DB=192.168.0.5:8500' --name Web chrch/docker-pets:1.0
这可能看起来与多主机桥示例非常相似,但有几个显着的差异:
从 web 到 db 的引用使用 db 本身的 IP 地址而不是主机 IP。请记住,使用 macvlan 驱动时,容器 IP 可以在底层网络上路由。
我们不暴露 db 或 web 的任何端口,因为容器中打开的任何端口都可以使用容器 IP 地址直接访问。
虽然 macvlan 驱动程序提供了这些独特的优势,但它牺牲的是可移植性。MACVLAN 配置和部署与底层网络密切相关。除了防止重叠地址分配之外,容器寻址必须遵守容器放置的物理位置。因此,必须注意在 MACVLAN 网络外部维护 IPAM。重复的 IP 地址或不正确的子网可能导致容器连接丢失。
MACVLAN 的优势与使用场景
macvlan 驱动由于没有用到 NAT,适用于超低延迟应用。
MACVLAN 可为每个容器提供 IP 地址,这可能是在某些场景中的特殊要求。
必须更加细致地考虑 IPAM。
结论
Docker 是一种快速发展的技术,网络选项日益增多,每天都在满足越来越多的用例需求。现有的网络供应商、纯粹的 SDN 供应商和 Docker 本身都是这一领域的贡献者。与物理网络的紧密集成、网络监控和加密都是备受关注和创新的领域。