提出容器网络标准的目的,就是为了把网络功能从容器运行时引擎、或者容器编排系统中剥离出去,毕竟网络的专业性和针对性极强,更适合做成外部可扩展的功能。
从程序功能上看, CNI 的网络插件提供的能力,都可以划分为网络的管理与 IP 地址的管理两类,而插件可以选择只实现其中的某一个,也可以全部都实现。下面我们就具体来了解一下。
这项能力解决的是如何创建网络、如何将容器接入到网络,以及容器如何退出和删除网络的问题。这个过程实际上是对容器网络的生命周期管理,如果你更熟悉 Docker 命令,可以把它类比理解成基本上等同于docker network命令所做的事情。
而 CNI 中只要实现对网络的增加与删除两项操作即可。你甚至不需要学过 Golang 语言,只从名称上都能轻松看明白以下接口中,每个方法的含义是什么。
type CNI interface {
AddNetworkList (net *NetworkConfigList, rt *RuntimeConf) (types.Result, error)
DelNetworkList (net *NetworkConfigList, rt *RuntimeConf) error
AddNetwork (net *NetworkConfig, rt *RuntimeConf) (types.Result, error)
DelNetwork (net *NetworkConfig, rt *RuntimeConf) error
}
这项能力解决的是如何为三层网络分配唯一的 IP 地址的问题。我们知道,二层网络的 MAC 地址天然就具有唯一性,不需要刻意考虑如何分配的问题。但是三层网络的 IP 地址只有通过精心规划,才能保证在全局网络中都是唯一的。否则,如果两个容器之间可能存在相同地址,那它们就最多只能做 NAT,而不可能做到直接通信。
相比起基于 UUID 或者数字序列实现的全局唯一 ID 产生器,IP 地址的全局分配工作要更加困难一些。
首先是要符合 IPv4 的网段规则,而且得保证不重复,这在分布式环境里就只能依赖 etcd、ZooKeeper 等协调工具来实现,Docker 自己也提供了类似的 libkv 来完成这项工作;其次是必须考虑到回收的问题,否则一旦 Pod 发生持续重启,就有可能耗尽某个网段中的所有地址;最后还必须要关注时效性,原本 IP 地址的获取采用标准的DHCP协议(Dynamic Host Configuration Protocol)就可以了,但 DHCP 有可能产生长达数秒的延迟,对于某些生存周期很短的 Pod,这就已经超出了它的忍受限度,所以在容器网络中,往往 Host-Local 的 IP 分配方式会比 DHCP 更加实用。
事实上,一个业界标准成功与否,很大程度上取决于它的支持者阵营的规模,对于容器网络这种插件式的规范就更是如此了。
Kubernetes 开源的初期(Kubernetes 1.5 提出 CRI 规范之前),在容器引擎上是选择彻底绑定于 Docker 的,但是在容器网络的选择上,Kubernetes 一直都坚持独立于 Docker,自己来维护网络。
在 CNI 提出以前的早期版本里,Kubernetes 会使用 Docker 的空置网络模式(--network=none)来创建 Pause 容器,然后通过内部的 kubenet 来创建网络设施,再让 Pod 中的其他容器加入到 Pause 容器的名称空间中,共享这些网络设施。
Kubernetes 最终决定,转为支持当时极不成熟的 RKT 的网络提案,他们与 CoreOS 合作,以 RKT 网络提案为基础发展出了 CNI 规范。
首先要说明的是,到今天为止,支持 CNI 的网络插件已经多达数十种,我不太可能逐一细说。不过,跨主机通信的网络实现方式,来去也就 Overlay 模式、路由模式、Underlay 模式这三种,所以接下来,我就不妨以网络实现模式为主线,每种模式给你介绍一个具有代表性的插件,以达到对网络插件生态窥斑见豹的效果。
我们已经学习过 Overlay 网络,知道这是一种虚拟化的上层逻辑网络,好处在于它不受底层物理网络结构的约束,有更大的自由度,更好的易用性;坏处是由于额外的包头封装,导致信息密度降低,额外的隧道封包解包会导致传输性能下降。
而在虚拟化环境(如 OpenStack)中,网络限制往往比较多,比如不允许机器之间直接进行二层通信,只能通过三层转发。那么,在这类被限制网络的环境里,基本上就只能选择 Overlay 网络插件。
路由模式其实是属于 Underlay 模式的一种特例,这里我把它单独作为一种网络实现模式来给你介绍一下。
相比起 Overlay 网络,路由模式的主要区别在于,它的跨主机通信是直接通过路由转发来实现的,因而不需要在不同主机之间进行隧道封包。这种模式的好处是性能相比 Overlay 网络有明显提升,而坏处是路由转发要依赖于底层网络环境的支持,并不是你想做就能做到的。
路由网络要求要么所有主机都位于同一个子网之内,都是二层连通的;要么不同二层子网之间由支持边界网关协议(Border Gateway Protocol,BGP)的路由相连,并且网络插件也同样支持 BGP 协议去修改路由表。
这里的 Underlay 模式特指让容器和宿主机处于同一网络,两者拥有相同的地位的网络方案。Underlay 网络要求容器的网络接口能够直接与底层网络进行通信,因此这个 **** 模式是直接依赖于虚拟化设备与底层网络能力的。常见的 Underlay 网络插件,有 MACVLAN、SR-IOV(Single Root I/O Virtualization)等。
实际上,对于真正的大型数据中心、大型系统来说,Underlay 模式才是最有发展潜力的网络模式。这种方案能够最大限度地利用硬件的能力,往往有着最优秀的性能表现。但也是由于它直接依赖于硬件与底层网络环境,必须根据软、硬件情况来进行部署,所以很难能做到 Overlay 网络那样的开箱即用的灵活性。
如何保证信息安全准确快速地出传输、如何更好地连接不同的集群节点、如何连接异构的容器云平台,这些都是我们需要考虑的一系列的网络问题。当然,容器网络技术也在持续地演进之中。我们要知道,容器间网络是把应用从单机扩展到集群的关键钥匙,但它也把虚拟化容器推入到了更复杂的境地,网络要去适应这种变化,要去适配容器的各种需求,所以才出现了百花齐放的容器网络方案。