kubernetes之K8s核心原理--第一篇(五)

网友投稿 258 2022-11-11

kubernetes之K8s核心原理--第一篇(五)

本来以为一篇就能搞定,还是低估了自己的废话,好吧,只能通过两篇文章向大家介绍K8s核心原理。

一、 Kubernetes API Server 原理分析

1. kubernetes API Server介绍

kubernetes API server的和核心功能是提供了kubernetes各类资源对象(pod、RC 、service等)的增、删、改、查以及watch等HTTP Rest接口,是整个系统的数据总线和数据中心。有时候我们使用kubectl创建或者查看pod等资源的时候,发现没有反应,可能就是你的kube-apiservice服务异常退出导致的。  Kubernetes API server通过一个名为kube-apiservice的进程提供服务,该进程运行与master节点上。默认情况下该进程的端口是本机的8080提供restful服务。(注意如果是HTTPS,则是6443端口)。  接下来的一些操作,介绍一些如何通过rest 与kubernetes API server交互,这有便于后各k8s各个组件之间通信的理解:

[root@zy ~]# kubectl cluster-info #查看主节点信息

[root@zy ~]# curl localhost:8080/api #查看kubernetes API的版本信息

[root@zy ~]# curl localhost:8080/api #查看kubernetes API支持的所有的资源对象

当然我们也可以访问具体的资源

[root@zy ~]# curl localhost:8080/api/v1/pods [root@zy ~]# curl localhost:8080/api/v1/services [root@zy ~]# curl localhost:8080/api/v1/replicationcontrollers

2. Kubernetes proxy API接口

kubernetes API server还提供了一类很特殊的rest接口—proxy接口,这个结构就是代理REST请求,即kubernetes API server把收到的rest请求转发到某个node上的kubelet守护进程的rest端口上,由该kubelet进程负责相应。举例:

masterIP:8080/api/v1/proxy/nodes/{node_name}/pods #某个节点下所有pod信息 masterIP:8080/api/v1/proxy/nodes/{node_name}/stats #某个节点内物理资源的统计信息 masterIP:8080/api/v1/proxy/nodes/{node_name}/spec #某个节点的概要信息

#接下来说一下比较重要的pod的相关接口

masterIP:8080/api/v1/proxy/namespaces/{namespace}/pods/{pod_name}/{path:*} #访问pod的某个服务接口 masterIP:8080/api/v1/proxy/namespaces/{namespace}/pods/{pod_name} #访问pod

假如这里有一个名为myweb的Tomcat的pod我们在浏览器中输入masterIP:8080/api/v1/proxy/namespaces/{namespace}/pods/myweb就能访问到该pod的controller(副本的控制器)的使用场景 :   - 重新调度:无论是否有节点宕机,还是pod意外死亡,RC都可以保证自己所管理的正在运行Pod的数量为预设值   - 弹性伸缩:实现集群的扩容和缩容(根据集群的可用资源和负载压力)   - 滚动升级:应用服务升级新的版本,并且保证整个升级过程,应用服务仍可对外提供服务。

2. Node Controller

3. ResourceQuota controller

三、 Scheduler 原理分析

1. 介绍

2. scheduler预选策略

在scheduler中可用的预选算有很多:NoDiskconflict、PodFitsResources、PodSelectorMatches、PodFitsHost、CheckNodeLabelPresence、CheckServiceAffinity、PodFitsPorts等策略。其中的5个默认的预选策略:PodFitsPorts、PodFitsResources、NoDiskconflict、PodSelectorMatches、PodFitsHost每个节点只有通过这5个预选策略后,才能初步被选中,进入下一个流程。下面小编介绍几个常用的预选策略:

(1) NoDiskconflict

判断备选pod的gcePersistentDisk或者AWSElasticBlockStore和备选的节点中已存在的pod是否存在冲突具体检测过程如下:     - 首先,读取备选pod的所有的volume信息,对每一个volume执行一下步骤的冲突检测     - 如果该volume是gcePersistentDisk,则将volume和备选节点上的所有pod的每个volume进行比较,如果发现相同的gcePersistentDisk,则返回false,表明磁盘冲突,检测结束,反馈给调度器该备选节点不合适作为备选的pod,如果volume是AWSElasticBlockStore,则将volume和备选节点上的所有pod的每个volume进行比较,如果发现相同的AWSElasticBlockStore,则返回false,表明磁盘冲突,检测结束,反馈给调度器该备选节点不合适作为备选的pod     - 最终,检查备选pod的所有的volume均为发现冲突,则返回true,表明不存在磁盘冲突,反馈给调度器该备选节点合适备选pod

(2) podFistResources

判断备选节点资源是否满足备选pod的需求,检测过程如下:     - 计算备选pod和节点中已存在的pod的所有容器的需求资源(CPU 和内存)的总和      - 获得备选节点的状态信息,其中包括节点的资源信息     - 如果备选pod和节点中已存在pod的所有容器的需求资源(CPU和内存)的总和超出了备选节点拥有的资源,则返回false,表明备选节点不适合备选pod,否则返回true,表明备选节点适合备选pod

(3) PodSelectorMatches

判断备选节点是否包含备选pod的标签选择器指定的标签:     - 如果pod没有指定spec.nodeSelector标签选择器,则返回true     - 如果获得备选节点的标签信息,判断节点是否包含备选pod的标签选择器所指的标签,如果包含返回true,不包含返回false

(4) PodFitsHost

判断备选pod的spec.nodeName域所指定的节点名称和备选节点的名称是否一致,如果一致返回true,否则返回false。

(5) PodFitsPorts

判断备选pod所用的端口列表汇中的端口是否在备选节点中被占用,如果被占用,则返回false,否则返回true。

3 .scheduler优选策略

Scheduler中的优选策略有:leastRequestedPriority、CalculateNodeLabelPriority和BalancedResourceAllocation等。每个节点通过优先策略时都会算出一个得分,计算各项得分,最终选出得分值最大的节点作为优选结果。小编接下来就给大家介绍一下一些常用的优选策略:

(1) leastRequestedPriority

该策略用于从备选节点列表中选出资源消耗最小的节点:     - 计算出所有备选节点上运行的pod和备选pod的CPU占用量     - 计算出所有备选节点上运行的pod和备选pod的memory占用量     - 根据特定的算法,计算每个节点的得分

(2) CalculateNodeLabelPriority

如果用户在配置中指定了该策略,则scheduler会通过registerCustomPriorityFunction方法注册该策略。该策略用于判断策略列出的标签在备选节点中存在时,是否选择该备选节点。如果备选节点的标签在优选策略的标签列表中且优选策略的presence值为true,或者备选节点的标签不在优选策略的标签列表中且优选策略的presence值为false,则备选节点score=10,否则等于0。

(3) BalancedResourceAllocation

该优选策略用于从备选节点列表中选出各项资源使用率最均衡的节点:     - 计算出所有备选节点上运行的pod和备选pod的CPU占用量     - 计算出所有备选节点上运行的pod和备选pod的memory占用量     - 根据特定的算法,计算每个节点的得分

四、 Kubelet 运行机制分析

在kubernetes集群中,每个node上都会启动一个kubelet服务进程。该进程用于处理master节点下发到本节点的任务,管理Pod以及Pod中的容器。每个kubelet进程会在API Server上注册节点信息,定期向master节点汇报节点资源的使用情况,并通过cAdvisor监控容器和节点的资源。

1. 节点管理

节点通过设置kubelet的启动参数“--register-node”来决定是否向API Server注册自己。如果该参数为true,那么kubelet将试着通过API Server注册自己。在自注册时,kubelet启动时还包括以下参数:    -api-servers:API Server的位置    --kubeconfing:kubeconfig文件,用于访问API Server的安全配置文件    --cloud-provider:云服务商地址,仅用于共有云环境   如果没有选择自注册模式,用户需要手动去配置node的资源信息,同时告知ndoe上的kubelet API Server的位置。Kubelet在启动时通过API Server注册节点信息,并定时向API Server发送节点新消息,API Server在接受到这些消息之后,将这些信息写入etcd中。通过kubelet的启动参数“--node-status-update-frequency”设置kubelet每个多长时间向API Server报告节点状态,默认为10s。

2. pod管理

kubelet通过以下几种方式获取自身node上所要运行的pod清单:    文件:kubelet启动参数“--config”指定的配置文件目录下的文件(默认为“/etc/Kubernetes/manifests”)通过--file-check-frequency设置检查该文件的时间间隔,默认为20s    HTTP端点:通过“--manifest-url”参数设置。通过“--  API Server:kubelet通过API server监听etcd目录,同步pod列表注意:这里static pod,不是被API Server创建的,而是被kubelet创建,之前文章中提到了静态的pod是在kubelet的配置文件中编写,并且总在kubelet所在node上运行。   Kubelet监听etcd,所有针对pod的操作将会被kubelet监听到。如果是新的绑定到本节点的pod,则按照pod清单的要求创建pod,如果是删除pod,则kubelet通过docker client去删除pod中的容器,并删除该pod。   具体的针对创建和修改pod任务,流程为:     - 为该pod创建一个目录     - 从API Server读取该pod清单     - 为该pod挂载外部volume     - 下载pod用到的secret     - 检查已经运行在节点中的pod,如果该pod没有容器或者Pause容器没有启动,则先停止pod里的所有容器的进程。如果pod中有需要删除的容器,则删除这些容器     - 检查已经运行在节点中的pod,如果该pod没有容器或者Pause容器没有启动,则先停止pod里的所有容器的进程。如果pod中有需要删除的容器,则删除这些容器     - 为pod中的每个容器做如下操作        △ 为容器计算一个hash值,然后用容器的名字去查询docker容器的hash值。若查找到容器,且两者得到hash不同,则停止docker中的容器的进程,并且停止与之关联pause容器的进程;若两个相同,则不做任何处理         △ 如果容器被停止了,且容器没有指定restartPolicy(重启策略),则不做任何处理        △调用docker client 下载容器镜像,调用docker client 运行容器

3. 容器的健康检查

Pod通过两类探针来检查容器的健康状态。一个是livenessProbe探针,用于判断容器是否健康,告诉kubelet一个容器什么时候处于不健康状态,如果livenessProbe探针探测到容器不健康,则kubelet将删除该容器,并根据容器的重启策略做相应的处理;如果一个容器不包含livenessProbe探针,那么kubelet认为livenessProbe探针的返回值永远为“success”。另一个探针为ReadinessProbe,用于判断容器是否启动完成,且准备接受请求。如果ReadinessProbe探针检测到失败,则pod的状态将被修改,endpoint controller将从service的endpoints中删除包含该容器所在pod的IP地址的endpoint条目。   Kubelet定期调用容器中的livenessProbe探针来诊断容器的健康状态。livenessProbe包括以下三种实现方式:    - Execaction:在容器内执行一个命令,如果该命令的退出状态码为0,表示容器健康    - TCPSocketAction:通过容器的IP地址和端口执行一个TCP检查,如果端口能被访问,则表明该容器正常    - TCPSocketAction:通过容器的IP地址和端口执行一个TCP检查,如果端口能被访问,则表明该容器正常具体的配置小编之前的文章中有详细说明: Kube-proxy运行机制分析

1. 概念介绍

2. 后端的pod选择

目前kube-proxy的负载均衡只支持round robin算法。round robin算法按照成员列表逐个选取成员,如果一轮循环结束,便从头开始下一轮循环,如此循环往复。Kube-proxy的负载均衡器在round robin算法得到基础上还支持session保持。如果service在定义中指定了session保持,则kube-proxy接受请求时会从本地内存中查找是否存在来自该请求IP的affinitystate对象,如果存在该对象,且session没有超时,则kube-proxy将请求转向该affinitystate所指向的后端的pod。如果本地存在没有来自该请求IP的affinitystate对象,则按照round robin算法算法为该请求挑选一个endpoint,并创建一个affinitystate对象,记录请求的IP和指向的endpoint。后面请求就会“黏连”到这个创建好的affinitystate对象上,这就实现了客户端IP会话保持的功能。

3. kube-proxy实现细节

kube-proxy通过查询和监听API Server中service与endpoint的变换,为每一个service都建立一个“服务代理对象“,并自动同步。服务代理对相关是kube-proxy程序内部的一种数据结构,它包括一个用于监听此务请求的socketServer, socketServer的端口是随机指定的是本地一个空闲端口。此外,kube-proxy内部也创建了一个负载均衡器—loadBalancer, loadBalancer上保存了service到对应的后端endpoint列表的动态路由转发表,而具体的路由选择则取决于round robin算法和service的session会话保持。   针对发生变化的service列表,kube-proxy会逐个处理,下面是具体的处理流程:   - 如果service没有设置集群IP,这不做任何处理,否则,获取该service的所有端口定义列表   - 逐个读取服务端口定义列表中的端口信息,根据端口名称、service名称和namespace判断本地是否已经存在对应的服务代理对象,如果不存在则创建,如果存在并且service端口被修改过,则先删除Iptables中和该service端口相关的规则,关闭服务代理对象,然后走新建流程并为该service创建相关的Iptables规则   - 更新负载均衡组件中对应service的转发地址列表,对于新建的service,确定转发时的会话保持策略   - 对于已删除的service则进行清理

接下来小编通过一个具体的案例,实际的给大家介绍一下kube-proxy的原理:#首先创建一个service:

apiVersion: v1 kind: Service metadata: labels: name: mysql role: service name: mysql-service spec: ports: - port: 3306 targetPort: 3306 nodePort: 30964 type: NodePort selector: mysql-service: "true"

mysql-service对应的nodePort暴露出来的端口为30964,对应的cluster IP(10.254.162.44)的端口为3306,进一步对应于后端的pod的端口为3306。这里的暴露出来的30964也就是为mysql-service服务创建的代理对象在本地的端口,在ndoe上访问该端口,则会将路由转发到service上。   mysql-service后端代理了两个pod,ip分别是192.168.125.129和192.168.125.131。先来看一下iptables。

[root@localhost ~]# iptables -S -t nat

-A KUBE-NODEPORTS -p tcp -m comment --comment "default/mysql-service:" -m tcp --dport 30964 -j KUBE-MARK-MASQ -A KUBE-NODEPORTS -p tcp -m comment --comment "default/mysql-service:" -m tcp --dport 30964 -j KUBE-SVC-67RL4FN6JRUPOJYM

然后进一步跳转到KUBE-SVC-67RL4FN6JRUPOJYM的链

-A KUBE-SVC-67RL4FN6JRUPOJYM -m comment --comment "default/mysql-service:" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-ID6YWIT3F6WNZ47P -A KUBE-SVC-67RL4FN6JRUPOJYM -m comment --comment "default/mysql-service:" -j KUBE-SEP-IN2YML2VIFH5RO2T

这里利用了iptables的--probability的特性,使连接有50%的概率进入到KUBE-SEP-ID6YWIT3F6WNZ47P链,50%的概率进入到KUBE-SEP-IN2YML2VIFH5RO2T链。

KUBE-SEP-ID6YWIT3F6WNZ47P的链的具体作用就是将请求通过DNAT发送到192.168.125.129的3306端口。

-A KUBE-SEP-ID6YWIT3F6WNZ47P -s 192.168.125.129/32 -m comment --comment "default/mysql-service:" -j KUBE-MARK-MASQ -A KUBE-SEP-ID6YWIT3F6WNZ47P -p tcp -m comment --comment "default/mysql-service:" -m tcp -j DNAT --to-destination 192.168.125.129:3306

同理KUBE-SEP-IN2YML2VIFH5RO2T的作用是通过DNAT发送到192.168.125.131的3306端口。

-A KUBE-SEP-IN2YML2VIFH5RO2T -s 192.168.125.131/32 -m comment --comment "default/mysql-service:" -j KUBE-MARK-MASQ -A KUBE-SEP-IN2YML2VIFH5RO2T -p tcp -m comment --comment "default/mysql-service:" -m tcp -j DNAT --to-destination 192.168.125.131:3306

总的来说就是:在创建service时,如果不指定nodePort则为其创建代理对象时代理对象再本地监听一个随机的空闲端口,如果设置了nodePort则以nodePort为本地代理对象的端口。客户端在访问本地代理对象的端口后此时会根据iptables转发规则,将请求转发到service的clusterIP+port上,然后根据负载均衡策略指定的转发规则,将请求再次转发到后端的endpoint的target Port上,最终访问到具体pod中容器的应用服务,然后将响应返回。

核心机制第二篇,共享存储:https://blog./14048416/2412207

文章内容参考至《kubernetes权威指南》

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

上一篇:史上最大AI模型GPT-3你要开始收费了 接下去可能用不起它了
下一篇:利用Maven添加工程版本信息及时间戳
相关文章

 发表评论

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