基于 Docker18.09.0
在正式讨论主题之前,我们还需借助下面的图来进一步说明 Docker
的夸主机通信稍微底层的一些网络知识。具体看下图:
上图中 container1
和 container2
两个容器需要在一个局域网中。
这样的话每个宿主机都必须有个含有相同子网的网络,比如 上图中的 docker0
接口是每个宿主机上都有的默认网络 bridge
的接口,他们都有着相同的子网 172.17.0.0/16
。
先不说如何将两台宿主机上默认网络合并为一个局域网,稍后会介绍是如何实现的。
现在假设是可以的,这样的话,就会带来一个问题,两个不同宿主机上的网络如何共享各自分配给自己宿主机上容器的 IP 信息(IP地址,子网掩码,网关地址等),从而保障在一个局域网中不会出现两个相同的 IP ,也就是不冲突。 这就需要借助一个外部的键值存储,来保存这些网络信息。这样的话,两台宿主机的 其中一个 Docker 进程就知道对方已经分配出去的 IP ,就不会再把这些 IP 分配给自己的容器了,从而防止产生一个局域网中同时出现两个相同 IP 的冲突现象。
注意:这里讨论的是不使用
Docker swarm
模式情况下的跨主机独立集群。
独立群需要依赖外部键值存储来存储网络信息。
而Docker swarm
模式是将群组信息存储在群集管理器的Raft
日志中。如果您使用swarm模式,请参阅官方swarm模式网络文档。
要想将Docker与外部键值存储一起使用,需要以下条件:
- Docker 守护进程需要在键值存储群集中的每个主机上运行。
- Docker 主机能够连接和访问键值存储群集。
- 群集中的 Docker主机必须具有唯一的主机名,因为键值存储使用主机名来标识群集成员。
Docker 目前支持 Consul
,Etcd
和 ZooKeeper
(分布式存储)键值存储。
除了需要了解以上的知识,我们还有一个知识点需要讨论一下,就是数据包是如何从一个宿主机传送到另一台宿主机的。这就是下面我们需要讨论的 VXLAN
网络。
了解一下 VXLAN 网络
什么是 VXLAN
VXLAN-Virtual eXtensible Local Area Network(虚拟化可扩展局域网)
VXLAN是NVO3(Network Virtualization over Layer3)中的一种网络虚拟化技术,通过将VM或物理服务器发出的数据包封装在UDP中,并使用物理网络的IP/MAC作为报文头进行封装,然后在IP网络上传输,到达目的地后由隧道终结点解封装并将数据发送给目标虚拟机或物理服务器。
NVE-Network Virtual Endpoint
网络虚拟边缘节点NVE,是实现网络虚拟化功能的网络实体。报文经过NVE封装转换后,NVE间就可基于三层基础网络建立二层虚拟化网络。
VTEP-VXLAN Tunnel Endpoints
VTEP是VXLAN隧道端点,封装在NVE中,用于VXLAN报文的封装和解封装。
VNI-VXLAN Network Identifier
VXLAN网络标识VNI类似VLAN ID,用于区分VXLAN段,不同VXLAN段的虚拟机不能直接二层相互通信。
一个VNI表示一个租户,即使多个终端用户属于同一个VNI,也表示一个租户。VNI由24比特组成,支持多达16M((224-1)/10242)的租户。
更多关于 VXLAN
网络的知识,请参考华为文档VXLAN
介绍或者自行 Google
关于数据报文的传输,我们用下图再来简单说明一下
先看下面这个网络拓扑图
假如从虚拟机 VM1 SSH 到另外一台虚拟机 VM2,这个数据包的传输过程如下:
- 数据包从第 ① 个位置开始进行封装,传输给宿主机 VTEP-1。
- 之后经过第 ② 步被传输到网关,再次被封装。
- 到达对方的网关时,进行解封装,通过 ③ 转发给宿主机 VTEP-2。
- 再经过 ④ 数据转发个 VM2
具体的封装看下图,在 VM1 时,数据链路层被封装源 MAC 是本机的,目标
MAC 是 VM2 的;源 IP 是自己的 IP ,目标 IP 是 VM2的 IP。
接下来,看下图,VXLAN 网络会把刚才整个数据包封装在宿主机的 UDP 的 数据部分中作为数据,之后再封装 UDP 头部信息,接着就是传递给宿主机的下一层 IP 层, 封装目标 IP 是对方宿主机的 IP;最后是宿主机的数据链路层封装为目标 MAC 为 对方宿主机的 MAC(或者是网关的 MAC);
到达对方宿主机后,就可以依次解封装了。
要想实现 Docker 容器的夸主机通信,还需要创建一个
overlay
网络,使用这个网络模式和外部的键值对存储相结合。接下来就来讨论一下 Docker 的overlay network
Docker 对 VXLAN
的实现: overlay network
以下大部分内容参考了 overlay network
驱动程序核心架构
Docker Overlay驱动程序自Docker Engine 1.9以来就已存在,并且需要外部 键值存储来管理网络状态。Docker 1.12将控制平面状态集成到Docker Engine中,因此不再需要外部存储。1.12还引入了一些新功能,包括加密和服务负载平衡。引入的网络功能需要支持它们的Docker Engine版本,并且不支持将这些功能与旧版本的Docker Engine一起使用。
内置的Docker overlay
网络驱动程序从根本上简化了多主机网络中的许多挑战。通过overlay
驱动程序,多主机网络是Docker中的第一类公民,无需外部配置或组件。overlay
使用Swarm分布式控制平台在非常大规模的集群中提供集中管理、稳定性以及安全性。
VXLAN数据平台
该 overlay
驱动程序使用行业标准的VXLAN数据平台,将容器网络与底层物理网络(底层)分离。Docker overlay network
将容器流量封装在 VXLAN header
中,允许流量穿过物理第2层或第3层网络。无论底层物理拓扑如何,overlay
使网络动态分段且易于控制。使用标准的 IETF VXLAN header
可促进标准工具检查和分析网络流量。
自Linux内核 3.7 版本以来,VXLAN一直是其中的一部分,Docker使用本机内核的 VXLAN 功能来创建覆盖网络。Docker
overlay
数据路径完全在内核空间中。这样可以减少上下文切换,减少CPU开销,并在应用程序和物理NIC之间实现低延迟,直接的流量路径。
IETF VXLAN(RFC 7348)是一种数据层封装格式,它通过第3层网络覆盖第2层网段。VXLAN旨在用于标准IP网络,可支持共享物理网络基础架构上的大规模多租户设计。现有的内部部署和基于云的网络可以透明地支持VXLAN。
VXLAN定义为 MAC-in-UDP
封装,将容器第2层帧放置在底层 IP/UDP
报头中(UDP 协议的数据单元称为: 数据报(datagrams))。底层 IP/UDP
报头提供底层(物理)网络上主机之间的传输。在一个给定的 overlay network
中,每个主机之间的点对多点连接来说, overlay
是作为无状态VXLAN隧道而存在的。由于overlay
独立于底层拓扑,因此应用程序变得更加便携。因此,无论应用程序是在内部部署,开发人员桌面还是公共云上,都可以传输网络策略和连接。
在此图中,我们可以看到 overlay network
上数据包的流向。
以下是在共享 overlay network
上 c1
向 c2
发送数据包时发生的步骤:
c1
进行DNS查找c2
。由于两个容器位于同一overlay network
上,因此Docker Engine本地DNS服务器解析c2
为其overlay
IP地址为:10.0.0.3
。overlay network
是L2段,因此c1
生成以c2
的MAC地址为目的地的L2帧。该帧由
overlay network
驱动程序封装为VXLAN标头。分布式覆盖控制平面会管理每个VXLAN隧道端点的位置和状态,因此,它知道c2
在host-B
在上的物理地址:192.168.1.3
。该地址成为底层IP头的目标地址。一旦封装,就会发送数据包。物理网络负责将VXLAN数据包路由或桥接到正确的主机。
数据包到达
host-B
的eth0
接口,并由overlay network
驱动程序解封装。 来自c1
的原始L2帧被传递到c2
的eth0
接口,直到应用程序侦听到。
overlay
驱动程序内部架构
Docker Swarm控制平面可自动完成覆盖网络的所有配置。 不需要VXLAN配置或Linux网络配置。
数据平面加密是 overlay
的可选功能,也可以在创建网络时由overlay
驱动程序自动配置。 用户只需定义网络(docker network create -d overlay ...
)并将容器连接到该网络。
在 overlay
创建期间,Docker Engine会在每台主机上创建 overlay
所需的网络基础架构。 为每个 overlay
创建一个Linux 网桥及其关联的VXLAN接口。 只有在主机上安排连接到该网络的容器时,Docker Engine才会智能地在主机上实例化 overlay network
。 这可以防止没有连接容器的 overlay
蔓延。
在以下示例中,我们创建了一个 overlay network
(覆盖网络)并将容器附加到该网络。 然后我们将看到Docker Swarm/UCP自动创建覆盖网络。
#使用 overlay 驱动程序创建名为 "ovnet" 的叠加层
$ docker network create -d overlay ovnet
#从 nginx 镜像创建服务并将其连接到 "ovnet" overlay network
$ docker service create --network ovnet --name container1 nginx
创建覆盖网络时,您会注意到在主机内部创建了多个接口和网桥。
# Run the "ifconfig" command inside the nginx container
$ docker exec -it container1 ifconfig
#docker_gwbridge network
eth1 Link encap:Ethernet HWaddr 02:42:AC:12:00:04
inet addr:172.18.0.4 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80::42:acff:fe12:4/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:8 errors:0 dropped:0 overruns:0 frame:0
TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:648 (648.0 B) TX bytes:648 (648.0 B)
# overlay network
eth2 Link encap:Ethernet HWaddr 02:42:0A:00:00:07
inet addr:10.0.0.7 Bcast:0.0.0.0 Mask:255.255.255.0
inet6 addr: fe80::42:aff:fe00:7/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1450 Metric:1
RX packets:8 errors:0 dropped:0 overruns:0 frame:0
TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:648 (648.0 B) TX bytes:648 (648.0 B)
# container loopback
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:48 errors:0 dropped:0 overruns:0 frame:0
TX packets:48 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:4032 (3.9 KiB) TX bytes:4032 (3.9 KiB)
在容器内部创建了两个接口,这两个接口对应于主机上现在存在的两个网桥。在覆盖网络上,每个容器至少有两个接口将它连接到 overlay
和docker_gwbridge
。
网桥 | 目的 |
---|---|
overlay | 入口和出口指向VXLAN封装的覆盖网络,并(可选)加密同一覆盖网络上的容器之间的流量。它将覆盖范围扩展到参与此特定叠加层的所有主机。一个主机上的每个覆盖子网将存在一个,并且它将具有与给定特定覆盖网络相同的名称。 |
docker_gwbridge | 离开集群的流量的出口桥。docker_gwbridge 每个主机只能存在一个。此网桥上的容器到容器之间流量被阻止,仅允许入口/出口流量。 |
数据最初从容器的
eth1
到ovnet
, 在此,进行 VXLAN 的封装,经过封装后的数据帧最终由docker_gwbridge
网络路由
到宿主机的网络上,再有宿主机的网络继续正确的数据路由转发。