Kubernetes_核心组件_kubelet_kubelet服务全解析(二)

这篇具有很好参考价值的文章主要介绍了Kubernetes_核心组件_kubelet_kubelet服务全解析(二)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

kubelet 架构

controller-manager包含了一组控制器,它是整个集群的大脑,每个控制器会关注自己所关注的对象,这些控制器里面都会有一个固定的模式,每个控制器都会通过list watcher,去watch它所关注的对象,当这些对象发生变化以后,以事件通知的形式,告知controller,controller会注册这些事件的回调函数,并且将这些事件放在一个队列里面,同时会去启动一堆worker线程,来从队列里面取出这些对象,并且进行相关的处理。

也就是说,当读了任何一个controller manager的代码,可以将同样的思路套到其他的controller里面。在每个节点上面都会去运行一个组件叫做kubelet。

Kubelet架构

Kubernetes_核心组件_kubelet_kubelet服务全解析(二),kubernetes,kubelet,容器

它是承担的很多很多职责的这样一个组件。

Kubernetes_核心组件_kubelet_kubelet服务全解析(二),kubernetes,kubelet,容器

最上层,它会提供一个api层,这个api里面就会有蛮多的职责,比如做自己的探活,还有业务指标如何上报,这些都是通过api,你可以看到它工作在不同的端口上面都有监听器,承担不同的职责。

Kubernetes_核心组件_kubelet_kubelet服务全解析(二),kubernetes,kubelet,容器

中间就是在kubelet里面运行了一个一个的manager,你可以理解为一个一个独立职责的小的管理器。

第一列(从左到右):

  1. probemanager:就是为这个节点上面的pod做探活的这样一个管理器,liveless readyless probe,这些探活由kubelet发起,kubelet的probemanager会去读取当前节点上面所有probe的定义,然后它会去看这些probe有没有定义探活,如果定义了探活的属性,那么probemanager就会按照你定义的interval去做监听。
  2. oomwatcher:kubelet是整个节点的守护神,它是一个代理,它负责所有应用的生命周期,同时它有包含节点当前继续正常工作的职责,所以它由oomwatcher来监听,比如节点出现oom问题,比如某些进程出现了oom的错误,oomwathcer就会通过oom这个事件来获取这些异常,并且上报给kubelet。
  3. GPU manager:就是用来管理GPU等等这些扩展的device,当节点上有GPU卡的时候,GPUmanager就是用来管理这张GPU卡的。
  4. cadvisor:是独立开源的软件,kubelet里面会内嵌cadvisor,它其实基于cgroup技术去获取节点上运行应用的资源状态,整个集群的监控都是由cadvisor收集上来的,并且通过kubelet上报的。

第二列(从左到右):
5. syncLoop: syncLoop使用控制器模式,它要去watch一堆对象,也就是当前节点的pod对象,syncloop做完之后,下面内嵌了一堆的worker,pod worker就是当我的 sync group 接收到pod的变更通知的时候,那么这个 pod worker 就会去干活,也就是去维护pod的生命周期。kubelet 中的 syncLoop,要去监听apiserver去获取当前节点的pod清单,获取pod清单之后,然后获取的pod清单,每个pod都是一个一个的通知事件,然后podworker就会处理这些事件。
6. PodWorker: PodWorker会去调用cri,然后获取当前节点pod对应的容器是不是在启动的状态。如果没有启动,那么他就会去启动这些容器进程,把这个应用拉起来,它就是通过cri的接口。cri接口传统都是通过dockershim去做的,但是随着kubernetes架构不断升级,它废弃了docker-shim这条线,它决定不再支持比较臃肿的dokcer shim,而是通过remote container interface,比如通过containerd,或者cri来支持整个运行时。

第三列(从左到右):
7. diskspacemanager:这个节点的磁盘空间大小,包括每个应用,你上去了临时空间,你是不是超了,由diskspacemanager来管理。
8. statusmanager:用来管理节点的状态的。
9. evictionmanager:kubelet是这个节点的守护神,它会去监听当前节点上所有pod所消耗的资源,比如说内存,内存是不可压缩资源,可压缩资源cpu,cpu是基于分时复用的,当你多个进程去抢cpu的时候,反正我们按照协商好的比例去分cpu时间片,当出现竞争的时候,顶多受损,不会异常退出。但是内存不一样,内存是不可压缩资源,针对内存,kubelet就会做比较激进的动作,eviction,evictionmanager就是承担这个职责的组件。它会去监听当前节点的资源使用情况,如果它发现内存已经达到了一个水位,比如10g内存使用了9g,如果不制止这种情况,那么可能整个节点都会受到影响。evictionmanager就会去监听这个水位,当这个内存达到一定水位的时候,它就会去做驱逐,它会去按照既定策略将低优先级的业务,而且占用内存又比较大的,超出了自己预设值的这些进程驱逐掉,也就是将pod从这个节点删除掉。通过这种极端的方式来保护整个节点。
10. volumemanager:pod启动要挂载磁盘,要挂载存储卷的,这个是由volumemanager去做的。

第四列(从左到右):
11. imagegc:如果我这个节点不断的启动应用,每个应用都需要拉取自己的image,如果我不做一些清理工作,就会导致这个节点的image会越来越多,把磁盘占满,所谓的imagegc就会去扫描一些不怎么活跃的镜像,把这些镜像清除掉。
12. containegc就是你的进程可能会退出,它会有一个exit的container,这个container其实它的文件层是没有清理掉的,所谓的container gc就是会清除这些退出的容器。
13. imagemanager:就是做镜像管理的。
14. certificatesmanager:kubelet有签证书的能力,当我们需要kubelet去管理证书的时候,certificatesmanager就可以起作用。

kubelet 职责

每个节点上都运行一个kubelet服务进程,默认监听10250端口,kubelet的职责包括:

  1. 管理Node(通过kubelet访问apiserver的方式): 每个kubelet进程会在APIServer上注册节点自身信息,定期向 master 节点汇报节点的资源使用情况,并通过 cAdvisor 监控节点和容器的资源。(pod健康状态通过pleg去做上报,节点的资源使用情况通过cadvisor上报)
  2. 管理Pod及Pod中的容器(通过kubelet watch apiserver的方式): 接收并执行master apiserver发来的指令(其实是用watch的机制去监听的,如果这些pod发生了变化,他就会去执行某些操作,具体执行什么样的操作就是管理pod当中的容器)

Node管理(节点管理)

kubelet本身有节点管理的功能,包括注册节点,更新节点状态,所以在启动kubelet的时候,如果加了register-node,那么就会自动的将当前节点注册到apiserver。

节点管理最主要的两个是节点自注册和节点状态更新∶

  1. kubelet 可以通过设置启动参数–register-node 来确定是否向API Server注册自己
  2. 如果 kubelet 没有选择自注册模式,则需要用户自己配置Node 资源信息,同时需要告知 kubelet集群上的APIServer的位置
  3. kubelet在启动时通过APIServer注册节点信息,并定时向APIServer发送节点新消息,APIServer在接收到新消息后,将信息写入etcd。

Pod管理

比节点管理相关更加重要的是pod的管理,因为节点的注册管理是一次性的,然后由kublet不断的上报状态,更重要的是应用的管理,也即是pod。

kubelet如何管理pod?它首先要知道节点上要启动哪些pod,这就涉及到如何获取pod清单,kubelet有几种方式去获取pod清单,第一种就是文件的方式,也就是static pod,扫描到目录下有pod清单文件,就将这些pod启动起来。

staticPodPath: /etc/kubernetes/manifests

第二种方式是通过http endpoint来加载pod,你可以给它一个启动参数叫做manifest-url,这个url是http-endpoint,你把所有pod清单放在那,他也会去读取那个url来加载pod清单。

第三种方式是通过apiserver了,当apiserver启动之后,它自己会去启动apiserver,kubelet连接到apiserver,会去监听apiserver上面所有的pod,当然是和这个节点相关的所有pod,经过调度scheduler之后的所有pod。(kubelet会去watch apiserver,apiserver那边有pod创建出来了,并且和这个节点相关,这个节点上的kubelet就会去工作)

获取Pod清单∶

(1) 文件∶启动参数–config指定的配置目录下的文件(默认/etc/Kubernetes/manifests/)。该文件每20秒重新检查一次(可配置)。
(2) HTTP endpoint(URL)∶启动参数–manifest-url设置。每20秒检查一次这个端点(可配置)。
(3) APIServer∶通过APIServer监听etcd目录,同步Pod清单。
(4) HTTP Server∶ kubelet 侦听 HTTP 请求,并响应简单的 API 以提交新的 Pod 清单。

kubelet管理Pod

Kubernetes_核心组件_kubelet_kubelet服务全解析(二),kubernetes,kubelet,容器

从上到下分为三层,如下:
最上一层:kubelet三个端口
次一层:kubelet获取Pod清单
第三层:kubelet通过CRI接口管理Pod以及里面的容器

下面分层介绍。

kubelet如何管理当前节点上所有Pod

kubelet三个端口

kubelet api 包含三个端口
(1) 10250 端口: 业务api, 上面阐述的节点管理和Pod管理通过 10520 端口的api来实现的
(2) 10250 端口: 只读api
(3) 10248 端口: 健康检查api
Kubernetes_核心组件_kubelet_kubelet服务全解析(二),kubernetes,kubelet,容器

查看 kubelet 三个端口

Kubernetes_核心组件_kubelet_kubelet服务全解析(二),kubernetes,kubelet,容器
测试 健康检查接口 (apiserver和kubelet都有健康检查接口)

Kubernetes_核心组件_kubelet_kubelet服务全解析(二),kubernetes,kubelet,容器

kubelet获取Pod清单

Kubernetes_核心组件_kubelet_kubelet服务全解析(二),kubernetes,kubelet,容器

中间还有一个syncloop和PodWorkder

所谓的syncloop又是控制器模式了,它要去watch一堆对象,也就是当前节点的pod对象,sync loop做完之后,下面内嵌了一堆的worker,pod worker就是当我的sync loop接收到pod的变更通知的时候,那么这个pod worker就会去干活,也就是去维护pod的生命周期。

kubelet要去监听apiserver去获取当前节点的pod清单,获取pod清单之后,然后获取的pod清单,每个pod都是一个一个的通知事件,然后podworker就会处理这些事件。

kubelet通过CRI接口管理Pod以及里面的容器

Kubernetes_核心组件_kubelet_kubelet服务全解析(二),kubernetes,kubelet,容器

kubelet (本质是kubelet中的podworker) 会去调用cri,然后获取当前节点pod对应的容器是不是在启动的状态。如果没有启动,那么他就会去启动这些容器进程,把这个应用拉起来,它就是通过cri的接口。

cri接口传统都是通过dockershim去做的,但是随着kubernetes架构不断升级,它废弃了docker-shim这条线,它决定不再支持比较臃肿的dokcer shim,而是通过remote container interface,比如通过containerd,或者cri来支持整个运行时。

PodWorker的工作细节

PodWorker的工作细节

上面将kubelet三层结构中,包括 kubelet 三个接口、kubelet 获取Pod清单、kubelet 通过 CRI 接口管理Pod以及里面的容器。这里详细讲解一下 kubelet 通过 syncLoop 获取 Pod 清单列表,这个 syncLoop 底层怎么工作的。
Kubernetes_核心组件_kubelet_kubelet服务全解析(二),kubernetes,kubelet,容器

步骤1:kubelet中的synloop监听pod的状态变化
kubelet它本身的synloop,就是去监听pod的状态变化,这个pod的状态变化就是一个一个的pod的update,或者是add事件,接收到这些事件之后,它就会将事件存在updateoption里面,那么不同的worker,在kubelet里面有多个worker,每个worker都会从队列里面获取这些pod变更事件的清单。

事件就是Event,类似于mysqll中的 crud 语句,记录着数据操作,APIServer审计日志会记录所有的事件的

步骤2:针对每一个pod执行sync pod的操作
针对每一个pod,它就会去执行sync pod的操作,sync pod里面最重要的行为就是 compute pod actions,也就是针对这个pod采取什么样的行为,它会去比对当前节点上面的已经在运行的容器进程,然后去判断说,如果pod是新的我就create,如果pod是已经存在的,比如说是delete事件,那么我就应该去删除,如果是一个更新事件,那么我就要去判断运行的容器进程和你当前的pod是不是匹配的,比如它的哈希值变的话,那么我就要重建,这就是 compute pod actions所做的事情。它算完了之后就需要通过cri的接口,去create这些进程,或者kill这些进程。

步骤3:kubelet中的pleg将每一个pod生命周期上报给apiserver
右边还有个组件叫做pleg,它主要是pod lifecycle event的一个汇聚器,就是pod运行完之后,上面左边部分是用来管理pod的生命周期,管理完之后这些pod的状态如何上报,那么就是通过pleg组件去上报的。kubelet在pleg组件里面维护了一个pod cache,它本地的一个缓存,这个pleg会去定期的往container runtime里面去发一个list的操作,去获取当前节点上pod的清单,正在运行的pod清单,那么这里就会把当前所有在运行pod状态在pleg这里汇聚,由pleg 通过pod lifecycle event发回上报给apiserver,apiserver那端就知道这个node节点上面所有pod的状态了。

小结:apiserver将Pod 事件Event发送给kubelet,kubelet将 Pod 生命周期 lifecycle 发送给apiserver
这就是 apiserver 与 kubelet 相互的业务沟通,之前学习证书的时候,apiserver和kubelet需要相互调用,各自保留对方的客户端证书

PLEG组件

PLEG 是 kubelet 用来检查容器 runtime 的健康检查机制。这件事情本来可以由 kubelet 使用 polling 的方式来做。但是 polling 有其成本上的缺陷,所以PLEG 应用而生。PLEG 尝试以一种“中断”的形式,来实现对容器 runtime 的健康检查,虽然实际上,它同时用了 polling 和”中断”两种机制。

Kubernetes_核心组件_kubelet_kubelet服务全解析(二),kubernetes,kubelet,容器

PLEG报错

如果contained runtime不响应了,那么relist就会失败,relist失败,那么这些状态就上报不了,那么最终这个状态无法上报,那么kubernets就会认为你整个节点有问题。所以runtime本身不响应,那么这个节点会变的不正常。

还有就是bug,退出容器的gc没有做好,就节点上出现了几千上万的exit container在那里,pleg再去跟runtime relist 这些container的时候,他就会去遍历所有的已经退出的容器,就会导致时间非常的长,它没有办法及时返回所有容器的清单,没有在它规定的时间内返回,那么整个pleg的操作就超时了,那么就会导致整个节点的状态发生变化。

当集群节点进入 NotReady 状态的时候,我们需要做的第一件事情,肯定是检查运行在节点上的 kubelet 是否正常。在这个问题出现的时候,使用 systemctl 命令查看 kubelet 状态,发现它作为 systemd 管理的一个 daemon,是运行正常的。当我们用 journalctl 查看 kubelet 日志的时候,发现下边的错误。

journalctl -u kubelet.service

journalctl命令是用于查看和管理systemd日志的命令行工具。systemd是Linux系统的系统管理和初始化系统,journalctl命令可以用来查看systemd的日志信息。

Kubernetes_核心组件_kubelet_kubelet服务全解析(二),kubernetes,kubelet,容器

这个报错很清楚的告诉我们,容器 runtime 是不工作的 [container runtime is down],且 PLEG 是不健康的 [PLEG is not healthy]。这里容器 runtime 指的就是 docker daemon。Kubelet 通过直接操作 docker daemon 来控制容器的生命周期。而这里的 PLEG,指的是 pod lifecycle event generator。

基本上看到上边的报错,我们可以确认,容器 runtime 出了问题。在有问题的节点上,通过 docker 命令尝试运行新的容器,命令会没有响应。这说明上边的报错是准确的。​​​​​​​

kubelet创建并启动Pod

Pod启动流程

Kubernetes_核心组件_kubelet_kubelet服务全解析(二),kubernetes,kubelet,容器

高层运行时有两种,一种docker,一种containerd,上图是以containerd来说明,更加轻量。

步骤1:用户去创建pod,那么pod的请求会发给apiserver,apiserver接收到请求之后,会把对象存储到etcd里面 [吻合: apiserver与etcd的关系是apiserver单向调用etcd],存储完成之后,调度器scheduler 就去watch到pod的创建时间,并且完成调度,绑定pod [scheduler完成了将这个新建的Pod放到哪个Node上]。

步骤2:绑定之后,也就是更新,继续去etcd里面做持久化,持久化完成之后,kubelet就watch到已经绑定的pod。[此时开始kubelet管理所在Node上的新建的这个Pod]

kubelet在启动pod的时候,它启动的不是一个容器进程,而是多个容器进程,不仅仅启动了应用进程,同时还启动了pause容器镜像的实例,容器进程启动了之后可以把它和某个网络namespace产生关联关系,把它放在某个网络namespace下面,在这个独立的网络namesapce下面,它可以有独立的网络配置,如果直接将容器进程放到网络namesapce里面会出现什么样的问题呢?

kubelet两个参数
–network-plugin=cni 表示Pod之间使用cni通信
–pod-infra-container-image=k8s.gcr.io/pause:3.4.1 表示Pod使用pause镜像启动一个pause容器,维持Pod生命周期,并且把这个pause容器放在一个network ns里面

你的容器进程可靠吗?会不会有错误,会不会异常退出,oom,空指针,等等,当出现这些问题的时候,容器进程就退出了,退出之后容器进程就和这个网络namespace之间的绑定关系就消失了,如果不做一些措施的话,那是不是每次容器进程退出都需要重新为这个容器进程配置网络,那么这样的效率不会很高,如果容器频繁重启,那么就会导致节点上面多了很多不必要的系统操作。

有什么办法让容器进程退出的时候,网络,存储这些东西都不发生变化,这就是为什么需要pause容器。在容器启动之前,网络就需要就绪,网络是要在容器之前就绪的,这就需要额外的容器进程先启动起来。这个也是pause的功能。

pause容器里面entrypoint其实就是pause,它永远sleep,它是一个不会退出,不消耗资源的一个稳定的进程,那么所有的网络就可以挂载在这个pause上面,当容器应用进程启动的时候,我只需要将应用进程的网络namespace挂载在pause上面就行了。

即使容器出现各种问题重启,没关系,因为网络是挂载在其下面的。所以它就提供了非常稳定的基座。

解释: pause容器里面entrypoint其实就是pause, pause容器永远运行
在Kubernetes中,Pause容器是一个特殊的容器,它被用作Pod中其他容器的父容器。Pause容器不执行任何操作,而只是保持运行状态,以保持Pod的生命周期。当Pod中的所有其他容器退出运行时,Pause容器仍然保持运行状态。
而entrypoint是容器在启动时运行的可执行程序或命令。对于Pause容器来说,entrypoint就是容器启动时运行的空命令(例如/pause),而这个空命令并不会做任何实际的工作。它只是保持容器的运行状态,以确保Pod在存在其他容器运行时保持处于活跃状态。
总之,Pause容器的entrypoint就是一个空命令,它的作用是保持容器的运行状态,以保持Pod的生命周期,并在其他容器退出运行时继续保持Pod的活跃状态。

更加详细的Pod启动过程

复述一下Pod启动:pod启动的时候最先启动pause容器,启动之后containerd就会去调用cni的插件去setup pod,其实它会将ADD的命令告诉cni插件帮我setup网络,cni这边setup网络之后会去将pod信息返回给runtime,然后这个信息会返回给kubelet,那么这个时候其实pod就有IP了,这个时候才会去进行下面的用户应用容器的启动。

可以看到在启动容器的时候,网络是已经就绪的,所以有网络需求就可以满足了,整个启动完之后就会将状态回写到Apiserver里面,那么整个的启动过程就完成了。
Kubernetes_核心组件_kubelet_kubelet服务全解析(二),kubernetes,kubelet,容器

上面是更加详细的Pod启动流程框图,最左边是kubelet 中间是CRI 右边是CNI

kubelet 最重要的功能是 syncPod ,在这个 syncPod 操作之前,包括五个步骤

步骤1. Check Admit:kubelet在sync pod也就是做pod同步的时候,首先也会去做准入,比如它要去启动pod,如果节点资源不够,你非要将nodename设置为该节点,因为和节点产生绑定关系,那么就需要启动这个容器了,启动的时候我要去check一下,也需要去做准入的,要去看你的cpu的需求满足不满足,如果不满足就直接报错了,out of cpu的error,写回到这个pod状态里面。

步骤2. Check network plugin:然后会去监听当前节点上网络插件的情况,如果网络插件不就绪,那么pod是启动不起来的,所以这里也会直接报错。

步骤3. Update cgroup:namspace cgroup这些技术要去启动容器进程的时候,要将容器对应的cgroup文件配置好。

步骤4. Makepoddir:pod需要存储日志,容器的日志需要在主机上有个目录,它会去将数据目录创建起来。

步骤5. Wait For attach and mount:你的pod是需要一些存储,比如configmap轻量级存储只需要将文件下载下来,然后mount到容器当中就行了,如果是更高要求的存储,比如网络存储,那么需要去创建volume,然后和这个节点产生attach关系,然后再mount到容器里面,其实就是等待存储就绪。

问题:CRI CNI CSI,它们之间关系是怎么样的呢?在启动pod的时候谁先启动,谁后启动?
回答:CRI (Container Runtime Interface)、CNI (Container Network Interface) 和 CSI (Container Storage Interface) 是 Kubernetes 中的三个关键组件,分别负责容器运行时、容器网络和容器存储。它们之间的关系是:
CRI:负责与容器运行时(如Docker、Containerd等)进行通信,以创建和管理容器。
CNI:负责为容器提供网络连接,包括为容器分配IP地址、配置网络路由等。
CSI:负责为容器提供持久化存储功能,包括挂载存储卷、快照、克隆等。
在启动Pod时,先启动CRI来创建和管理容器,然后CNI为容器分配网络连接,最后CSI为容器提供持久化存储。因此,启动顺序是先启动CRI,然后启动CNI,最后启动CSI。这样确保容器能够正常运行并连接到网络和存储资源。

上面可以很清晰看到 CSI 容器存储 就在kubelet这部分运行,就是在pod后续加载还没有启动的时候,我就得先去将存储挂载好,并且mount进来。如果这一步不过的话,它会一直卡在这里,接下来才会去做syncpod。

syncpod里面就是来计算sandbox和容器变化,如果sandbox发生变化了,就是已经在运行的容器和我pod本身不匹配了,那就是重建了,这里面其实就是computer pod action的动作,如果这个pod已经不存在了,你的容器还在,那么就需要将容器kill掉。

然后你要启动一个新的容器,那么会去经历启动的动作。之前讲了第一步就是createpodsandbox,它会去生成这些sandbox的config,它有一些mainfest,要将这些配置文件生成出来。然后pod的日志目录要创建好,然后要去调用runtime的sandbox。

Kubernetes_核心组件_kubelet_kubelet服务全解析(二),kubernetes,kubelet,容器

以containerd为例,它的cri本身又是一个grpc的服务,kubelet会去调用grpc服务,这个时候整个请求就转到containerd进程里面,kubelet就要暂时等待它的返回结果了。然后就是 create sandbox container,然后一步一步的往下走,最后由cri去调用cni的接口。

针对这种创建网络的请求,其实就是setup网络的过程,在cni里面是add network这种方法去实现的,上面就是整个pod清单的获取,一直到pod容器进程启动的这样一个过程。至于用户容器启动就不多说了。

kubelet CRI 容器运行时

CRI 容器运行时

kubelet在启动容器进程的时候,要真正的启动这些容器进程,随着版本的迭代,它逐渐将启动过程当中这些标准的行为,抽象为一个一个的接口。它的好处是kubelet就不需要和具体的runtime产生绑定关系了,kubelet自己是一些代码框架,它定义好这些接口,它只需要调用这些接口,由不同的容器运行时提供商来实现这些接口。(让运行时可以适配我的标准,这样可以使用你也可以不使用你,这就是 SPI 机制)

SPI 我定义,我使用,别人实现
API 我定义,我实现,别人使用
对于任何一个程序来说(也包括),最上层提供给别人使用的是自己的API接口,最下层依赖/使用别人的SPI机制

这样我们就可以选择是使用docker还是containerd呢还是kata其他类型的容器?这样就使得kubernetes就不和某一个运行时有具体的强绑定关系,防止被某个厂商锁死。

容器运行时(Container Runtime),运行于Kubernetes(K8s)集群的每个节点中,负责容器的整个生命周期。其中Docker是目前应用最广的。随着容器云的发展,越来越多的容器运行时涌现。为了解决这些容器运行时和Kubernetes的集成问题,在Kubernetes1.5版本中,社区推出了CRI(Container Runtime Interface,容器运行时接口)以支持更多的容器运行时。

Kubernetes_核心组件_kubelet_kubelet服务全解析(二),kubernetes,kubelet,容器

如上图,CRI就是将容器运行时的这些实现标准的接口都抽象出来了,kubelet要去操作任何的容器进程都是通过CRI的接口,然后不同的容器运行时会实现这些CRI接口,最终去操作这些容器进程。

CRI 容器运行时架构

整体流程:kubele调动高层运行时通过 CRI 规范接口,高层运行时调用低层运行时通过 OCI 规范接口

Kubernetes_核心组件_kubelet_kubelet服务全解析(二),kubernetes,kubelet,容器

kubelet是客户端,可以看到在kubelet里面有grpc client,它会去基于grpc框架去调用运行时服务,类似docker containerd这类的服务。它是运行在kubelet之外的,在安装kubernetes的时候,会先去安装docker containerd,安装完之后再去安装kubelet。

这样的话kubelet作为客户端来调用runtime的接口。这些runtime就要按照CRI的规范去实现那些接口。

容器运行时最终的目的是要去启动一个一个的容器进程,所以你可以理解为容器运行时它本身是一个中间层,它向上面对的是kubelet,向下面对的是这些容器进程。所以它分为high-level-runtime和low-level-runtime,high-level-runtime是对外提供的这些grpc服务。由客户端grpc client来调用这些服务,它接受到这些请求需要通过low-level的api去启动和操作这些容器。它(指高层运行时)有点像一层代理,它只是做命令的转发,接受来自客户端的请求,然后再去操作下面的容器进程。它所谓的low-level-runtime,容器的话就是runc。

kubele调动高层运行时通过 CRI 规范接口

Dockershim, containerd和CRI-O都是遵循CRI的容器运行时, 我们称他们为高层级运行时(High-level Runtime)。kubele调动高层运行时通过 CRI 规范接口。

CRI是Kubernetes定义的一组gRPC服务。kubelet作为客户端,基于gRPC框架,通过Socket和容器运行时通信。CRI 包括两类服务∶镜像服务(Image Service)和运行时服务(Runtime Service)。

(1) 镜像服务:提供下载、检查和删除镜像的远程程序调用。
(2) 运行时服务:包含用于管理容器生命周期,以及与容器交互的调用(exec/attach/port-forward)的远程程序调用。

imageservice提供了很多接口,主要是对image相关的操作。
runtimeservice同样通过了很多接口,可以看到对sandbox有很多操作,以及和对用户容器相关的。

Kubernetes_核心组件_kubelet_kubelet服务全解析(二),kubernetes,kubelet,容器

高层运行时调用低层运行时通过 OCI 规范接口

容器运行时最终遵循了oci的一个标准,oci定义了容器相关的行业标准。这个行业标准主要分为了三大类:定义了镜像如何打包、如何解压的这样一个规范、通过镜像如何去运行容器进程这样的一个规范。

OCI(Open Container Initiative,开放容器计划)定义了创建容器的格式和运行时的开源行业标准,包括镜像规范(Image Specification)和运行时规范(Runtime Specification)。

镜像规范定义了OCI镜像的标准。高层级运行时将会下载一个OCI镜像,并把它解压成 OCI运行时文件系统包(filesystem bundle)(还有存储镜像文件到指定的目录下)。

运行时规范则描述了如何从OCI运行时文件系统包运行容器程序,并且定义它的配置、运行环境和生命周期。如何为新容器设置命名空间(namepsaces)和控制组(cgroups),以及挂载根文件系统等等操作,都是在这里定义的。它的一个参考实现是runC。我们称其为低层级运行时(Low-level R untime)。除runC以外,也有很多其他的运行时遵循OCI标准,例如kata-runtime。

CRI 高层运行时和低层运行时

高层运行时和低层运行时

容器运行时是真正管理容器的组件,分为高层运行时和低层运行时。高层运行时由 Docker、containerd或CRI-O 充当, 低层运行时由 runc、kata 或 gVisor 充当。低层运行时 kata 和 gVisor 都处于小规模落地或者实验阶段,其生态成熟度和使用案例都比较欠缺,所以除非有特殊的需求,否则 runc 几乎是必然的选择。因此对于容器运行时的选择上,主要聚焦与高层运行时的选择。

容器运行时包括高层运行时和低层运行时,流程是 kubelet 调用高层运行时,高层运行时调用低层运行时,低层运行时调用具体业务容器。在整个业务流程中,kubelet和具体业务流程是不可选择的,但是高层运行时和低层运行时是可选择的。
高层运行时有 Docker containerd 和 cri-o 三种可以选择,具体选择那种,下面会讨论这三种的优劣,一般是 containerd 和 cri-o 比较好。
低层运行时目前只有 runc 一种可用,那就那没得选,就是 runc。

如何查看当前k8s集群的高层运行时和低层运行时

kubectl get nodes -o jsonpath=‘{range .items[*]}{@.metadata.name}{“\n”}{@.status.nodeInfo.containerRuntimeVersion}{“\n”}{@.status.nodeInfo.containerRuntimeVersion}{end}’

该命令将返回一个包含每个节点名称和容器运行时版本的列表。通常,如果你安装了Docker,那么结果应该显示Docker的版本号。如果你使用其他容器运行时,你将看到相应的版本号。

kubectl get nodes 获取节点的信息。这将返回所有集群中的节点列表。
-o jsonpath 使用 JSON 路径格式化输出结果。 [-o 表示 output, 后面的 jsonpath 表示输出格式]
‘…’ JSON 路径的模板包裹在引号中。其中使用 {} 定义占位符来引用节点属性。
{range .items[*]} 是一个迭代器,用于遍历每个节点的列表。
@.metadata.name 引用节点的名称。
{“\n”} 是一个换行符,用于在每个节点信息之间添加换行。
@.status.nodeInfo.containerRuntimeVersion 引用节点的容器运行时版本。
最后,{end} 表示结束迭代。

Kubernetes_核心组件_kubelet_kubelet服务全解析(二),kubernetes,kubelet,容器

{@.metadata.name}{“\n”} 输出节点名称,这里为 m
{@.status.nodeInfo.containerRuntimeVersion}{“\n”} 输出该节点的容器运行时版本
{@.status.nodeInfo.containerRuntimeVersion}{end} 输出该节点的容器运行时版本

直接使用 kubectl get nodes -o json 也可以看到

Kubernetes_核心组件_kubelet_kubelet服务全解析(二),kubernetes,kubelet,容器

使用 kubectl describe nodes m 也可以看到
Kubernetes_核心组件_kubelet_kubelet服务全解析(二),kubernetes,kubelet,容器

另外,你还可以通过逐个检查每个节点上运行的容器运行时进程来确定。连接到每个节点,并使用以下命令检查运行的容器运行时进程:

ps -ef | grep containerd

这将在节点上查找名为"containerd"的进程。如果存在,则使用Containerd作为高层运行时。同样,你还可以使用其他容器运行时的进程名称进行检查,如Docker(“docker”)或CRI-O(“crio”)等。

Kubernetes_核心组件_kubelet_kubelet服务全解析(二),kubernetes,kubelet,容器
Kubernetes_核心组件_kubelet_kubelet服务全解析(二),kubernetes,kubelet,容器

高层运行时: 从docker到containerd

Docker 内部关于容器化运行时功能的核心组件是 containerd ,后来 containerd 也可以直接和 kubelet 通过 CRI SPI 对接,独立在 k8s 中使用。(即 containerd 可以独立作为一个进程运行,不一定需要放在 docker 进程里面)

相对于 docker 而言,containerd 减少了 docker 所需的处理模板 dockerd 和 docker-shim ,并且对 docker 支持的存储驱动进行了优化,因此在容器的创建爱你、启动、停止、删除,已经对镜像的拉取上,都具有性能上的优势。

架构的简化同时也带来了维护的遍历。当然 docker 也具有很多 containerd 不具有的功能 (因为 containerd 是docker的简化),尽管如此,containerd 目前也基本能够满足容器的众多管理需求,所以将它作为运行时的也越来越多。

Kubernetes_核心组件_kubelet_kubelet服务全解析(二),kubernetes,kubelet,容器

containerd是能够取代docker的,因为docker本身内嵌了containerd的,docker是一个独立的产品,我们可以通过docker的命令去管理自己的网络,管理自己的存储,所以docker大部分臃肿的逻辑都在docker daemon它自身的逻辑上面,这些逻辑处理完之后也是将请求发给自带内嵌的containerd里面去, 这样其实真正执行的逻辑是在containerd里面。

存储,网络是kubernetes和docker之间的竞争,在kubernetes里面用的都是kubernetes管理的这套体系,那么docker的那部分其实是冗余的。不再使用docker自身的daemon了,但是可以用你docker自身所带的containerd的。

三种高层运行时比较

上面说的,高层运行时包括三种,所以有选择的余地,低层运行时只有一种,就是runc,所以只能选它。

Kubernetes_核心组件_kubelet_kubelet服务全解析(二),kubernetes,kubelet,容器
上面是三个常用的运行时,最上面的路径其实是最长的。

(1) 如果是docker运行时,那么是kubelet调用dockeshim,调用docker再去调用containerd,containerd再通过runc的接口去调用底层的容器进程。
(2) 如果是containerd那么就没有docker-shim和docker,直接由kubelet发起命令到containerd,然后containerd到runc。
(3) 如果是crio那么更加轻量级,kubelet调用crio,然后直接调用runc。

越来越简洁。

Docker和Containerd的细节差异

Kubernetes_核心组件_kubelet_kubelet服务全解析(二),kubernetes,kubelet,容器

方式1(图片上半部分):如果是通过kubelet去调用docker的话,这个请求是在kubelet的cri接口调用docker-shim,docker-shim再连接到docker-daemon,docker-daemon里面红圈的这部分才是有用的,也就是和image相关的操作,以及正真去去调用containerd的这部分操作是有用的,上面存储网络,已经docker提供的cli这些都是不需要的,那么这些都是冗余的。

方式2(图片下半部分):如果将docker换掉的话,那么红线外的部分就没有了,那么复杂性就大大降低的,直接使用cri的接口调用containerd,移除了这些不必要的调用转发环节,这样整个系统架构更加简单。

上面就是理解去掉docker的意义,这是一个必要的动作。

Containerd和CRI-O的性能比较

Kubernetes_核心组件_kubelet_kubelet服务全解析(二),kubernetes,kubelet,容器
在选型的时候一个是架构的简单程度,稳定性,还有它的性能。

Kubernetes_核心组件_kubelet_kubelet服务全解析(二),kubernetes,kubelet,容器

小结

问题:kubelet是如何调用容器的?
回答:kubelet 通过基于 SPI 机制,通过 CRI 接口调用 高层运行时,然后高层运行时调用低层运行时,然后低层运行时调用 Container 容器。高层运行时和低层运行时都是逻辑上的概念,需要具体实现的中间件。当前的高层运行时只有 dockershim containerd cri-o 三种选择,低层运行时只有 runC 可以选择。

注意:kubelet是如何和docker打交道的 kubelet和docker的cgroupDriver必须相同

尾声

参考资料:kubelet 架构
kubelet 职责
kubelet 管理 Pod
kubelet 创建并启动 Pod
kubelet CRI 容器运行时文章来源地址https://www.toymoban.com/news/detail-610905.html

到了这里,关于Kubernetes_核心组件_kubelet_kubelet服务全解析(二)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • kubernetes核心组件

    目录 前言 一、Kubernetes的核心组件 1、Master组件 1.1 kube-api server 1.2 Kube-controller-manager 1.3 kube-scheduler 1.4 配置存储中心 — etcd 1.5 主节点工作流程 2、Node 组件 2.1 Kubelet 2.2 Kube-Proxy 2.3 docker 或 rocket 二、Kubernetes核心概念  1、Pod 2、Pod 控制器 2.1 Deployment—无状态应用部署 2.2 Statefu

    2023年04月17日
    浏览(38)
  • Kubernetes(k8s)入门:核心组件详解

    附:集群搭建请移步: Kubernetes(k8s)集群搭建,完整无坑,不需要科学上网~ Controllers官网文档:https://kubernetes.io/docs/concepts/workloads/controllers/ 官网:https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller/ 官网原文:A ReplicationController ensures that a specified number of pod repl

    2024年02月13日
    浏览(50)
  • Kubernetes(K8s)的核心组件简介

    Kubernetes(简称 K8s)是一个开源的,用于自动化部署、扩展和管理容器化应用程序的平台。在这篇文章中,我们将深入研究 Kubernetes 的核心组件及其功能。 一、Master 组件 1. API Server:Kubernetes 的主要管理组件。所有的管理任务都是通过 API Server 进行的。它是 Kubernetes 的前端,

    2024年02月15日
    浏览(42)
  • Kubernetes核心组件之kube-proxy实现原理

    kube-proxy,负责为Service提供集群内部的服务发现和负载均衡。 了解不同网络组件的工作原理有助于正确设计和配置它们,以满足你的应用程序需求。 在Kubernetes网络的背后,有一个在幕后工作的组件。它将你的服务(Services)转化为一些可用的网络规则。这个组件被称为 Kube

    2024年02月03日
    浏览(40)
  • Kubernetes(k8s)核心资源解析:Pod详解

    💖The Begin💖点点关注,收藏不迷路💖 Pod是Kubernetes中最小的调度单元,它可以包含一个或多个容器。Pod中的所有容器共享网络和存储卷,它们一起运行在同一个节点上。Pod提供了一种抽象层,使得容器可以作为一个逻辑单元来管理。 Pod中的容器共享IP地址、端口空间和存储

    2024年04月11日
    浏览(70)
  • Kubernetes 的核心概念:Pod、Service 和 Namespace 解析

    🌷🍁 博主 libin9iOak带您 Go to New World.✨🍁 🦄 个人主页——libin9iOak的博客🎐 🐳 《面试题大全》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 🌊 《IDEA开发秘籍》学会IDEA常用操作,工作效率翻倍~💐 🪁🍁 希望本文能够给您带来一定的帮助🌸文章粗浅,敬

    2024年02月16日
    浏览(40)
  • 【kubernetes组件合集】深入解析Kubernetes组件之一:kubectl

    简介:Kubernetes是当今最受欢迎的容器编排和管理平台之一,而kubectl是Kubernetes的命令行工具,它提供了与Kubernetes集群进行交互的能力。本文将深入探讨kubectl的功能和用法,帮助读者全面了解这个重要的Kubernetes组件。 架构图来源: Cluster Architecture | Kubernetes  kubectl是Kubernete

    2024年04月28日
    浏览(33)
  • 云计算的含义及其基本特征和kubernetes的核心组件及相应用途

    云计算是指能够按照需求,随时随地、便捷高效地从可配置的计算资源共享池中获取网络、服务器、存储、应用及服务等所需资源的模式。 规模大、虚拟化、高可靠性、响应速度快、高可伸缩性、按需服务、托管省心、更安全等。 kubernetes整体架构包括Master、Node以及etcd。 3.

    2024年02月02日
    浏览(37)
  • Kubernetes技术--k8s核心技术Service服务

    1.service概述         Service 是 Kubernetes 最核心概念, 通过创建 Service,可以为一组具有相同功能的容器应用提供一个统一的入口地址,并且将请求负载分发到后端的各个容器应用上 。 2.service存在的意义 -1: 防止pod失联(服务发现) 我们先说一下什么叫pod失联。     -2:

    2024年02月10日
    浏览(44)
  • 【kubernetes】部署kubelet与kube-proxy

    前言 :二进制部署kubernetes集群在企业应用中扮演着非常重要的角色。无论是集群升级,还是证书设置有效期都非常方便,也是从事云原生相关工作从入门到精通不得不迈过的坎。通过本系列文章,你将从虚拟机准备开始,到使用二进制方式从零到一搭建起安全稳定的高可用

    2024年02月10日
    浏览(57)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包