K8S的设计初衷就是支持可插拔架构,解决PaaS平台不好用、不能用、需要定制化等问题,K8S集成了插件、附加组件、服务和接口来扩展平台的核心功能。附加组件被定义为与环境的其他部分无缝集成的组件,提供类似本机的特性,并扩展集群管理员可用的组件,扩展还可以用于添加自定义软硬件的支持;服务和接口提供了看似繁琐和冗余的设计(比如我们常见的PV、PVC、SC),实际上为开发人员提供了更多的可扩展性。在本文中,我们将更多地关注K8S提供三个特定功能的接口插件:运行时插件、存储插件和网络插件。更具体地说,我们将讨论容器网络接口(CNI)、容器运行时接口(CRI)和容器存储接口(CSI)如何扩展K8S的核心功能,以及它对定制服务的支持。
众所周知,CNI是一个类似于docker0的网桥,那么K8S为什么没有直接在docker0的基础上实现overlay网络插件,而是重新抽象了CNI?
如下图所示,三层覆盖网络直接在docker0网桥的基础上进行数据转发,docker0和cni同样都是实现了网桥的功能。
而K8S在实现的时候偏偏把docker0换成了cni0,如下图所示。之所以如此,其主要原因是docker和K8S的网络模型的设计思路是完全不同的。
Docker容器只有在同一台机器上(也就是同一个虚拟网桥上)才能与其他容器进行通信。不同机器上的容器无法相互连接——事实上,对于其它服务器,它们最终可能拥有完全相同的网络范围和IP地址。
K8S网络模型要求所有容器都可以直接使用IP地址与其他容器通信,而无需使用NAT;所有宿主机都可以直接使用IP地址与所有容器通信,而无需使用NAT。反之亦然。容器自己的IP地址,和别人(宿主机或者容器)看到的地址是完全一样的。
另外,K8S在Pod范围内的所有容器共享同一个网络命名空间——包括它们的IP地址。这意味着同一个Pod内部的容器可以通过localhost直接通信,这一点通过docker0网桥是无法直接简单实现的。
因此,K8S提供了一个插件规范,称为容器网络接口(CNI)。只要满足K8S的基本需求,第三方厂商可以随意使用自己的网络栈,通过使用overlay网络来支持多子网或者一些个性化使用场景,所以出现很多优秀的CNI插件被添加到Kubernetes集群中。
Calico是许多集群管理员使用的一个流行插件,它使用标准的三层网络方案提供可伸缩的网络功能。它不仅支持AWS等公有云环境中的网络划分,而且能够跟私有云网络无缝集成。
Flannel是另一个利用三层网络结构的CNI插件。它直接使用K8S API并直接设置默认的VXLAN体系结构。与其他工具一起使用时,Flannel为用户提供了可扩展的支持,比如我们可以选择使用hostgw。
Canal在易用性和健壮性之间提供了很好的平衡。它基本上是一个随时可用的VXLAN网络解决方案,利用现有的CNI插件(如Calico),包括自定义网络策略和策略隔离。Canal使构建网络架构的过程变得更容易。
我们在谈论CNI插件时不能不提到Weave Net,它提供的模式和我们目前为止讨论的所有网络方案都不同。Weave在集群中的每个节点之间创建网状Overlay网络,参与者之间可以灵活路由。这一特性再结合其他一些独特的功能,在不同的云网络配置中整合覆盖网络,使网络更加通用。对于那些寻求功能丰富的网络、同时希望不要增加大量复杂性或管理难度的人来说,Weave是一个很好的选择。它设置起来相对容易,提供了许多内置和自动配置的功能,并且可以在其他解决方案可能出现故障的场景下提供智能路由。例如,它可以在K8S不同服务之间提供通信加解密服务。
CNI插件并不是Kubernetes唯一可用的网络插件。虽然CNI插件被设计成与Kubernetes作为一个平台无缝集成的接口,并以一种更开放的方式提供功能,但你仍然可以选择使用Kubernetes插件通过基本的cbr0实现与CNI插件一起工作。
初期,K8S并没有实现CRI功能,docker运行时代码跟kubelet代码耦合在一起,再加上后期其它容器运行时的加入给kubelet的维护人员带来了巨大负担。解决方式也很简单,把kubelet对容器的调用之间再抽象出一层接口即可,这就是CRI。CRI接口设计的一个重要原则是只关注接口本身,而不关心具体实现,kubelet就只需要跟这个接口打交道。而作为具体的容器项目,比如Docker、rkt、containerd、kata container它们就只需要自己提供一个该接口的实现,然后对kubelet暴露出gRPC服务即可。简单来说,CRI主要作用就是实现了kubelet和容器运行时之间的解耦。
容器运行时位于每个K8S运行环境中。它是K8S的核心组件,负责获取硬件资源信息、镜像拉取、容器运行和停止,并确保容器接收到它们最佳运行所需的资源。然而,容器运行时自身并不受限制。
容器运行时接口或CRI插件在这里允许新的容器API被充分利用。使用正确的插件可以使Docker等运行时更加灵活。当然,CRI插件提供了一个主要的好处:它们允许您使用不同的容器运行时,而无需重新编译。
仔细观察,CRI插件提供了三个主要功能,第一个是前面提到的对更换容器运行时的支持。这意味着您可以在任何阶段和任何原因更改Kubernetes环境使用的运行时。如果您发现一个运行时比另一个运行时更有效率,通过CRI,更换就变得容易,很多组织就把Docker运行时换成了相对更高效的containerd。
CRI集成了gRPC协议并对外提供API,所以你可以在你的环境的一部分使用像Dart和Go这样的语言,在另一部分使用Python或Java。RPC框架被设计为在任何环境或网络体系结构之上运行。特别是gRPC API,它简化了服务定义,并且可以很容易地扩展为无数个rpc接口。
最流行的CNI插件是CRI-O,一个容器运行时,以其不可思议的轻便和敏捷而闻名。它可以与Kubic(它被配置为可以开箱运行CRI-O)以及Minikube和Kubeadm一起工作。它完全集成了Open Container Initiative (OCI),消除了对Docker的依赖;您可以运行Kata容器或使用任何OCI容器镜像启动容器。
其实K8S在没有提供CSI接口之前就已经提供了强大的数据卷插件系统,但是这些插件系统实现是K8S核心代码的一部分,并随K8S核心二进制文件一起发布。如果第三方存储厂商发现一些问题,即使修复后不得不跟K8S一起发布;另外第三方厂商的代码跟K8S核心代码存储在一起引起了安全性、可靠性以及后期维护成本。K8S将存储体系抽象出了外部存储组件接口,也就是CSI,通过grpc接口对外提供服务。第三方存储厂商可以发布和部署公开的存储插件,而无需接触K8S核心代码。同时为K8S用户提供了更多的存储选项,这不正是衡量开源项目质量的重要标准吗?
CSI允许第三方存储提供商提供持久的和动态的存储块,而K8S集群本身并不需要去实现它们。CSI插件和核心K8S卷插件之间的主要区别是CSI插件不需要编译和附带核心Kubernetes二进制文件。
CSI插件的其他特性也同样有趣。原始块卷允许您为块卷创建CSI驱动程序,并允许将这些块分配给K8S运行时。支持在任意时间点创建和恢复存储块快照。像MapR Data Fabric这样的插件甚至支持livenessprobe这样的命令,它允许容器探测存储驱动程序。
有一些经过认证的CSI驱动程序和插件可以立即集成到K8S环境中。来自Blockbridge、VMware和Portworx的插件自动支持动态配置,并提供了管理CSI部署的GUI。
结合前面讨论过的CNI和CRI、CSI插件,无论多么复杂的应用程序,Kubernetes都可以很好的支持,这使得基于K8S的PaaS平台非常健壮,并且能够更有效地应对现代云计算带来的挑战。
更多文档请参考 [1][2][3]
[1]
容器运行时: https://www.programmersought.com/article/92413759101/
[2]存储插件: https://kubernetes.io/blog/2019/01/15/container-storage-interface-ga/
[3]接口设计: https://caylent.com/understanding-kubernetes-interfaces-cri-cni-csi
HPA|聊聊K8S的横向扩容能力
Kubernetes入门培训(内含PPT)
原创不易,随手关注或者”在看“,诚挚感谢!