容器网络与生态

2023-09-18 15:21:08 浏览数 (1)

容器网络与生态:CNI下的网络插件生态

提出容器网络标准的目的,就是为了把网络功能从容器运行时引擎、或者容器编排系统中剥离出去,毕竟网络的专业性和针对性极强,更适合做成外部可扩展的功能

从程序功能上看, CNI 的网络插件提供的能力,都可以划分为网络的管理与 IP 地址的管理两类,而插件可以选择只实现其中的某一个,也可以全部都实现。下面我们就具体来了解一下。

  • 管理网络创建与删除

这项能力解决的是如何创建网络、如何将容器接入到网络,以及容器如何退出和删除网络的问题。这个过程实际上是对容器网络的生命周期管理,如果你更熟悉 Docker 命令,可以把它类比理解成基本上等同于docker network命令所做的事情。

而 CNI 中只要实现对网络的增加与删除两项操作即可。你甚至不需要学过 Golang 语言,只从名称上都能轻松看明白以下接口中,每个方法的含义是什么。

代码语言:javascript复制
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 地址分配与回收

这项能力解决的是如何为三层网络分配唯一的 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 容器的名称空间中,共享这些网络设施。

  • 额外知识:kubenet kubenet是 kubelet 内置的一个非常简单的网络,它是采用网桥来解决 Pod 间通信。kubenet 会自动创建一个名为 cbr0 的网桥,当有新的 Pod 启动时,会由 kubenet 自动将其接入 cbr0 网桥中,再将控制权交还给 kubelet,完成后续的 Pod 创建流程。kubenet 采用 Host-Local 的 IP 地址管理方式,具体来说是根据当前服务器对应的 Node 资源上的PodCIDR字段所设的网段,来分配 IP 地址。当有新的 Pod 启动时,会由本地节点的 IP 段中分配一个空闲的 IP 给 Pod 使用。

Kubernetes 最终决定,转为支持当时极不成熟的 RKT 的网络提案,他们与 CoreOS 合作,以 RKT 网络提案为基础发展出了 CNI 规范。

  • 补充知识CNM,CNM曾是CNI的竞争对手 在非技术方面,Kubernetes 决定放弃 CNM 的原因,很大程度上还是由于他们与 Docker 在发展理念上的冲突,Kubernetes 当时已经开始推进 Docker 从必备依赖变为可选引擎的重构工作了,而 Docker 则坚持 CNM 只能基于 Docker 来设计。蒂姆·霍金在他的文章中举了一个例子:CNM 的网络驱动没有向外部暴露网络所连接容器的具体名称,只使用了一个内部分配的 ID 来代替,这就让外部(包括网络插件和容器编排系统)很难将网络连接的容器与自己管理的容器对应关联起来,而当他们向 Docker 开发人员反馈这个问题时,却以“工作符合预期结果”(Working as Intended)为理由,被直接关闭掉了这个问题。 蒂姆·霍金还专门列出了这些问题的详细清单,比如libnetwork #139、libnetwork #486、libnetwork #514、libnetwork #865、docker #18864。这种设计,被 Kubernetes 认为是在人为地给非 Docker 的第三方容器引擎使用 CNM 设置障碍。而在整个沟通过程中,Docker 表现得也很强硬,明确表示他们对偏离当前路线或委托控制的想法都不太欢迎。

网络插件生态

首先要说明的是,到今天为止,支持 CNI 的网络插件已经多达数十种,我不太可能逐一细说。不过,跨主机通信的网络实现方式,来去也就 Overlay 模式、路由模式、Underlay 模式这三种,所以接下来,我就不妨以网络实现模式为主线,每种模式给你介绍一个具有代表性的插件,以达到对网络插件生态窥斑见豹的效果。

  • Overlay 模式

我们已经学习过 Overlay 网络,知道这是一种虚拟化的上层逻辑网络,好处在于它不受底层物理网络结构的约束,有更大的自由度,更好的易用性;坏处是由于额外的包头封装,导致信息密度降低,额外的隧道封包解包会导致传输性能下降。

而在虚拟化环境(如 OpenStack)中,网络限制往往比较多,比如不允许机器之间直接进行二层通信,只能通过三层转发。那么,在这类被限制网络的环境里,基本上就只能选择 Overlay 网络插件。

  • 路由模式

路由模式其实是属于 Underlay 模式的一种特例,这里我把它单独作为一种网络实现模式来给你介绍一下。

相比起 Overlay 网络,路由模式的主要区别在于,它的跨主机通信是直接通过路由转发来实现的,因而不需要在不同主机之间进行隧道封包。这种模式的好处是性能相比 Overlay 网络有明显提升,而坏处是路由转发要依赖于底层网络环境的支持,并不是你想做就能做到的。

路由网络要求要么所有主机都位于同一个子网之内,都是二层连通的;要么不同二层子网之间由支持边界网关协议(Border Gateway Protocol,BGP)的路由相连,并且网络插件也同样支持 BGP 协议去修改路由表。

  • Underlay 模式

这里的 Underlay 模式特指让容器和宿主机处于同一网络,两者拥有相同的地位的网络方案。Underlay 网络要求容器的网络接口能够直接与底层网络进行通信,因此这个 **** 模式是直接依赖于虚拟化设备与底层网络能力的。常见的 Underlay 网络插件,有 MACVLAN、SR-IOV(Single Root I/O Virtualization)等。

实际上,对于真正的大型数据中心、大型系统来说,Underlay 模式才是最有发展潜力的网络模式。这种方案能够最大限度地利用硬件的能力,往往有着最优秀的性能表现。但也是由于它直接依赖于硬件与底层网络环境,必须根据软、硬件情况来进行部署,所以很难能做到 Overlay 网络那样的开箱即用的灵活性。

小结

如何保证信息安全准确快速地出传输、如何更好地连接不同的集群节点、如何连接异构的容器云平台,这些都是我们需要考虑的一系列的网络问题。当然,容器网络技术也在持续地演进之中。我们要知道,容器间网络是把应用从单机扩展到集群的关键钥匙,但它也把虚拟化容器推入到了更复杂的境地,网络要去适应这种变化,要去适配容器的各种需求,所以才出现了百花齐放的容器网络方案。

0 人点赞