Kubernetes 是如何调度的?

本文深入探讨了Kubernetes的调度机制,包括kube-scheduler如何选择最优节点运行Pod,以及预选和打分策略的详细解释。同时介绍了自定义调度器的方法。

作者 | 阿文,责编 | 郭芮

头图 | 优快云 下载自东方IC

出品 | 优快云(ID:优快云news)

自互联网出现以来 ,云计算的概念已经提出了有 50 年。从1957 年,John McCarthy 将计算机中的分时共享概念设计成了一种工具。从此以后,这个概念的名字经历过数次变化:从“服务中心(service bureau)”到应用服务提供商,到互联网即服务,到云计算,再到软件定义的数据中心。

一直以来基础设施是云计算的基础核心,基础设施服务(Infrastructure as a Service,IaaS)将IT基础设施资源(计算、网络与存储)以一种弹性的服务方式对外提供。近十余年来随着云计算不断发展与落地,云基础设施技术架构也在不断往前演进,从传统虚拟化、基础设施资源管理走向软件定义架构。随着近5年来以容器和微服务为代表的云原生技术兴起,云基础设施架构需要演进以全面拥抱云原生技术。

以容器、容器编排、微服务、服务网格为代表的云原生技术正在日益体现出期在云计算领域的非凡价值,众所周知,容器编排中 Kubernetes 已经成为容器编排领域的事实标准。

kubernetes 可以提供所需的编排和管理功能,以便针对工作负载大规模部署容器。借助 Kubernetes 编排功能,可以快速的构建跨多个容器的应用服务、跨集群调度、扩展这些容器,并长期持续管理这些容器的健康状况。在 Kubernetes 中,调度 是指将 Pod 放置到合适的 Node上,然后对应 Node 上的 Kubelet才能够运行这些 pod。

那么kubernetes 是如何进行调度的呢?我们来一起看下。

Kubernetes  是如何调度的?

kube-scheduler 是 Kubernetes 集群的默认调度器,并且是集群 控制面的一部分。同时 kube-scheduler 在设计上是允许你自己写一个调度组件并替换原有的 kube-scheduler。

对每一个新创建的 Pod 或者是未被调度的 Pod,kube-scheduler 会选择一个最优的 Node 去运行这个 Pod。那么kube-scheduler 是如何选择最优的 Node 呢?

kube-scheduler监听apiserver的/api/pod/,当发现集群中有未得到调度的pod(即PodSpec.NodeName为空)时,会查询集群各node的信息,经过Predicates(过滤)、Priorities(优选器),得到最适合该pod运行的node后,再向apiserver发送请求,将该容器绑定到选中的node上。

Kubernetes Scheduler 提供的调度流程分三步:

  • 过滤, 遍历nodelist,选择出符合要求的候选节点,Kubernetes内置了多种预选规则供用户选择。

  • 打分, 在选择出符合要求的候选节点中,采用优选规则计算出每个节点的积分,最后选择得分最高的。

  • 绑定,  选出其中得分最高的 Node 来运行 Pod。之后,调度器将这个调度决定通知给 kube-apiserver,这个过程叫做绑定。

整个过程,如图所示:

预选的算法可以参考源码predicates.go(https://github.com/kubernetes/kubernetes/blob/master/pkg/scheduler/algorithm/predicates/predicates.go):

const (
    // MatchInterPodAffinityPred defines the name of predicate MatchInterPodAffinity.
    MatchInterPodAffinityPred = "MatchInterPodAffinity"
    // CheckVolumeBindingPred defines the name of predicate CheckVolumeBinding.
    CheckVolumeBindingPred = "CheckVolumeBinding"
    // GeneralPred defines the name of predicate GeneralPredicates.
    GeneralPred = "GeneralPredicates"
    // HostNamePred defines the name of predicate HostName.
    HostNamePred = "HostName"
    // PodFitsHostPortsPred defines the name of predicate PodFitsHostPorts.
    PodFitsHostPortsPred = "PodFitsHostPorts"
    // MatchNodeSelectorPred defines the name of predicate MatchNodeSelector.
    MatchNodeSelectorPred = "MatchNodeSelector"
    // PodFitsResourcesPred defines the name of predicate PodFitsResources.
    PodFitsResourcesPred = "PodFitsResources"
    // NoDiskConflictPred defines the name of predicate NoDiskConflict.
    NoDiskConflictPred = "NoDiskConflict"
    // PodToleratesNodeTaintsPred defines the name of predicate PodToleratesNodeTaints.
    PodToleratesNodeTaintsPred = "PodToleratesNodeTaints"
    // CheckNodeUnschedulablePred defines the name of predicate CheckNodeUnschedulablePredicate.
    CheckNodeUnschedulablePred = "CheckNodeUnschedulable"
    // CheckNodeLabelPresencePred defines the name of predicate CheckNodeLabelPresence.
    CheckNodeLabelPresencePred = "CheckNodeLabelPresence"
    // CheckServiceAffinityPred defines the name of predicate checkServiceAffinity.
    CheckServiceAffinityPred = "CheckServiceAffinity"
    // MaxEBSVolumeCountPred defines the name of predicate MaxEBSVolumeCount.
    // DEPRECATED
    // All cloudprovider specific predicates are deprecated in favour of MaxCSIVolumeCountPred.
    MaxEBSVolumeCountPred = "MaxEBSVolumeCount"
    // MaxGCEPDVolumeCountPred defines the name of predicate MaxGCEPDVolumeCount.
    // DEPRECATED
    // All cloudprovider specific predicates are deprecated in favour of MaxCSIVolumeCountPred.
    MaxGCEPDVolumeCountPred = "MaxGCEPDVolumeCount"
    // MaxAzureDiskVolumeCountPred defines the name of predicate MaxAzureDiskVolumeCount.
    // DEPRECATED
    // All cloudprovider specific predicates are deprecated in favour of MaxCSIVolumeCountPred.
    MaxAzureDiskVolumeCountPred = "MaxAzureDiskVolumeCount"
    // MaxCinderVolumeCountPred defines the name of predicate MaxCinderDiskVolumeCount.
    // DEPRECATED
    // All cloudprovider specific predicates are deprecated in favour of MaxCSIVolumeCountPred.
    MaxCinderVolumeCountPred = "MaxCinderVolumeCount"
    // MaxCSIVolumeCountPred defines the predicate that decides how many CSI volumes should be attached.
    MaxCSIVolumeCountPred = "MaxCSIVolumeCountPred"
    // NoVolumeZoneConflictPred defines the name of predicate NoVolumeZoneConflict.
    NoVolumeZoneConflictPred = "NoVolumeZoneConflict"
    // EvenPodsSpreadPred defines the name of predicate EvenPodsSpread.
    EvenPodsSpreadPred = "EvenPodsSpread"
)

常用的预选策略:

  • PodFitsHostPorts:如果 Pod 中定义了 hostPort 属性,那么需要先检查这个指定端口是否 已经被 Node 上其他服务占用了。

  • PodFitsHost:若 pod 对象拥有 hostname 属性,则检查 Node 名称字符串与此属性是否匹配。

  • PodFitsResources:检查 Node 上是否有足够的资源(如,cpu 和内存)来满足 pod 的资源请求。

  • PodMatchNodeSelector:检查 Node 的 标签 是否能匹配 Pod 属性上 Node 的 标签 值。

  • NoVolumeZoneConflict:检测 pod 请求的 Volumes 在 Node 上是否可用,因为某些存储卷存在区域调度约束。

  • NoDiskConflict:检查 Pod 对象请求的存储卷在 Node 上是否可用,若不存在冲突则通过检查。

  • MaxCSIVolumeCount:检查 Node 上已经挂载的 CSI 存储卷数量是否超过了指定的最大值。

  • CheckNodeMemoryPressure:如果 Node 上报了内存资源压力过大,而且没有配置异常,那么 Pod 将不会被调度到这个 Node 上。

  • CheckNodePIDPressure:如果 Node 上报了 PID 资源压力过大,而且没有配置异常,那么 Pod 将不会被调度到这个 Node 上。

  • CheckNodeDiskPressure:如果 Node 上报了磁盘资源压力过大(文件系统满了或者将近满了), 而且配置异常,那么 Pod 将不会被调度到这个 Node 上。

  • CheckNodeCondition:Node 可以上报其自身的状态,如磁盘、网络不可用,表明 kubelet 未准备好运行 pod。如果 Node 被设置成这种状态,那么 pod 将不会被调度到这个 Node 上。

  • PodToleratesNodeTaints:检查 pod 属性上的 tolerations 能否容忍 Node 的 taints。

  • CheckVolumeBinding:检查 Node 上已经绑定的和未绑定的 PVCs 能否满足 Pod 对象的存储卷需求。

打分策略如下:

  • SelectorSpreadPriority:尽量将归属于同一个 Service、StatefulSet 或 ReplicaSet 的 Pod 资源分散到不同的 Node 上。

  • InterPodAffinityPriority:遍历 Pod 对象的亲和性条目,并将那些能够匹配到给定 Node 的条目的权重相加,结果值越大的 Node 得分越高。

  • LeastRequestedPriority:空闲资源比例越高的 Node 得分越高。换句话说,Node 上的 Pod 越多,并且资源被占用的越多,那么这个 Node 的得分就会越少。

  • MostRequestedPriority:空闲资源比例越低的 Node 得分越高。这个调度策略将会把你所有的工作负载(Pod)调度到尽量少的 Node 上。

  • RequestedToCapacityRatioPriority:为 Node 上每个资源占用比例设定得分值,给资源打分函数在打分时使用。

  • BalancedResourceAllocation:优选那些使得资源利用率更为均衡的节点。

  • NodePreferAvoidPodsPriority:这个策略将根据 Node 的注解信息中是否含有 scheduler.alpha.kubernetes.io/preferAvoidPods 来 计算其优先级。使用这个策略可以将两个不同 Pod 运行在不同的 Node 上。

  • NodeAffinityPriority:基于 Pod 属性中 PreferredDuringSchedulingIgnoredDuringExecution 来进行 Node 亲和性调度。你可以通过这篇文章 Pods 到 Nodes 的分派 来了解到更详细的内容。

  • TaintTolerationPriority:基于 Pod 中对每个 Node 上污点容忍程度进行优先级评估,这个策略能够调整待选 Node 的排名。

  • ImageLocalityPriority:Node 上已经拥有 Pod 需要的 容器镜像 的 Node 会有较高的优先级。

  • ServiceSpreadingPriority:这个调度策略的主要目的是确保将归属于同一个 Service 的 Pod 调度到不同的 Node 上。如果 Node 上 没有归属于同一个 Service 的 Pod,这个策略更倾向于将 Pod 调度到这类 Node 上。最终的目的:即使在一个 Node 宕机之后 Service 也具有很强容灾能力。

  • CalculateAntiAffinityPriorityMap:这个策略主要是用来实现pod反亲和。

  • EqualPriorityMap:将所有的 Node 设置成相同的权重为 1。

自定义调度器

除了 kubernetes 自带的调度器,考虑到实际环境中的各种复杂情况,kubernetes 的调度器采用插件化的形式实现,可以方便用户进行定制或者二次开发,我们可以自定义一个调度器并以插件形式和 kubernetes 进行集成。你也可以编写自己的调度器。通过 spec:schedulername 参数指定调度器的名字,可以为 pod 选择某个调度器进行调度。

kube-scheduler在启动的时候可以通过 --policy-config-file参数来指定调度策略文件,我们可以根据我们自己的需要来组装PredicatesPriority函数。选择不同的过滤函数和优先级函数、控制优先级函数的权重、调整过滤函数的顺序都会影响调度过程。

比如下面的 pod 选择 test-my-scheduler 进行调度,而不是默认的 default-scheduler:

apiVersion: v1
kind: Pod
metadata:
  name: test-scheduler
  labels:
    name: testscheduler-example
spec:
  schedulername: test-my-scheduler
  containers:
  - name: pod-with-second-annotation-container
    image: gcr.io/google_containers/pause:2.0

调度器的编写请参考 kubernetes 默认调度器的实现,最核心的内容就是读取 apiserver 中 pod 的值,根据特定的算法找到合适的 node,然后把调度结果会写到 apiserver。

官方给出的范例:

#!/bin/bash
SERVER='localhost:8001'
while true;
do
    for PODNAME in $(kubectl --server $SERVER get pods -o json | jq '.items[] | select(.spec.schedulerName == "my-scheduler") | select(.spec.nodeName == null) | .metadata.name' | tr -d '"')
;
    do
        NODES=($(kubectl --server $SERVER get nodes -o json | jq '.items[].metadata.name' | tr -d '"'))
        NUMNODES=${#NODES[@]}
        CHOSEN=${NODES[$[ $RANDOM % $NUMNODES ]]}
        curl --header "Content-Type:application/json" --request POST --data '{"apiVersion":"v1", "kind": "Binding", "metadata": {"name": "'$PODNAME'"}, "target": {"apiVersion": "v1", "kind"
: "Node", "name": "'$CHOSEN'"}}' http://$SERVER/api/v1/namespaces/default/pods/$PODNAME/binding/
        echo "Assigned $PODNAME to $CHOSEN"
    done
    sleep 1
done

【END】

更多精彩推荐
 ☞中国 AI 应用元年来了!
☞新基建东风下,开发者这样抓住工业互联网风口!
☞15 岁黑进系统,发挑衅邮件意外获 Offer,不惑之年捐出全部财产,Twitter CEO 太牛了!
☞避坑!使用 Kubernetes 最易犯的 10 个错误
☞必读!53个Python经典面试题详解
☞赠书 | 1月以来 Tether 增发47亿 USDT,美元都去哪儿了?
你点的每个“在看”,我都认真当成了喜欢
<think>好的,用户让我介绍一下Kubernetes是什么,而且希望用中文回答。首先,我需要确定用户对Kubernetes的了解程度。可能用户是刚接触容器技术,或者听说过Kubernetes但不太清楚具体细节。接下来,我应该从基础讲起,解释Kubernetes的核心概念和用途。 Kubernetes是一个容器编排平台,所以需要先简要提一下容器化的背景,比如Docker,这样用户能明白为什么需要编排工具。然后说明Kubernetes的主要功能,比如自动化部署、扩展和管理容器应用。用户可能想知道它能解决什么问题,比如在大规模环境中管理多个容器的复杂性。 还要提到Kubernetes的起源,由Google开发后交给CNCF,这样用户了解它的可靠性和社区支持。然后,核心功能部分需要分点说明,比如自动扩缩、服务发现、滚动更新、自愈能力等,这样结构清晰。 用户可能对架构也感兴趣,所以需要简要介绍Master节点和Worker节点,以及各个组件的作用,比如API Server、Controller Manager、kubelet等。不过这部分可能会比较复杂,所以保持简洁,避免太过技术化。 最后,应用场景和优势部分可以让用户明白Kubernetes适合哪些情况使用,比如微服务、混合云等。同时,强调它的优势,如开源、跨云支持、弹性伸缩,帮助用户评估是否适合他们的需求。 需要注意的是,用户可能没有技术背景,所以要用通俗易懂的语言,避免过多术语。同时,检查有没有拼写错误,确保信息准确,比如Kubernetes的发音和缩写(k8s)是否正确。另外,确认最新的信息,比如当前的维护机构CNCF,确保内容不过时。 总结回答结构:定义、起源、核心功能、架构概述、应用场景、优势。这样用户能逐步了解Kubernetes的全貌,满足不同层次的信息需求。K</think>ubernetes(发音为 **koo-ber-net-eez**,常缩写为 **K8s**)是一个开源的**容器编排平台**,用于自动化部署、扩展和管理容器化(Containerized)应用程序。它最初由 Google 设计并开源,现由 **云原生计算基金会(CNCF)** 维护,是云计算领域最核心的基础设施工具之一。 --- ### **为什么需要 Kubernetes?** 随着容器技术(如 Docker)的普及,开发人员可以轻松将应用打包成独立的容器。但在生产环境中,管理成百上千个容器会面临复杂问题: - **如何保证应用高可用性?** - **如何实现故障自愈?** - **如何管理网络和存储?** Kubernetes 正是为了解决这些问题而诞生,它让大规模容器集群的管理变得高效、可靠。 --- ### **核心功能** 1. **自动化运维** - **自动扩缩容**:根据 CPU、内存等指标动态调整容器数量。 - **自愈能力**:自动重启故障容器、替换异常节点。 - **滚动更新**:逐步更新应用版本,避免服务中断。 2. **服务发现与负载均衡** - 自动为容器分配 IP 地址和 DNS 名称,并通过负载均衡分发流量。 3. **存储管理** - 支持多种存储系统(如本地磁盘、云存储),动态挂载到容器。 4. **配置与密钥管理** - 集中管理敏感信息(如密码、API 密钥),避免硬编码。 5. **跨环境运行** - 可在物理机、虚拟机、公有云、私有云或混合云中部署,实现“一次部署,随处运行”。 --- ### **核心架构** Kubernetes 集群由两类节点组成: 1. **Master 节点(控制平面)** - **API Server**:接收用户操作指令(如创建、删除容器)。 - **Controller Manager**:监控集群状态,确保实际状态与预期一致。 - **Scheduler**:决定将容器调度到哪个节点运行。 - **etcd**:分布式键值数据库,存储集群所有配置和状态。 2. **Worker 节点(工作节点)** - **kubelet**:与 Master 通信,管理本节点的容器。 - **kube-proxy**:处理网络通信和负载均衡。 - **容器运行时**(如 Docker):实际运行容器的软件。 --- ### **典型应用场景** - **微服务架构**:管理大量松散耦合的服务。 - **持续交付(CI/CD)**:自动化测试、构建和部署。 - **混合云/多云部署**:统一管理跨云平台的资源。 - **大数据与机器学习**:弹性扩展计算资源处理任务。 --- ### **优势** - **开源生态丰富**:拥有庞大的工具链和社区支持。 - **标准化**:成为云原生应用的“通用语言”,主流云厂商均提供托管版(如 AWS EKS、Google GKE)。 - **弹性伸缩**:轻松应对流量高峰,优化资源利用率。 --- ### **学习资源** - 官方文档:[kubernetes.io](https://kubernetes.io/) - 实践工具:Minikube(本地单节点集群)、Kind(基于 Docker 的集群) 简单来说,Kubernetes 是现代化应用运维的“大脑”,让开发者专注于业务逻辑,而非底层基础设施的复杂性。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

优快云资讯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值