kubernetes# 核心组件篇

本文详细解析了Kubernetes的核心组件,包括MasterNode、Etcd、ApiServer、ControllerManager、Scheduler和WorkNode等,阐述了它们各自的功能、工作原理和组件间协作,重点讲解了ApiServer的CRUD接口、访问控制、组件间通信以及资源调度的复杂性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在上文kubernetes基础概念篇中,作者介绍了kubernets内部的一些资源对象,那么本篇就来介绍下这些资源在内部如何工作,本文是在上篇基础上的升华,主要是探究kubernetes是如何工作的?有哪些组件?组件之间又是如何相互协作从而完成各自的使命的?

kubernetes核心组件

我们先来看下Kubernetes整体有哪些组成部分:

核心组件分类

MasterNode

MasterNode包含3个程序,分别是:

  • Etcd

    • 集群的数据库,存储kubernetes各种类型的资源数据,是一个高性能的分布式的

      KV结构的数据库。

  • ApiServer

    • 集群中资源CRUD的统一入口,提供Http Rest服务。

    • 集群内部各个组件通信的渠道。

  • ControllerManager

    • 集群内部Controller的管理器,Controller有很多种类型,每种都处理着单独的逻辑,负责系统的某一部分功能。

  • Scheduler

    • 集群的资源调度器,负责调度功能,如:为Pod找到一个合适的宿主机器。

WorkNode

和MasterNode类似,WorkerNode本质也并不是一个独立的应用程序,它包含两个组件,如下:

  • kubelet

    • Node节点上具体工作的进程

    • Node节点管理

    • Pod和容器的生命周期管理

  • kube-proxy

    • 集群service的访问代理组件,控制service的访问

核心组件详解

上一段我们对kubernetes的核心组件按照所在节点进行了划分,同时介绍了每种组件的所做的事情,这里我们再针对每一个组件,看看他们究竟是如何工作的。

ApiServer

API

ApiServer是集群资源的访问入口,资源的CRUD都是通过该组件来实现,在集群内部起到一个承上启下的作用,"承上"是对外提供Http Rest服务,供外部Client调用,         "启下"是对接etcd数据库,保存资源信息。除了资源的CRUD还有健康检查、UI、日志、性能指标等运维监控相关的API。

访问控制
  • Authentication
    竟然是对外提供的服务,那肯定就少不了安全认证,ApiServer的安全认证采用的CA双向认证,如果对这种认证方式不了解的可以阅读小编的另外一篇:WEB安全认证

  • Admission Cnotrol

    资源访问许可控制层,集群的资源都有指定的配额,比如物理资源的限制,如果超出就会拒绝访问,比如创建Pod系统没有可用的资源

组件通讯渠道

kubernetes内部的组件之间也是需要通信的,kubernetes的核心理念是一个自动化的控制系统,那就需要感知资源的变化的,比如controller感知pod的变化做一些自适应调整,而资源的数据变化是在etcd端,这些都是通过ApiServer这个中间桥梁来连接的。

这里提一个kubernetes里一个很有意思的设计是list-watch机制,试想一下如果你来设计每个组件之间相互访问、资源感知该如何处理,调用HTTP接口?没办法保证可靠性以及性能,kubernetes里的list-watch机制完美地解决了这个问题

  • list:获取资源(Resource)列表,在初始化的时候把资源全量更新到本地缓存中。

  • watch:监听某种Resource的事件,通知注册EventHandler,在事件发生的时候做回调操作。

有经验的同学一眼就可以发现这就是典型的观察者模式,每个组件只需要订阅(watch)自己感兴趣的事件,然后注册回调函数即可。同时ApiServer的这种设计可以让业务组件与存储系统解耦,不直接进行交互,有很好的扩展性。关于list-watch更深入的讲解小编打算专门起一个篇幅,因为这里还涉及到etcd的监听机制,涉及到Go里面的组件使用,这里就不做过多延伸了。

完整的ApiServer分层架构如下:

ControllerManager

我们刚才说了kubernetes是一个自动化的控制系统,这意味着不需要人为的干预,可以自动实现资源的动态伸缩、发布、故障转移等,这些具体的操作在kubernetes中就是一个个的Controller控制器,每个Controller都有它独特的作用,并且只做分内的事情,比如NodeController管理着Node节点,NameSpaceController管理NameSpace空间,kubernetes提供的Controller有很多个,有兴趣的同学可以线下去了解,这里我列举几个常用的控制器。

Node Controller

集群中Node节点的管理,kubelete在初始化的时候会向ApiServer注册节点信息,然后通过watch监听机制转发到NodeController,由该控制器统一管理,如果发现系统中Node故障,NodeController会将该节点删除,同时将该节点的资源转移到其他节点。

Deployment Controller

副本控制器,通过监听集群中Pod的情况,动态地调整Pod,比如创建和删除,保证集群中的Pod数量始终和设置的值保持一致。DeploymentController在运行过程中实际控制这两种资源对象:Deployment和ReplicaSet,每次通过引用deploy模板的时候,会创建一个Deployment对象,同时会创建一个ReplicaSet对象,细心的同学会发现,ReplicaSet的名称的前缀是对应的Deployment,而Pod名称的前缀是对应的ReplicaSet。DeploymentController的主要作用如下:

  • 副本控制器,保证集群中始终有N个Pod示例,N是在RC中定义的Pod副本数量。

  • 动态伸缩,通过spec.replicas属性值来动态扩容或者缩容。

  • 滚动发布,我们可以通过改变Pod的模板(比如修改容器的镜像版本号)来实现Pod的滚动发布。

集群内部的资源自平衡就是通过Deployment Controller来实现的,监听Deployment、ReplicaSet和Pod三种资源对象,当三种资源发生变化时会触发 deployment controller 对相应的deployment资源进行调谐操作,从而完成deployment的扩缩容、暂停恢复、更新、回滚、状态status更新、所属的旧replicaset清理等操作。

ResourceQuota Controller

资源配额控制器,作为完备的企业级的容器集群管理平台,kubernetes提供了资源配额管理这一高级功能,该功能确保指定的资源在任何情况下都不会超额占用系统的物理资源,这样做是为了防止有些业务系统在设计上的缺陷导致占用过多的物理资源,从而影响到整个集群,影响系统的稳定性。kubernetes目前支持三种级别的资源配置控制:

  • 容器级别:可以对物理机器的CPU和Memory进行限制

  • Pod级别:可以对一个Pod内所有容器的可用资源进行限制

  • NameSpace级别:NameSpace级别的资源控制,包括Pod、Service、RC的数量

资源配额管理是通过上面ApiServer的Admission Control(准入控制)来实现的,在做资源的具体创建的时候会先做准入控制,只有通过了才会进行后续步骤

Endpoints Controller

另外一个比较重要的控制器是Endpoints Controller,顾名思义,就是管理集群中的service对应的Endpoints资源对象。在基础篇中我们已经介绍过Endpoint对象,这里我们再来回顾一下,一个Endpoint对象唯一的代表了Pod中资源的访问,是PodIp和容器targetPort的组合。一个service对应N个Pod,每个Pod有M个容器启动(targetPort),这样就会有M*N个Endpoint对象,用一张图来标识Service、Pod、EndPoint的关系如下:

Endpoints Controller负责监听Service和Pod副本的变化,如果监测到Service被删除,则删除和该Service同名的Endpoints对象。如果监测到新的Service被创建或者修改,则根据该Service信息获得相关的Pod列表,然后创建或者更新Service对应的Endpoints对象。如果监测到Pod的事件,则更新它所对应的Service的Endpoints对象(增加、删除或者修改对应的Endpoint条目)。

Sehcduler

集群的资源调度器,是kubernetes负责Pod调度的核心组件,主要是完成Pod的调度,帮助Pod找到合适的宿主机进行绑定。通过上面对控制器组件的介绍,我们知道组件是可以知道系统中Node的资源列表,可能有的人会想,那这样分配不就很简单吗?就和服务的负载均衡类似的方式,采取特定的算法完成调度就可以。但是随着kubernetes在企业大面积的应用,人们对其调度的要求也是越来越高,既需要自动化调配,又想通过自定义的方式去指定,而指定调度方式也是各种各样,这就大大提升了Pod调度的复杂度。Kubernetes Sehcduler在整个调度过程中涉及三个对象,分别是待调度Pod列表、可用Node列表及调度算法和策略,用一张图表示如下:

Kubernetes Sehcduler对Pod的调度主要分为两步:预选(Predicates)和优选

(Priorities)。

预选(Predicates)

初步筛选,输入是Node列表,输出是可以被调度的Node列表,预选主要是针对一些Node的硬件指标,比如物理资源、端口占用等进行筛选,每一个筛选条件封装成一个Filter,可以看做是一个责任链的模式,多个Filter去筛选合适的Node节点

过滤条件包含如下:

  • PodFitsHostPorts:检查Pod容器所需的HostPort是否已被节点上其它容器或服务占用。如果已被占用,则禁止Pod调度到该节点。

  • PodFitsHost:检查Pod指定的NodeName是否匹配当前节点。

  • PodFitsResources:检查节点是否有足够空闲资源(例如CPU和内存)来满足Pod的要求。

  • PodMatchNodeSelector:检查Pod的节点选择器(nodeSelector)是否与节点(Node)的标签匹配

  • NoVolumeZoneConflict:对于给定的某块区域,判断如果在此区域的节点上部署Pod是否存在卷冲突。

  • NoDiskConflict:根据节点请求的卷和已经挂载的卷,评估Pod是否适合该节点。

  • MaxCSIVolumeCount:决定应该附加多少CSI卷,以及该卷是否超过配置的限制。

  • CheckNodeMemoryPressure:如果节点报告内存压力,并且没有配置异常,那么将不会往那里调度Pod。

  • CheckNodePIDPressure:如果节点报告进程id稀缺,并且没有配置异常,那么将不会往那里调度Pod。

  • CheckNodeDiskPressure:如果节点报告存储压力(文件系统已满或接近满),并且没有配置异常,那么将不会往那里调度Pod。

  • CheckNodeCondition:节点可以报告它们有一个完全完整的文件系统,然而网络不可用,或者kubelet没有准备好运行Pods。如果为节点设置了这样的条件,并且没有配置异常,那么将不会往那里调度Pod。

  • PodToleratesNodeTaints:检查Pod的容忍度是否能容忍节点的污点。

  • CheckVolumeBinding:评估Pod是否适合它所请求的容量。这适用于约束和非约束PVC。

如果在predicates(预选)过程中没有合适的节点,那么Pod会一直在pending状态,不断重试调度,直到有节点满足条件。

优选(Priorities)

优先顾名思义是通过优先级进行筛选,这个阶段也叫打分阶段,计算每个节点的积分,积分最高者胜出,作为Pod的绑定目标进行绑定操作。评分条件也有很多,比如资源占用最小,指定Label调度、节点亲和性等等。

Kubernetes包含如下优选评分条件:

  • SelectorSpreadPriority:对于属于同一服务、有状态集或副本集(Service,StatefulSet or ReplicaSet)的Pods,会将Pods尽量分散到不同主机上。

  • InterPodAffinityPriority:策略有podAffinity和podAntiAffinity两种配置方式。简单来说,就说根据Node上运行的Pod的Label来进行调度匹配的规则,匹配的表达式有:In, NotIn, Exists, DoesNotExist,通过该策略,可以更灵活地对Pod进行调度。

  • LeastRequestedPriority:偏向使用较少请求资源的节点。换句话说,放置在节点上的Pod越多,这些Pod使用的资源越多,此策略给出的排名就越低。

  • MostRequestedPriority:偏向具有最多请求资源的节点。这个策略将把计划的Pods放到整个工作负载集所需的最小节点上运行。

  • RequestedToCapacityRatioPriority:使用默认的资源评分函数模型创建基于ResourceAllocationPriority的requestedToCapacity。

  • BalancedResourceAllocation:偏向具有平衡资源使用的节点。

  • NodePreferAvoidPodsPriority:根据节点注释scheduler.alpha.kubernet .io/preferAvoidPods为节点划分优先级。可以使用它来示意两个不同的Pod不应在同一Node上运行。

  • NodeAffinityPriority:根据preferredduringschedulingignoredingexecution中所示的节点关联调度偏好来对节点排序。

  • TaintTolerationPriority:根据节点上无法忍受的污点数量,为所有节点准备优先级列表。此策略将考虑该列表调整节点的排名。

  • ImageLocalityPriority:偏向已经拥有本地缓存Pod容器镜像的节点。

  • ServiceSpreadingPriority:对于给定的服务,此策略旨在确保Service的Pods运行在不同的节点上。总的结果是,Service对单个节点故障变得更有弹性。

  • EqualPriority:赋予所有节点相同的权值1。

  • EvenPodsSpreadPriority:实现择优 pod的拓扑扩展约束。

kubelet

在kubernetest集群里,每个Node上都会启动一个kubelet服务进程。改进程用于处理Master下发到本节点的任务。管理Pod以及Pod中的容器,我们在命令行中执行的一系列命令最终也是该服务进程来执行。每个kubelet进程都会在API Server上注册节点自身信息,定期向Master汇报节点资源使用情况,并通过cAdvisor监控容器和节点资源。kubelet服务进程主要管理着以下信息:

节点管理

在启动的时候kubelet进程会向API Server注册Node信息,所以通过上述的NodeController才可以获取到集群中Node节点信息。kubelet会定时向API Server汇报节点的状态,包括一些资源使用情况,默认汇报间隔时间是10s。

Pod管理

kubelet管理着所在节点的Pod资源信息,负责Pod的CRUD操作。上面在讲述API Server组件的时候说过,组件通过监听的方式来感知对应的操作。kubelet通过监听etcd的Pod事件,范围是当前Node所有的Pod清单,如果发现有新的绑定到本节点的Pod,那么就创建Pod,如果有修改或者删除,也会做相应的Pod修改和删除操作。

容器健康检查

容器作为真实的业务服务所在地,其健康状态需要时刻关注,kubelet监控容器的健康状态是通过Pod提供的两类探针实现的。容器是运行在Pod里的,Pod有两类探针:

  • LivenessProbe探针:用来判断容器是否健康并返回给kubelet,LivenessProbe探针的检测方式有三种:
    1、ExecAction:在容器中执行一个命令,如果命令正常退出,状态码为0,则表明容器健康。

    2、TCPSocketAction:这个类似于心跳检测,通过容器的IP和端口执行一个TCP检查,如果端口能访问,表明容器健康。

    3、HttpGetAction:通过接口来确定,这个也是使用的比较多的一种方式,经常我们在设计web服务的时候会有一个/hs接口。容器通过调用该接口如果返回200则认为容器健康。

  • ReadinessProbe探针:用于判断容器是否启动完成,且准备接受请求,如果ReadinessProbe探针检测容器启动失败,则Pod的状态将被修改,Endpoint Controller将从Service的Endpoint中删除包含该容器所在的Pod的IP地址的Endpoint条目。

LivenessProbe和ReadinessProbe的区别在于在返回容器健康检测失败的时候,前者kubelet会删除容器并根据容器的重启策略来做x相应的处理,而后者kubelet会直接删除对应的容器以Endpoint条目。

容器运行时

容器运行时可以理解对运行容器所执行的一系列操作,如何来包装并启动一个容器。kubelet管理着Pod上的容器生命周期,包括容器的创建和销毁。但是容器的操作代码本身不属于kubernetes范畴,而是由各自的容器引擎自身去实现,比如Docker、

Containerd等。kubelet是通过某种进程间的调用方式比如gRPC接口来实现对容器引擎的控制。竟然是采用外部接入,那么就必须提供规范,让各个容器运行时自己来实现,这个规范就是CRI接口规范,主要定义了两个gRPC接口服务:ImageService和RuntimeService,ImageService提供了从仓库拉取镜像、查看和移除镜像的功能;RuntimeService则负责实现Pod和容器的生命周期管理,以及与容器的交互(端口映射等)。

kube-proxy

kube-proxy是运行在每个Node上的一个服务进程,作为Service的代理负载均衡器,其核心功能是将某个Service的访问请求转发给后端的多个Pod实例上。这里就要说下kubernetes中service代理的三种模式:

用户空间代理

最开始的时候,kube-proxy确实是作为一个TCP/UDP代理存在,类似于HA Proxy,通过ClusterIp访问某个服务的时候,这个流量会先经过机器上的iptables规则转发给kube-proxy进程,根据转发规则找到后端Pod,然后跟Pod建立TCP/UDP连接,再将请求转发到后端的Pod上,这种是已经淘汰的kube-proxy实现方式。

iptables代理

在iptables代理模式下,kube-proxy不再起到数据层面的代理作用,也就是不需要像用户空间代理模式那样,通过kube-proxy选择后端的pod然后建立连接。这种模式下Client向Servie发送的请求会直接通过iptables的NAT机制转发到对应的Pod上,中间不需要转发,Client直接和Pod通讯。而在这种模式下,kube-proxy进程只承担控制层面的功能,即通过API Server的Watch接口实现Servie与Endpoint的变更信息,并更新Node节点上的iptables规则,可以看到这种模式下没有了中间代理,性能肯定是更好。

IPVS代理

ipvs代理模式的整体实现和iptables没有太大区别,之所以衍生出这种模式,是因为iptables的存储限制。iptables规则是一行行的转发记录,从入口的ClusterIp到最终的某一个Pod,从上到下遍历,如果系统中存在的Pod数量过大,那么规则列表数量就会变得无比巨大,访问起来也是很耗时。所以引入了ipvs模式,ipvs是用Hash表做的存储,我们知道Hash表的查询复杂度是O(1),所以这种存储接口可以支撑更大的规则数据量同时性能较好。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值