• docker&kubernets篇(二十五)


    Kubernetes的整体架构

    Kubernetes由两种节点组成:master节点和工作节点,前者是管理节点,后者是容器运行的节点。其中master节点中主要有3个重要的组件,分别是APIServer、scheduler和controller manager。APIServer组件负责响应用户的管理请求、进行指挥协调等工作;scheduler的作用是将待调度的pod绑定到合适的工作节点上;controller manager是一组控制器的合集,负责控制管理对应的资源,如副本(replication)和工作节点(node)等。工作节点上运行了两个重要组件,分别为kubelet和kube-proxy。前者可以被看作一个管理维护pod运行的agent,后者则负责将service的流量转发到对应的endpoint。在实际生产环境中,不少用户都弃用了kube-proxy,而选择了其他的流量转发组件。

    Kubernetes架构可以用简单描述。可以看到,位于master节点上的APIServer将负责与master节点、工作节点上的各个组件之间的交互,以及集群外用户(例如用户的kubectl命令)与集群的交互,在集群中处于消息收发的中心地位;其他各个组件各司其职,共同完成应用分发、部署与运行的工作。各个组件的详细工作流程我们会在接下来的章节进行细致的描述。

    在这里插入图片描述
    Kubernetes的架构体现了很多分布式系统设计的最佳实践,比如组件之间松耦合,各个组件之间不直接存在依赖关系,而是都通过APIServer进行交互。又比如,作为一个不试图形成技术闭环的项目,Kubernetes只专注于编排调度等工作,而在存储网络等方面留下插件接口,保证了整体的可扩展性和自由度,例如可以注册用户自定义的调度器、资源管理控制插件、网络插件和存储插件等,这使得用户可以在不hack核心代码的前提下,极大地丰富Kubernetes的适用场景。下面我们列出Kuberntest各个重要组件对应的源码位置,方便读者进行查阅。注意,由于版本不同的影响,可能会导致代码位置有所出入,此处以v1.2.0版为主。

    ● master节点
    ❏ APIServer
    ❏ kubernetes/cmd/kube-apiserver/app/server.go
    ❏ kubernetes/pkg/apiserver/
    ❏ kube-scheduler
    ❏ kubernetes/plugin/cmd/kube-scheduler/app/server.go
    ❏ kubernetes/plugin/pkg/scheduler/
    ❏ kube-controller-manager
    ❏ kubernetes/cmd/kube-controller-manager/app/controllermanager.go
    ❏ kubernetes/pkg/controller

    ● 工作节点
    ❏ kubelet
    ❏ kubernetes/cmd/kubelet/app/server.go
    ❏ kubernetes/pkg/kubelet/
    ❏ kube-proxy
    ❏ kubernetes/cmd/kube-proxy/app/server.go
    ❏ kubernetes/pkg/proxy/

    APIServer

    Kubernetes APIServer负责对外提供Kubernetes API服务,它运行在Kubernetes的管理节点——master节点中。作为系统管理指令的统一入口,APIServer担负着统揽全局的重任,任何对资源进行增删改查的操作都要交给APIServer处理后才能提交给etcd。Kubernetes

    APIServer总体上由两个部分组成:HTTP/HTTPS服务和一些功能性插件。其中这些插件又可以分成两类:

    1. 一部分与底层IaaS平台(Cloud Provider)相关,
    2. 另一部分与资源的管理控制(admission control)相关。

    与Cloud Provider相关的插件无非是调用IaaS的API完成对Kubernetes工作节点的操作(如果工作节点是这些IaaS提供的虚拟机)。对于相对比较复杂的admission control插件,后面会专门介绍。为方便起见,下文凡是出现APIServer的地方都可以认为是APIServer的HTTP/HTTPS服务的缩写。

    1. APIServer的职能

    APIserver作为Kubernetes集群的全局掌控者,主要负责以下5个方面的工作。
    ❏ 对外提供基于RESTful的管理接口,支持对Kubernetes的资源对象譬如:pod、service、replication controller、工作节点等进行增、删、改、查和监听操作。例如,GET :/api/v1/pods表示查询默认namespace中所有pod的信息。GET:/api/v1/watch/pods表示监听默认namespace中所有pod的状态变化信息,返回pod的创建、更新和删除事件。该功能在前面的设计讲解中经常提到,这样一个get请求可以保持TCP长连接,持续监听pod的变化事件。
    ❏ 配置Kubernetes的资源对象,并将这些资源对象的期望状态和当前实际存储在etcd中供Kubernetes其他组件读取和分析。值得一提的是,Kubernetes除了etcd之外没有任何持久化节点,这也使得它的部署和升级非常方便
    ❏ 提供可定制的功能性插件(支持用户自定义),完善对集群的管理。例如,调用内部或外部的用户认证与授权机制保证集群安全性,调用admission control插件对集群资源的使用进行管理控制,调用底层IaaS接口创建和管理Kubernetes工作节点等。这一点也很特殊,因为它把对Kubernetes进行功能性定制的自由交给了用户,这与大多数平台级开源项目有很大的不同。
    ❏ 系统日志收集功能,暴露在/logs API。
    ❏ 可视化的API(用Swagger实现,此处不做详细讨论)。

    1. APIServer启动过程

    在接下来所有的组件剖析中,一般都会从这个组件进程的启动过程开始。目的也很简单,首先,启动过程是跟踪该组件功能的一个最有效入口;其次,基本上所有重要的参数和依赖都会在这个启动过程中体现出来,这是后面原理解析的重要基础。APIServer的启动程序读者可以参考cmd/kube-apiserver/apiserver.go的main函数,其启动流程如下所示。(1) 新建APIServer,定义一个APIServer所需的关键信息。首先是组件自身所需信息及其所需的依赖和插件配置,如表8-9所示。
    在这里插入图片描述(2) 接受用户命令行输入,为上述各参数赋值。
    (3) 解析并格式化用户传入的参数,最后填充APIServer结构体的各字段。
    (4) 初始化log配置,包括log输出位置、log等级等。Kubernetes组件使用glog作为日志函数库。值得一提的是,Kubernetes能保证即使APIServer异常崩溃也能够将内存中的log信息保存到磁盘文件中。
    (5) 启动运行一个全新的APIServer。APIServer作为master节点上的一个进程(也可以运行在容器中)通常会监听2个端口对外提供Kubernetes API服务,分别为一个安全端口和一个非安全端口,如图所示。
    在这里插入图片描述本地端口

    APIServer在默认情况下监听本地的8080端口,客户端或者Kubernetes其他组件就只能在master节点上直接使用localhost:8080访问这个本地端口。为了能从master节点外访问该端口,可以安装并使用开启HTTPS模式的nginx监听443端口,然后才将经过nginx认证的用户请求转发给8080端口。

    这个本地端口可以处理读/写请求,但是该端口是一个非安全端口,即默认没有用户认证和授权检查机制。如果需要自定义APIServer监听的非安全端口,在启动时传入insecure-port参数。该端口默认绑定到localhost(127.0.0.1),如果需要自定义绑定的网络接口地址,在启动时传入insecure-bind-address参数或者将其写入配置文件(譬如0.0.0.0表示绑定所有的网络接口地址),当然一般不推荐这样做。

    安全端口

    使用HTTPS访问,默认为6443端口(0表示不启用HTTPS),可以响应读/写请求,同时支持x509安全证书和x509私钥认证。在APIServer启动时分别通过tls-cert-file参数传入证书文件和tls-private-key-file参数传入私有密钥文件。如果启用了HTTPS且APIServer在启动时未被提供以上参数,那么APIServer会自动为该端口绑定的公有APIServerIP地址生成一个自注册的证书文件和密钥并将它们存储在/var/run/kubernetes目录下,分别为:/var/run/kubernetes/apiserver.crt和/var/run/kubernetes/apiserver.key。该端口的请求会根据iptables规则被转发给Kubernetes系统自定义的用于提供Kubernetes读/写API的service,即kubernetes service。

    Kubernetes service和其他普通的service一样,处于Kubernetes自己管理的虚拟网络中,且一般绑定在ServiceClusterIPRange的一个可用的IP地址(默认为10.0.0.1)上,对外暴露443端口,因此在访问时需要通过认证与授权。

    $ kubectl get service -o wide
    NAME 	CLUSTER-IP 	EXTERNAL-IP 	PORT(S) AGE SELECTOR
    kubernetes 	10.0.0.1 	 	443/TCP 1h 
    
    • 1
    • 2
    • 3

    如果需要自定义该端口,在启动时传入secure-port参数,如果需要自定义安全连接绑定的网络接口地址,在APIServer启动时传入bind-address参数,否则APIServer会默认监听所有网卡地址(即0.0.0.0)。APIServer可以使用多种认证授权机制来保障该端口的安全,包括基于token文件的认证机制和基于访问规则的授权机制,

    所有用户请求在执行之前都需要通过资源管理插件admission controller的考验,即根据用户的API请求类型、用户请求上下文所处namespace和申请的资源数量等信息决定到底是通过还是驳回该API请求。如果考验通过,那么APIServer就会对存储在etcd中的REST对象执行实际的增、删、改、查和监听操作了。Admission controller是一类插件的总称,常见的包括Namespace-Lifecycle、LimitRanger、ServiceAccount、ResourceQuota,可以根据不同的需求在启动APIServer时通过传入参数来决定。

    APIServer对etcd的封装

    ❏ GET / 返回类型为resourceName的资源对象列表,例如GET /pods返回一个pod列表。
    ❏ POST / 根据客户端提供的描述资源对象的JSON文件创建一个新的资源对象。
    ❏ GET // 根据一个指定的资源名返回单个资源对象信息,例如GET /pods/first返回一个名为first的pod信息。
    ❏ DELETE // 根据一个指定的资源名删除一个资源对象。
    ❏ POST // 根据客户端提供的描述资源对象的JSON文件创建或更新一个指定名字的资源对象。除了上面提到的通用增、删、改、查操作以外,APIServer还提供了其他一些URL以支持额外的操作,如下所示。

    除了上面提到的通用增、删、改、查操作以外,APIServer还提供了其他一些URL以支持额外的操作,如下所示。
    ❏ GET /watch/ 使用etcd的watch机制,返回指定类型资源对象实时的变化信息。
    ❏ GET /watch// 使用etcd的watch机制,根据客户端提供的描述资源对象的JSON文件,返回一个名为name的资源对象实时的变化信息。

    ● APIServer如何操作资源

    APIServer将集群中的资源都存储在etcd中,默认情况下其路径都由/registry开始,用户可以通过传入etcd-prefix参数来修改该值。当用户向APIServer发起请求之后,APIServer将会借助一个被称为registry的实体来完成对etcd的所有操作,这也是为什么在etcd中,资源的存储路径都是以registry开始的。

    $ etcdctl ls /registry -recursive
    ...
    /registry/pods
    /registry/pods/default
    /registry/pods/default/f54759d0-a51f-11e4-91b1-005056b43972...
    
    • 1
    • 2
    • 3
    • 4
    • 5

    Kubernetes目前支持的资源对象很多
    在这里插入图片描述
    一次创建pod请求的响应流程
    (1) APIServer在接收到用户的请求之后,会根据用户提交的参数值来创建一个运行时的pod对象。
    (2) 根据API请求的上下文和该pod对象的元数据来验证两者的namespace是否匹配,如不匹配则创建pod失败。
    (3) namespace验证匹配后,APIServer会向pod对象注入一些系统元数据,包括创建时间和uid等。如果定义pod时未提供pod的名字,则APIServer会将pod的uid作为pod的名字。
    (4) APIServer接下来会检查pod对象中的必需字段是否为空,只要有一个字段为空,就会抛出异常并终止创建过程。
    (5) 在etcd中持久化该pod对象,将异步调用返回结果封装成restful.Response,完成操作结果反馈。至此,APIServer在pod创建的流程中的任务已经完成,剩余步骤将由Kubernetes其他组件(kube-scheduler和kubelet)通过watch APIServer继续执行下去。

    1. scheduler的数据采集模型

    不同于很多平台级开源项目(比如Cloud Foundry), Kubernetes里并没有消息系统来帮助用户实现各组件间的高效通信,这使得scheduler需要定时地向APIServer获取各种各样它感兴趣的数据,比如已调度、待调度的pod信息,node状态列表、service对象信息等,这会给APIServer造成很大的访问压力。所以scheduler专门为那些感兴趣的资源和数据设置了本地缓存机制,以避免一刻不停的暴力轮询APIServer带来额外的性能开销。这里的缓存机制可以分为两类,一个是简单的cache对象(缓存无序数据,比如当前所有可用的工作节点),另一个是先进先出的队列(缓存有序数据,比如下一个到来的pod)。scheduler使用reflector来监测APIServer端的数据变化。

    所以scheduler专门为那些感兴趣的资源和数据设置了本地缓存机制,以避免一刻不停的暴力轮询APIServer带来额外的性能开销。这里的缓存机制可以分为两类,一个是简单的cache对象(缓存无序数据,比如当前所有可用的工作节点),另一个是先进先出的队列(缓存有序数据,比如下一个到来的pod)。scheduler使用reflector来监测APIServer端的数据变化。最后,我们总结一下scheduler调度器需要的各项数据、如何捕获这些数据,以及这些数据存储在本地缓存的什么数据结构中,

    在这里插入图片描述值得注意的是,scheduler调度器所需的输入数据包括pod和node很好理解,为什么还包含service对象/replication controller对象等其他资源呢?这是因为,这些资源均会在调度pod时起到不同层面上的约束与限制作用,具体在下面的调度策略介绍一节中有更详细的解释。

    1. scheduler调度算法

    在Kubernetes的最早期版本中,scheduler为pod选取工作节点的算法是round robin——即依次从可用的工作节点列表中选取一个工作节点,并将待调度的pod绑定到该工作节点上运行,而不考虑譬如工作节点的资源使用情况、负载均衡等因素。这种调度算法显然不能满足系统对资源利用率的需求,而且极容易引起竞争性资源的冲突,譬如端口,无法适应大规模分布式计算集群可能面临的各种复杂情况。当然,这之后scheduler对调度器的算法框架进行了较大的调整,已经能够支持一定程度的资源发现。目前默认采用的是系统自带的唯一调度算法default,当然,scheduler调度器提供了一个可插拔的算法框架,开发者能够很方便地往scheduler添加各种自定义的调度算法。接下来将以default算法为例,详细解析scheduler调度算法的整体设计。Kubernetes的调度算法都使用如下格式的方法模板来描述:

    func RegisterAlgorithmProvider(name string, predicateKeys, priorityKeys sets.String) string
    
    • 1

    其中,第1个参数即算法名(比如default),第2个和第3个参数组成了一个算法的调度策略。

    Kubernetes中的调度策略分为两个阶段:Predicates和Priorities,其中Predicates回答“能不能”的问题,即能否将pod调度到某个工作节点上运行,而Priorities则在Predicates回答“能”的基础上,通过为候选节点设置优先级来描述“适合的程度有多高”。具体到default算法,目前可用的Predicates包括:PodFitsHostPorts、PodFitsResources、NoDiskConflict、NoVolumeZoneConflict、MatchNodeSelector、HostName、MaxEBSVolumeCount和MaxGCEPDVolumeCount。所以,工作节点能够被选中的前提是需要经历这几个Predicates条件的检验,并且每一条都是硬性标准。一旦通过这些筛选,候选的工作节点就可以进行打分(评优先级)了。

    打分阶段的评分标准(Priorities)有7项:LeastRequestedPriority、BalancedResourceAllocation、SelectorSpreadPriority、NodeAffinityPriority、EqualPriority、ServiceSpreadingPriority和Image-LocalityPriority。每一项都对应一个范围是0~10的分数,0代表最低优先级,10代表最高优先级。除了单项分数,每一项还需要再分配一个权值(weight)。以default算法为例,它包含了LeastRequestedPriority、BalancedResourceAllocation、SelectorSpreadPriority和NodeAffinityPriority这三项,每一项的权值均为1。所以一个工作节点最终的优先级得分是每个Priorities计算得分的加权和,即Sum(score*weight)。最终,scheduler调度器会选择优先级得分最高的那个工作节点作为pod调度的目的地,如果存在多个优先级得分相同的工作节点,则随机选取一个工作节点。

    ● Predicates

    在default算法中,目前可用的Predicates包括:PodFitsHostPorts、PodFitsResources、NoDisk Conflict、NoVolumeZoneConflict、MatchNodeSelector、HostName、MaxEBSVolumeCount和MaxGCEPDVolumeCount。下面我们分别介绍。

    • PodFitsHostPorts
      PodFitsHostPorts的评估依据就是宿主机上的端口是否冲突,即检查待调度的pod中所有容器需要用到的HostPort集与工作节点上已使用的端口是否冲突。需要注意容器内部打开的端口(ContainerPort)和HostPort的区别,在同一个工作节点上,ContainerPort可以随意重复,但HostPort不能冲突。具体检测过程如下所示。

      (1) 枚举待调度的pod要用到的所有HostPort,即查询pod中每个容器的ContainerPort所对应的HostPort。由于HostPort是一个1~65535的整数,这里使用了一个key为int型,value为bool型的map结构,value值为true用于标记某个HostPort需要被该pod使用。
      (2) 根据cache中存储的node相关信息,采用步骤(1)中的方法获得node上运行的所有pod中每个容器的ContainerPort所对应的HostPort。
      (3) 比较步骤(1)和步骤(2)得到的两个HostPort集合是否有交集。如果有交集则表明将pod调度到该工作节点上会产生端口冲突,返回一个false值表示不适合调度;否则表明不会产生端口冲突,返回一个true值表示适合调度。

    • podFitsResources
      podFitsResources的评估依据就是node上的资源是否够用,即检测每个node上已经在运行的所有pod对资源的需求总量与待调度pod对资源的需求量之和是否会超出工作节点的资源容量(node的capacity)。目前,这条规则检查node上允许部署的最大pod数目,以及CPU(milliCPURequested)和Mem(memoryRequested)这两种资源的容量是否满足条件。需要注意的是,对于CPU和Mem, podFitsResources只计算资源的请求量而不是资源的实际使用量。

    • NoDiskConflict
      NoDiskConflict对应的实现函数是NoDiskConflict,它的评估依据就是容器挂载的卷(volume)是否有冲突,注意这里只针对GCEPersistentDisk、Amazon EBS和Ceph RBD类型的volume。不同的volume类型有各自的挂载规则,具体如下。
      ❏ GCE PersistentDdisk允许多次挂载相同的volume,前提是这些挂载卷都是只读的。
      ❏ AWS EBS禁止两个pod共享同一个ID的volume。
      ❏ Ceph RBD不允许两个pod共享一个monitor,以及相同的pool和image。具体的检测过程如下所示

    • NoVolumeZoneConflict

    • MatchNodeSelector
      MatchNodeSelector对应的实现函数是podSelectorMatches,它的评估依据是node是否能被pod的NodeSelector选中以及该node是否符合pod对于NodeAffinity的要求。也就是说,调度器会首先检查工作节点的labels属性和pod的NodeSelector的要求(label selector的一种)是否一致;接下来再检查pod manifest中的scheduler.alpha.kubernetes.io/affinitylabel与node名字是否相吻合。podSelectorMatches工作流程如下所示。
      (1) 如果pod的NodeSelector属性(即pod.Spec.NodeSelector)不为空,则解析工作节点对象的元数据,提取labels属性,应用NodeSelector对工作节点的labels进行匹配,如果匹配不成功,则表明该node不适合调度。
      (2) 获取pod的Spec中scheduler.alpha.kubernetes.io/affinitylabel对应的值NodeAffinity。NodeAffinity是一组亲和性调度规则,目前实现了其中两种,分别为RequiredDuringSchedu-lingIgnoredDuringExecution和PreferredDuringSchedulingIgnoredDuringExecution。其中,仅有前者在这里的检查中使用到了,意指在pod被调度时,选择的node必须符合这一规则的定义。这同样是通过label匹配与否进行判定的。

    • HostName
      HostName评估的依据被定义在PodFitsHost中,即如果待调度的pod指定了pod.Spec.Host的值为hostname,则将它调度到主机名为指定hostname的工作节点上运行,这个策略非常简单。

    • MaxEBSVolume
      CountMaxEBSVolumeCount检查node上即将被挂载的AWS EBS Volume是否超过了默认限制39。

    • MaxGCEPDVolumeCount
      MaxGCEPDVolumeCount检查node上即将被挂载的GCE Persistent Disk是否超过了默认限制16。

    ● Priorities
    在通过了上述硬性筛选之后,顺利过关的工作节点就可以通过打分过程来评优先级了。注意,不同的priorities函数之间,也可能存在权重的区分,但在default调度算法中,这些priorities函数的权重都相同。

    1. LeastRequestedPriority
      LeastRequestedPriority的计算原则是尽量将pod调度到资源占用比较小的工作节点上,这样能够尽可能地实现Kubernetes集群工作节点上pod资源均衡分配。具体计算分数的方法可以用如下公式描述:cpu((capacity - sum(requested)) * 10/ capacity)+ memory((capacity - sum(requested)) * 10 / capacity) / 2其中,requested cpu和requested memory是被调度的pod所需申请的资源总量加上正在被检查的工作节点上所有运行的pod所申请的资源总量,而capacity则是正在检查的工作节点目前可用的容量。
      (1) 对给定的待调度pod,查询该pod所在的namespace下对应的service。由于一个pod对应的service的数目是没有限制的(可能为0个,1个或多个),如果与该pod匹配的service数目不为0,则此处会返回所有匹配的service列表,否则返回错误标识没有找到匹配的service。
      (2) 对给定的待调度pod,查询该pod所在的namespace下对应的ReplicationController。同样地,当匹配的ReplicationController数目不为0时,返回匹配列表,否则返回错误。
      (3) 对给定的待调度pod,查询该pod所在的namespace下对应的ReplicaSet。当匹配的ReplicaSet数目不为0时,返回匹配列表,否则返回错误。
      (4) 对于上述返回的与pod有相同label selector的service、ReplicationController和ReplicaSet,将其整合在一起,并且计算与待调度pod处于namespace下各个node上具有同样label selector的pod数目,并将所有node中相同label的pod数量最多的值记为maxCountByNodeName。
      (5) 同样地,我们再针对zone进行类似的计算,将所有zone中相同label的pod数目最多的值即为maxCountByZone。当然,有一些集群中的工作节点并没有zone这一特征,在这种情况下,无需在后续步骤中考虑zone因素的影响。
      (6) 运用简单的打分策略对各个工作节点进行打分,将该节点上相同label的pod与maxCount-ByNodeName及maxCountByZone进行投射比对,得到一个0~10分间的分数,具体计算过程如下。
      LeastRequestedPriority已保证了pod会被尽量调度到资源充裕的节点上,因此,接下来SelectorSpreadPriority在考虑每台工作节点上pod个数时,就不应该以单纯数量上的均匀分布为考量依据,而应该以拥有相同selector的pod均分分布为标准。这样才能在保证资源利用率的同时照顾到高可用需求。
      2. ServiceSpreadingPriority
      3. NodeAffinityPriority
      NodeAffinityPriority是一个新的特征,允许用户在pod manifest中指定pod的工作节点亲和性,对应的annotation为scheduler.alpha.kubernetes.io/affinity。node亲和性本质上是一些调度规则,目前实现了其中两种,其一为强规则required-DuringSchedulingIgnoredDuringExecution,若某个工作节点不满足该字段的要求,则待调度的pod一定不会被调度到该工作节点上;其二为弱规则preferredDuringSchedulingIgnoredDuring-Execution,即说明pod偏好的工作节点,但是调度器仍然可能将该pod调度到不满足这一字段的工作节点上
      4. EqualPriority
      EqualPriority对应的实现函数是EqualPriority,它的计算原则是平等对待NodeLister中的每一个工作节点。与其他计算函数相比,EqualPriority函数的工作流程简单很多,即遍历NodeLister中所有备选的工作节点,将每个工作节点的优先级(score)均置为1。
      5. ImageLocalityPriority

    scheduler的启动与运行

    下面将从代码的角度带大家感受scheduler是如何精巧地完成它的工作。Kubernetes scheduler启动程序与APIServer启动程序大同小异,因此这里将着重介绍scheduler调度逻辑,省略其他非核心的部分。scheduler组件比较特别的地方是,它启动程序的过程被放在plugin/cmd/kube-scheduler目录下,而非像其他组件那样被放在cmd/{component}下,事实上scheduler是Kubernetes项目的一个Git submodule。

    负责进行调度工作的核心进程成为scheduler server,它的结构相对来说比较简单,主要的属性如表所示。
    在这里插入图片描述在程序入口的main函数中,首先完成对SchedulerServer的初始化工作,这是一个涵盖了要运行调度器所需要的参数的结构体,并且调用Run函数来运行一个真正的调度器。Run函数完成的事情如下。
    (1) 收集scheduler产生的事件信息并构建事件对象,然后向APIServer发送这些对象,最终由APIServer调用etcd客户端接口将这些事件进行持久化。event来源非常广泛,除了scheduler外,它的来源还包括kubelet、pod、Docker容器、Docker镜像、pod Volume和宿主机等。
    (2) 创建一个http server,默认情况下绑定到IP地址Address 上并监听10251端口。在启用对scheduler的profiling功能时,该server上会被注册3条路由规则(/debug/pprof/、/debug/pprof/profile和/debug/pprof/symbol),可以通过Web端对scheduler的运行状态进行辅助性检测和debug。
    (3) 根据配置信息创建调度器并启动SchedulerServer。在启动调度器之前,需要进行一些初始化操作,这些初始化操作的结果将作为调度器的配置信息传入,
    (4) 注册metrics规则,用于检测调度器工作的性能,包括调度延迟时间、binding延迟时间等。上述动作完成后,调度器的主循环就可以自动执行调度工作了。简单地说,就是不停地从缓存待调度pod对象的队列podQueue中弹出一个pod对象。然后将这个待调度pod和所有可用工作节点对象的链表NodeLister被用作调度算法的输入。一旦成功选择一个可用的工作节点,则使用该pod的namespace、pod名、选择的node这3个属性新建一个Binding对象

    multi-scheduler

    实现multi-scheduler的关键在于,如何将pod与其对应的scheduler对应起来。我们知道,一个pod应当被一个且只被一个调度器调度到一个工作节点上,否则就会发生pod运行失败或者调度工作节点冲突等状况。社区目前采用了annotations来实现这一需求。所谓annotations,可以看作一组非结构化的键/值对,在这里我们用到的key值为scheduler.alpha.kubernetes.io/name,用于为用户提供自定义使用调度器的自由。若该字段为空,则系统自动将其指派给默认的调度器进行调度。遗憾的是,这一方案还没有解决用户指定无效调度器(如拼写错误等)的问题,一旦发生这一状况,对应的pod将会一直处于pending的状态。

  • 相关阅读:
    Mac根据端口查询进程id的命令
    什么样的程序化交易程序才能算好程序?
    屏幕不清晰,可能是你的设置不正确
    使用 Tess4J 实现本地与远程图片的文字识别
    ROS:ubuntu 20.04 noetic安装指南(简略版)
    Java基础入门day62
    为什么越来越多的企业在会议室使用无线流媒体网关?
    macOS Sonoma 正式版系统已发布,macos14值得更新吗
    TMMi测试成熟度模型.概念总结
    【C++】堆区空间的申请和释放--- 2024.3.19
  • 原文地址:https://blog.csdn.net/yitian881112/article/details/126564193