Kubernetes 架构核心组件工作原理解析

网友投稿 877 2022-10-18

Kubernetes 架构核心组件工作原理解析

Kubernetes 架构核心组件工作原理解析

Kubernetes 的所有管理能力构建在对象抽象的基础上,核心对象包括∶

Kubernetes采用与borg类似架构/组件合作关系

APIServer可以简单理解为Rest server,它接受外部请求的,无论是通过命令行还是浏览器,这些请求都会被转化为rest的调用,发到APIServer里面,APIServer会将请求存放到自己数据库里面就结束了,APIServer就做这些事情,接受请求,并且存储。

etcd本身是个数据库,其次etcd访问有种模式叫做watch模式,也就是当你去get一个对象的时候,你可以加一个watch的参数,那么客户端的这次get请求首先会完成,其次连接不会断掉,客户端会和etcd保持长连接,要请求的对象后续发生任何的变换,etcd会主动的将这个变化以事件的形式推送给客户端。

所以etcd 第一 是一个数据库  第二 是个消息中间件,任何对象的变更都会推送,所以Apiserver会去和etcd做连接,apiserver将数据推送到etcd,etc任何的对象变更,会主动通知apiserver。

apiserver除了去接受客户端和各个组件过来的请求,它也可以将对象的变更推送出去。所以这样的话整个系统就可以联动起来了。

api server是简单的rest api,它提供了对象的传输,接受其他所有组件请求(上图可以看到,所有组件都是连接到api server的),也就是其他组件之间不互相通信,其他组件都和api server通信,将请求发送给apiserver,apiserver做认证,鉴权,校验之后将数据存储到etcd,任何对象的变化都会推送给其他的组件。

通过上面消息的机制来驱动整个系统的变化。

在管理节点上面还有两个重要组件,schedule,用户创建pod会被,会被apiserver接收到,apiserver就会告诉schedule有这么一个调度需求去做调度,schedule就会去做调度,先看你的pod需要多少资源,也就是你的需求是什么,然后去看集群里面资源利用状况,这个资源状况哪里来的呢?每个节点上面有个kubelet,第一它会将资源信息上报,报告每个节点的健康状况和资源使用情况,apiserver接收到这些请求之后,同样将数据存放到etcd里面,同时schedule能够接受到变化的请求,所以shcedule是知道集群整个资源利用情况的。

接收到调度请求之后,调度器就有能力帮它选择最适合的节点,完成调度,所谓的完成调度就是将pod和节点绑定,告诉APiserver,apiserver将pod的信息里面nodename属性更新到etcd里面保存下来。

最后kubelet就会去看和我这个节点相关的pod有些哪些,如果新的pod和我相关我就去拉起进程。

所有的kubernetes组件均使用“watch”机制来跟踪检查API Server上的相关的变动。

apiserver里面没有什么业务逻辑,它就接受请求,只要你有权限,请求又是合法的,就存一下,这就是apiserver的动作,这个请求存下来之后,具体怎么做是由control manager去做的。

上面就是控制面组件的合作关系

各个组件细节

apiserver其实就是整个集群的api 网关,那么就包括认证和鉴权,确保客户端请求是合法的,并不是谁都可以给我发指令,我需要知道你是谁,其次还需要做鉴权,知道你是谁但是不代表你有整个集群的权限,所以要去校验有没有这个权限,最后你有这个权限,但是也不代表你的操作是合法的,所以要做准入控制。

conrtol manager管理了一堆的控制器,比如deployment控制器,replicaset,lifecycle控制器,他们分别用来管理不同的对象。

control manager是让整个集群运作起来的核心,它是一个大脑,apiserver里面没有什么业务逻辑,它就接受请求,只要你有权限,请求又是合法的,就存一下,这就是apiserver的动作,这个请求存下来之后,具体怎么做是由control manager去做的。

工作节点上面的kubelet:第一 上报节点的健康状态和资源需求,第二 维护当前节点pod的生命周期,第三 cadvisor收集容器的资源使用情况。

kube-proxy:就是你去定义service的时候,发布服务的时候要为这个服务配置负载均衡。

Kubernetes 主节点(Master Node)

kubeadm安装的集群默认将控制面的组件安装到了kube-system下面

[root@master ~]# kubectl get po -n kube-systemNAME READY STATUS RESTARTS AGEcalico-kube-controllers-97769f7c7-7md6n 1/1 Running 8 65dcalico-node-445z4 1/1 Running 5 65dcalico-node-5j6fg 1/1 Running 6 65dcalico-node-bjqq6 1/1 Running 5 65dcoredns-6d56c8448f-6zm8m 1/1 Running 5 65dcoredns-6d56c8448f-pltwl 1/1 Running 6 65detcd-master 1/1 Running 5 65dkube-apiserver-master 1/1 Running 3 34dkube-controller-manager-master 1/1 Running 9 35dkube-proxy-7ffvg 1/1 Running 5 65dkube-proxy-j75lv 1/1 Running 5 65dkube-proxy-npt9k 1/1 Running 6 65dkube-scheduler-master 1/1 Running 16 65dmetrics-server-b66888848-c2p6b 1/1 Running 11 34d

Kubernetes 工作节点(worker node)

ETCD

etcd是分布式的存储,它一般要做两件事情,一件事情是选主,选出哪个是master,所有的写需求经由master,否则当所有member都接受写操作,那么就会发生冲突.

raft协议保证两个事情,第一个通过投票方式选主,选择一个节点为leader,所有写操作从leader去做,其次leader在接收到写操作的数据之后,会将写的指令下发,下发到其他的节点上面去,其他节点通过某种机制来保证和leader同步,这样才能保住数据的一致性。

第二个etcd支持监听机制的,它也是消息中间件。

直接访问Etcd数据

etcdctl是操作etcd的客户端工具,可以yum安装,也可以进入etcd pod当中自带了。

下面是去get一些值,因为是键值对存储,我要去get所有的key,key一斜杠/开头我就可以将其打印出来,通过这样的方式就可以看到当前集群在etcd当中存储的路径。

[root@master etcd]# ETCDCTL_API=3 etcdctl --endpoints --cert /etc/kubernetes/pki/etcd/server.crt --key /etc/kubernetes/pki/etcd/server.key --cacert /etc/kubernetes/pki/etcd/ca.crt get --keys-only --prefix / | head -n 10/registry/apiextensions.k8s.io/customresourcedefinitions/bgpconfigurations.crd.projectcalico.org/registry/apiextensions.k8s.io/customresourcedefinitions/bgppeers.crd.projectcalico.org/registry/apiextensions.k8s.io/customresourcedefinitions/blockaffinities.crd.projectcalico.org/registry/apiextensions.k8s.io/customresourcedefinitions/caliconodestatuses.crd.projectcalico.org/registry/apiextensions.k8s.io/customresourcedefinitions/clusterinformations.crd.projectcalico.org

如果要监听对象的变化可以使用watch操作来监听,任何对象发生了变化,一旦敲了这条命令,那么就保持在那,一旦有对象发生了变更它就会通知你,这就是它的消息机制。

[root@master ~]# kubectl get podNAME READY STATUS RESTARTS AGEdns-test 1/1 Running 3 36dnginx-74d69c5bbb-6478r 1/1 Running 3 34dnginx-74d69c5bbb-mhmsc 1/1 Running 3 34dnginx-web-0 1/1 Running 4 36dnginx-web-1 1/1 Running 3 36d[root@master ~]# kubectl delete pod nginx-web-1 pod "nginx-web-1" deleted[root@master etcd]# ETCDCTL_API=3 etcdctl --endpoints --cert /etc/kubernetes/pki/etcd/server.crt --key /etc/kubernetes/pki/etcd/server.key --cacert /etc/kubernetes/pki/etcd/ca.crt watch --prefix /registry/statefulsets/default/nginx-webPUT/registry/statefulsets/default/nginx-webk8sapps/v1 StatefulSet nginx-webdefault"*$08a540f1-83b9-4575-9441-7e2a1e079e0528..b?0kubectl.kubernetes.io/last-applied-configuration.{"apiVersion":"apps/v1","kind":"StatefulSet","metadata":{"annotations":{},"name":"nginx-web","namespace":"default"},"spec":{"replicas":2,"selector":{"matchLabels":{"app":"nginx_test"}},"serviceName":"nginx-test","template":{"metadata":{"labels":{"app":"nginx_test"}},"spec":{"containers":[{"image":"nginx:1.11","name":"nginx-test","ports":[{"containerPort":80,"name":"nginx-web"}]}]}}}}z.

APIServer

apiserver本身是rest server,所以它的扩展是比较简单的,etcd是一个有状态的,对于有状态的加减一个member还是需要做些配置,对于无状态的apiserver来说就只需要做水平扩展就行了,如果不够了添加一个apiserver实例就行了,前面负载均衡把这个实例加进来就行了。

准入控制(mutating&valiating)

准入其实又分为两个阶段 mutating:你要建一个对象,这个对象发到apiserver了,但是从集群层面上来说要给这个对象做些变形,给你增加一些属性,或者改变某些值,这个时候就可以通过mutating操作在apiserver接收到请求到存储到etcd过程中给你原始的需求里面添加一些其他的属性,这样方便做平台层面的一些动作。

valiting:前面做了那些操作,你可能mutat了一个对象,变形了,但是对象是否合法呢?后面还有一些校验逻辑的,加一些validting的逻辑,比如一个对象的名称不能超过10,那怎么办,加一个validting的规则,让它去校验一下,如果超过10,就不让它通过,这里就需要实现准入了逻辑,准入不通过,那么就不会存储到etcd当中。

认证,鉴权,准入是三道门,任何一道门不通过请求就被废弃掉了,它不会存储到etcd里面。

缓存

apiserver本身还是一个缓存,apiserver是唯一一个和etcd通信的组件,etcd本身规是分布式的键值对,可想而知性能不好特别的好,如果有海量的需求那么是一定没有办法帮你处理的。

apiserver里面维护了一份数据的缓存,如果是读操作,apiserver这边是有cache的,如果在cache当中就直接返回给你了,这个请求不会发送到etcd里面去。

APIServer展开

apiserver是一个rest server,他要去注册每个对象的handler,所以有个api handler的过程,接下来会去做认证,做完认证之后要去做限流,限流就是自我保护,每个apiserver节点都有承受上线的,如果不做限流那么很可能并发就将其打死了,之后来到audit做审计,任何的对象操作会记录log,为了日后的安全审查,在之后authz就是做鉴权的,k8s自己支持RBAC的,另外你也可以编写自己的webhook去做鉴权。

Aggregator:因为本身apiserver作为一个rest api,它其实是有能力和nginx反向代理软件一样来做一些路由配置,比如你是标准的k8s对象,那么你就走默认的k8s apiserver,那么muta valid就继续往下走了,所有路走完了就到etcd了,但是如果我有额外的扩展对象,这些额外的对象我不想在k8s本身实例里面去实现,我希望有自己的逻辑,那么就需要自己去写aggregated apiserver,aggregate会去看你这个对象在另外一个apiserver里面那么就会将请求转到这个aggregated apiserver,在之后的逻辑和上面是一样的,只不过是独立部署的apiserver。

Controller Manager

它里面有很多的控制器,很多的控制器都有不同的职责,所有的控制器都遵循同样的规范,先去读用户的这个抽象对象,这个对象里面有用户的期望状态,它读取到期望状态就需要去干活了,它就需要去做真实的配置,确保系统的真实状态和用户的期望状态保持一致。

控制器的工作流程

针对任何对象,k8s其实他有一些辅助工具叫做codegenerator,会帮你生成对象一些访问框架和代码框架,这个代码框架主要是control interface,分为两个部分,一部分为informer,另外一部分为lister,任何的对象k8s允许你去做监听,所谓的informer就是相当于去监听某个对象,你可以通过informer的框架去监听某个对象,这个对象发生任何的状态变化,比如增 删 改这些写的动作,它都会生成新的event。

控制器利用这个framework之后就需要去监听这些事件,任何的控制器都是生产者和消费者模型,监听到这些事件之后将Key拿出来(informer上面的部分其实是生产者,生产者去监听这些对象的事件,然后注册你的handler,它做的就是将对象时间的key存放到一个队列里面去,消费者就是worker thread,它要做的就是从队列里面取出数据,取出来去做配置)

lister部分是用来干嘛的呢?相当于所有kuebernetes在list watch之后,它会在客户端那边存一个缓存,也就说通过List可以直接在客户端这边拿到这个对象的缓存状态,而不需要去访问apiserver了。list更多的是提供了接口,让你从clicent-cache里面获取对象。

Deployment 控制器

我们先简单看一下控制器实现原理。

首先,我们所有的控制器都是通过 Informer 中的 Event 做一些 Handler 和 Watch。

这个地方 Deployment 控制器,其实是关注 Deployment 和 ReplicaSet 中的 event,收到事件后会加入到队列中。

而 Deployment controller 从队列中取出来之后,它的逻辑会判断 Check Paused,这个 Paused 其实是 Deployment 是否需要新的发布,如果 Paused 设置为 true 的话,就表示这个 Deployment 只会做一个数量上的维持,不会做新的发布。

如上图,可以看到如果 Check paused 为 Yes 也就是 true 的话,那么只会做 Sync replicas。也就是说把 replicas sync 同步到对应的 ReplicaSet 中,最后再 Update Deployment status,那么 controller 这一次的 ReplicaSet 就结束了。

ReplicaSet 控制器

当 Deployment 分配 ReplicaSet 之后,ReplicaSet 控制器本身也是从 Informer 中 watch 一些事件,这些事件包含了 ReplicaSet 和 Pod 的事件。从队列中取出之后,ReplicaSet controller 的逻辑很简单,就只管理副本数。也就是说如果 controller 发现 replicas 比 Pod 数量大的话,就会扩容,而如果发现实际数量超过期望数量的话,就会删除 Pod。

上面 Deployment 控制器的图中可以看到,Deployment 控制器其实做了更复杂的事情,包含了版本管理,而它把每一个版本下的数量维持工作交给 ReplicaSet 来做。

那么如果 paused 为 false 的话,它就会做 Rollout,也就是通过 Create 或者是 Rolling 的方式来做更新,更新的方式其实也是通过 Create/Update/Delete 这种 ReplicaSet 来做实现的。

Informer的内部工作机制

上面是APIserver,shareinformer通过list watch去关注一个对象,所谓list watch就是第一,他会将所有对象list返回回来,第二作为一个长连接,关注它后续的变更,所有这些框架都是通用的。

但是这个框架所关注的不同的对象是怎么转换的呢?就是informer里面有reflector,就是按照你给定的对象的类型,(本身apiserver是个rest server,它传回给你的是json或者protobuffer序列化的对象,那你要做反序列化,要将其转化为指定的对象,那么通过反射的机制,去转化为不同类型的对象 )这里就有一个delta fifo quenue,这个对象最后会存到thread safe store,就是List部分,这部分对象无论是被list到还是watch到,它的事件变化都会都会通过informer发送到handler,由handler处理这些对象事件。

控制器协同工作原理

当命令敲下去做了什么,第一将deployment创建出来了,就这样结束了,这个请求就被发到apiserver了,加上-v 9。

客户端做的第一件事情是它去读取config file,这个就是默认kubectl读取的配置

这个配置里面有什么呢?

这里面有我的apiserver的地址,已经客户身份的一些认证信息,所以他会将请求发送到api server的地址

deployment它就会去创建副本集的对象叫replicaset,replicaset就是副本集,也就是deployment告诉apiserver我创建了一个副本集,这个副本集里面有个对象,然后模板是怎么样的,这个副本集被apiserver接受了之后,一样结果认证,鉴权,准入控制存入到etcd里面。

这个时候replicaset controller会watch apiserver,监听replicaset对象,他发现replicaset对象被创建了,它就需要去解析了,你需要几个副本以及pod的模板是怎么样的,那么replicatset controller就会去创建pod,这个pod一样会提交到apiserver这边,在Pod刚刚创建出来它有一个属性叫做nodename,刚创建出来nodename属性是空的,也就是pod没有经过调度的,它没有和任何节点产生绑定关系,这个时候调度器就去工作了,它去集群节点里面,找一个适合这个pod的节点,并且绑定pod,那么nodename这个值就被调度器填上了。

之后pod就和节点产生了绑定关系,这个节点就会去加载这个pod,通过容器运行时,将容器进程拉起来,(kubernetes将这些都标准化了,抽象为cri cni csi),通过cni插件将网络配置起来通过csi将存储帮它挂载上去,这样就完成了pod应用的加载。

schedule

调度器predict阶段:你一个集群多少个节点,pod有一个pod的资源需求,先将不满足需求的过滤掉,比如资源不足,端口冲突。过滤出来的节点就要去评分,里面有很多的插件,按照不同的维度,权重去评分,然后选择评分最高的节点,作为pod的被选节点。最后通过bind的操作将节点和pod产生绑定关系。

Kubelet

Kube-proxy

推荐的Add-Ons

上面是kubernetes的核心组件,kubernetes还有一些Add-Ons组件,最核心的就是kube-dns,就是整个集群的域名服务,没有核心组件集群是启动不起来的,add-on是指有了核心组件,add-ons可以以用户创建pod的方式去启动起来。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:pq - database/sql的纯Go Postgres 驱动程序
下一篇:基于Spring Cache实现Caffeine+Redis二级缓存
相关文章

 发表评论

暂时没有评论,来抢沙发吧~