曾经看过一本名叫《深入浅出neutron》的书,简直是,烂的不能再烂。犹记得里面有提到解决 ipsec 的 nat 穿越,然后一脸认真进去,一脸懵逼出来。这本书让我对「深入浅出」这个词有了新的认识——我只管一顿“高大上”的乱抄,抛出各种概念装X,你浅尝辄止就好。

那我这里为什么要用“深入浅出”一词呢,就是想说明不要对我这篇博文抱太大期望,能把容器网络深入剖析后再逻辑分明的简简单单讲出来。尴尬,为了弥补这部分内容,强烈建议阅读 CloudMan 的《每天5分钟玩转 Docker 容器技术》中关于容器网络的部分,一文搞懂各种 Docker 网络是其该部分的总结篇,里面有各种容器网络解决方案讲解的超链接,简单明了。So,有了这样的深度好文,我下面的谈论当然是多多“借鉴“了。

顺便一提,如果想学习 neutron,可以阅读李宗标的《深入理解 OpenStack Neutron》,真可谓是「抽丝剥茧」,难得的好书。

容器间的通信

我们知道容器本质上就是使用 cgroup 进行资源控制,再使用 namespace 进行访问控制,那么使用这些虚拟化技术之后,该怎么使容器进行通信呢?最基本的就是 docker 中的 none 和 host 模式了,然后是应用更广的 bridge 网络。

  • none

    故名思议,none 网络就是什么都没有的网络。挂在这个网络下的容器除了 lo,没有其他任何网卡。

  • host

    连接到 host 网络的容器共享 Docker host 的网络栈,容器的网络配置与 host 完全一样。

  • bridge

    说白了,就是使用虚拟化实现虚拟网桥(Linux Bridge 或 OVS 实现方式都行),然后将容器“插”到虚拟网桥上,形成了一个广播域,然后就能通信了。

以上就是 docker 自动创建的网络,除此之外用户也可以根据业务需要创建 user-defined 网络。大部分的 user-defined 网络都是解决跨主机通信的,我们下一小节再讨论,这里就只先说明自定义的 bridge 网络,因为它就是让用户自行创建虚拟网桥,自己规划 IP,然后将容器“插”上去,跟 docker 自动创建的 bridge 原理上没什么区别(自动创建的 bridge默认桥接了主机的网卡)。

那不同的 bridge 间可以通信么?这里因为跨了广播域,需要进行三层路由,而 docker 没有提供这样的功能,所以不能进行通信——当然不包括已经进行了跟外层网络 nat 的情况,既然谈到这里,就顺便把容器同外部网络进行通信的原理也简单谈一下。

首先 none 和 host,没什么好说的了,none 肯定不能,host 宿主机能就能。然后默认的 bridge 网络默认桥接了宿主机的网卡,并将其作为 nat 网关,主动访问外面的时候使用端口复用,外面主动访问里面就需要配置端口映射了。自行定义的 bridge 网络原理一样,但要先自行将其桥接到宿主机能访问外网的网卡上并配置 nat。

另外一提,CloudMan 在其文中提到了通信的三种方式(非定义的网络模式)。一种是直接使用 IP;第二种是使用 docker dns server,即通过容器名进行通信,但貌似没起 dns 服务,就是将 hostname 和对应 IP 推进了 容器的 host文件;最后一种是叫 joined,这个与其说是通信方式,不如也说是一种网络模式,因为它同 host 原理一致,只不过是容器间共享一个网络栈,即通过同一个 IP 的不同端口进行服务访问。

容器网络

细心的朋友应该已经发现了,docker 所定义的网络都是处于各自的二层网络中,同外部通信也是通过 nat ,那么如果进行跨主机部署,怎么保证广播域呢?下面将介绍几种解决方案。

主要分为 docker 原生的 overlay、macvlan,和第三方方案,常用的包括 flannel、weave 和 calico。(docker 网络是一个非常活跃的技术领域,不断有新的方案开发出来,那么要问个非常重要的问题了:如此众多的方案是如何与 docker 集成在一起的?答案是:libnetwork 以及 CNM,这里就不在展开介绍了。)

overlay

为支持容器跨主机通信,Docker 提供了 overlay driver,使用户可以创建基于 VxLAN 的 overlay 网络。VxLAN 可将二层数据封装到 UDP 进行传输,VxLAN 提供与 VLAN 相同的以太网二层服务,但是拥有更强的扩展性和灵活性。有关 VxLan 的详细内容就不做过多介绍了。

Docerk overlay 网络需要一个 key-value 数据库用于保存网络状态信息,包括 Network、Endpoint、IP 等。Consul、Etcd 和 ZooKeeper 都是 Docker 支持的 key-vlaue 软件。

下面主要来看看overlay 网络跨主机通信的原理。docker 会为每个 overlay 网络创建一个独立的 network namespace,其中会有一个 linux bridge br0,endpoint 还是由 veth pair 实现,一端连接到容器中,另一端连接到 namespace 的 br0 上。br0 除了连接所有的 endpoint,还会连接一个 vxlan 设备(即宿主机作为VETP 的网卡),用于与其他 host 建立 vxlan tunnel。容器之间的数据就是通过这个 tunnel 通信的。

需要说明的是,很多文章在谈到隔离性时拿那个独立的 namespace 大谈特谈,然而隔离性明明靠的是广播域的隔绝,也就是说不用那个 namespace 也完全 ok,它完全是为了提高网络架构的可读性,点个赞。

还需要说明的是,这里 docker 不像 OpenStack 提够了 router 的概念,那怎么和外网进行通信呢?其实创建容器使用 overlay 时会为容器创建两个网络接口。一个用来连接 overlay 网络,另一个连接到了一个叫 “docker_gwbridge” 的网桥上,其为所有连接到 overlay 网络的容器提供访问外网的能力(默认路由指向这边)。我要吐槽的是,这是一个“假的”网关,docker将这个网络的属性标的是“local”,即每台宿主机上都有这样一个 local 网桥,且具有同样的网关 IP,nat 之后,宿主机再次进行 nat。

macvlan

由于要管理宿主机,或者宿主机根本不在一个广播域,从而隔绝了容器的广播域,那就再给每个宿主机插一块网卡,用这块网卡将所有宿主机连到一个广播域,docker 的不同网络再进行 802.1q 的封装(即 vlan)——这就是 macvlan 的核心思想了,细节还是建议阅读 CloudMan 的教程。

flannel

flannel 是 CoreOS 开发的容器网络解决方案。flannel 为每个 host 分配一个 subnet,容器从此 subnet 中分配 IP,这些 IP 可以在 host 间路由,容器间无需 NAT 和 port mapping 就可以跨主机通信。

每个 subnet 都是从一个更大的 IP 池中划分的,flannel 会在每个主机上运行一个叫 flanneld 的 agent,其职责就是从池子中分配 subnet。为了在各个主机间共享信息,flannel 用 etcd(与 consul 类似的 key-value 分布式数据库)存放网络配置、已分配的 subnet、host 的 IP 等信息。

数据包如何在主机间转发是由 backend 实现的。flannel 提供了多种 backend,最常用的有 vxlan 和 host-gw,我们将在本章讨论这两种 backend。其他 backend 请参考 https://github.com/coreos/flannel。

  • vxlan

    与 Docker overlay 类似,属于 overlay 网络。flannel 没有创建新的 docker 网络,而是直接使用默认的 bridge 网络,即同一主机的容器通过 docker0 连接,但跨主机流量是通过一个叫 flannel.1 的网卡转发(由 flannel 添加的静态路由进行控制),这是flannel 在宿主机上创建的虚拟网卡,其将数据包封装成 VxLAN,通过预先配置好的宿主机的网卡发送给其它主机。

    详情另参。

  • host-gw

    host-gw 不会封装数据包,而是在主机的路由表中创建到其他主机 subnet 的路由条目,从而实现容器跨主机通信。

是不是看完稍微有点晕,两种 backend 都添加了路由,一个封装 vxlan 能行,另外一个不封装也行,WTF?!其实在我看来,host-gw 就是个废柴,因为他从原理上要求宿主机不能跨路由,如果跨了路由,中间的路由设备不就方了,它拿不到 docker 网络中的信息啊(支持 SDN 另说啊,不是一个次元),那自然没啥用了,这种情况还不如直接使用 vlan,避免三层拆包,提高转发性能。而 vxlan backend 进行了 vxlan 的封装,所以无碍。

weave

weave 是 Weaveworks 开发的容器网络解决方案,其属于 overlay 网络,原理上跟其它 vxlan 网络没什么区别(但同时使用了 Linux bridge 和 OVS,前者连接容器,后者封装 vxlan),但有其它几点比较脑洞打开的设计。

一是其不依赖分布式数据库(例如 etcd 和 consul)交换网络信息,每个主机上只需运行 weave 组件就能建立起跨主机容器网络,原因是把 VETP 的信息维护在了本地。

二是对容器来说,weave 就像一个巨大的以太网交换机,所有容器都被接入这个交换机,容器可以直接通信,即只维护了一个 vxlan 网络。那么不同的网络隔离又是怎么实现的呢?答案是给要隔离的网络分配不同的网络前缀,这个不同的网络前缀是指要使目标隔绝的网络的地址与本地掩码与运算后判定为不同网段的意思,即使用 vlsm 或者直接划分一个新的前缀都可以,然后查路由表的时候自然没有匹配的明细路由(生成的直连路由不是一个网段了),就走默认丢给 eth0 了,包自然丢了。但是,这产生了一个非常严重的问题,新隔离出来的网络怎么进行跨主机通信?weave 表示无能为力,所以只能说它只实现了一半,你既然要隔离那就隔离彻底点吧,任性!

三是与外网通信是没有 nat 的。它要求外部若想要与它通信,那就自己写明细路由,自行收敛网络,任性!

calico

在了解到 calico 之前,我一直觉得 k8s 和 openstack 在云计算领域不是一个量级的,尤其是在网络方面。openstack 的强大毋庸置疑,但容器用着没办法,就一个字,爽!是的,容器就目前需求来说,完胜openstack,而且比如在计算方面,少了一层 hypersior 的容器要更胜一筹,更符合像机器学习、图像识别等应用场景的需求,就连一直诟病的安全方面,像 kata 等项目也一直在完善,但除过裸机管理等 IaaS 方面的需求,容器唯一的弱点就是网络,也就单是这一项就让我不认为 k8s 能彻底击败 openstack。然而,调研了 calico 之后,我才发现 k8s 的野心有多大。(虽然 openstack 也能对接 calico,但其与 calico 的网络模型概念差距过大。)

我曾在之前的博文里有详细的介绍过 calico,传送门:Calico Research

杂谈

这部分呢,主要谈谈相关网络的发展,加入了很多我个人的观点,有些甚至与行业的主流意见相左,还请以辩证的态度看待。

首先吐槽俩。一,openflow已死,不接受反驳;二,DVR 这个东西从纯软件设计考虑是很不错,但我觉得其背离了网络思想,非常的不可取。

接着,强烈推荐两篇深度好文:

最后,简单谈谈目前比较关注的几个发展点。