引言
Kubernetes能够成为容器编排领域事实标准的一个非常重要的原因就是它具有极强的扩展性。高度的可扩展性在丰富其功能特性的同时,吸引了大量国内外厂商和个人围绕着Kubernetes的可扩展特性开发出极富创造性的云上产品,各显其能,各得其利,人人都有机会从迅速做大的云原生蛋糕中分得一杯羹,云原生生态也因之空前繁荣。
Kubernetes原生提供了两种扩展方式:API Server聚合(API Aggregation)和自定义资源定义(CustomResourceDefinition)。其中,前者是开发单独的API Server并通过内置的聚合层与原生API Server聚合形成统一的API视图,这种方式灵活性极高,但开发难度大。后者则是直接通过API Server内置的自定义资源与控制器支持对API Server本身进行扩展,在保证足够灵活性的同时降低了开发运维难度。
CRD是业内普遍采用的扩展方式,大至Rancher、OpenShift、ACP等容器云平台,小到Prometheus-Operator、ElasticSearch-Operator等厂商产品集群的自动化管理服务;从存储编排引擎Rook到虚拟机管理系统VM-Operator,CRD扩展方式在各个领域都有着丰富的应用。
CRD开发无疑是Kubernetes的一个重要主题,从本篇开始,我们将使用一个系列的篇幅对其进行深入介绍。由于CRD开发的主要工作是自定义控制器的开发,因此我们将本系列主题命名为《自定义控制器开发》。在本主题中我们将首先介绍自定义资源与控制器的实现原理,然后分别详细讲解手动和脚手架两种自定义控制器开发方式,最后介绍流行的容器云平台Rancher的开发知识以及如何将自定义控制器集成到Rancher Server中。
本期我们主要介绍自定义资源与控制器。
资源与控制器
资源
在Kubernetes中,所有可被管理的对象都被抽象为资源,如节点、存储、网络策略、工作负载、配置信息、命名空间、事件、用户角色、网络出入口等等都是资源。资源会按照用途或关联度被划分为不同的分组。
资源分为集群和命名空间两个级别,集群级资源可以作用于所有命名空间。所有资源的信息都是以json或yaml格式的资源清单的形式呈现的。资源清单中描述的资源信息会由API Server经转换后以json或protobuf的结构持久化存储到Etcd数据库中。
所有资源都可以通过统一的REST API进行声明式管理,即只要通过API将资源清单传递到API Server,Kubernetes将自动维护该资源在清单中指定的状态。
Kubernetes是以API Group的方式组织各种资源的API的。API群组是一组相关API对象的集合,如Deployment、DaemonSet、StatefulSet等资源的API就属于apps组;Role、RoleBindings、ClusterRoleBindings等资源的API就属于rbac.authorization.k8s.io组。此外由于最初并未考虑资源分组,因此一些核心资源如node、pod、service等属于core组并可省略组名。API群组的概念使得API的管理和扩展变得更为方便。
每一个API群组都可以存在多个版本。基于稳定性的差异,每个版本又可以细分Alpha、Beta和Stable三个级别。资源API的多版本是在群组层面统一定义的,所有版本在一个Kubernetes集群中是可以通过API Server的无损转换机制并存使用的。
对于API而言,通过GVR三元组(API Group、API Version和Resource)就可以唯一定位某个资源的API路径,如:
/apis/rbac.authorization.k8s.io/v1beta1/clusterroles
而在我们熟悉的资源清单文件中,则是对应通过GVK三元组(API Group,API Version和Kind)进行资源区分的。如:
{\\\"apiVersion\\\":\\\"apps/v1\\\", \\\"kind\\\":\\\"Deployment\\\"}
控制器
资源是数据,而处理资源的逻辑就是控制器,Kubernetes是基于控制器模式设计的,所有资源的生命周期由API Server管理,而资源所代表的实际含义则是由对应的控制器完成解释的。系统组件kube-controller-manager中内置了大量的控制器用于对各种原生资源类型的逻辑处理 ,如Pod控制器、Endpoint控制器、Namespace控制器、Serviceaccount控制器等。
每个控制器内部都包含两个核心组件:Informer/SharedInformer和Workqueue。Informer或SharedInformer负责监听Kubernetes中资源对象的状态变化并缓存资源,同时将状态变化事件(create、update、delete)通过ResourceEventHandler发送到Workqueue中,然后由控制器中的Worker从Workqueue中取出事件交由具体的处理逻辑进行处理。
Informer
控制器监控资源状态需要向API Server发送查询请求以获取相应资源全部对象的详细信息,大量控制器对API Server的频繁请求会对API Server产生较大压力,因此控制器普遍采用本地Cache缓存机制实现对资源的监听和同步缓存,具体流程如下:
-
Reflector反射器通过Liste/Watche机制获取监听资源的变化,然后将变化的资源对象添加到Delta FIFO队列
-
Informer通知器从Delta FIFO队列中取出变化的对象,将其传递到Indexer索引器
-
Index索引器为资源对象构建索引,然后将资源数据存储到线程安全的Key-Value本地存储
当同一种资源被多个控制器监听时,可以通过SharedInformer进行统一监听和缓存,以节省监听和缓存开销。
Workqueue
控制器除了监听资源变化并缓存资源对象到本地之外,还需要针对资源变化作出响应。被监听资源对象的变化以事件的方式被存储到Workqueue工作队列中等待被处理,事件是动作(create、update、delete)和资源对象key的组合。具体流程如下:
-
Informer通知器将变化的资源传递到Indexer索引器的同时,还会通过ResourceEventHandler资源事件处理函数将事件动作和资源对象key发送到Workqueue工作队列
-
Worker从Workqueue中取出事件,根据资源对象key从Key-Value本地存储中获取资源对象详细数据,然后根据事件中的动作决定对该资源对象具体的处理逻辑
-
Worker将处理结果通过Client客户端调用API Server写入Kubernetes
控制器的所有功能组件中,Informer(包含Lister/Watcher、Reflector、LocalStore)、Workqueue和Client都是Kubernetes的原生SDK client-go默认提供的,仅ResourceEventHandler和Worker是需要开发者根据具体需求自行开发实现的。
关于client-go SDK的源码分析可参考charlieroro博客文章(点击下方
原创文章,作者:EBCloud,如若转载,请注明出处:https://www.sudun.com/ask/33604.html