Kubernetes的调度机制和基于节点的Pod调度

Pod 调度框架

调度机制

基础知识

Kubernetes的调度是指为新创建的Pod挑选一个最佳的运行节点

kube-scheduler是 kubernetes 系统的核心组件之一,主要负责整个集群Pod资源的调度功能

kube-scheduler 负责分配调度 Pod 到集群内的节点上,它 watch 监听 kube-apiserver,查询还未分配

Node 的 Pod (Pod 的 nodeName 字段为空),然后根据调度策略为这些 Pod 分配节点(更新 Pod 的

nodeName 字段)。

kube-scheduler 根据特定的调度算法和策略,将 Pod 调度到最优的工作节点上面去,从而更加合理、

更加充分的利用集群的资源

什么是最优节点

  • 满足Pod资源要求,包括资源限制 Resource limit ,QoS(Guaranteed/Burstable/BestEffort)等,资源配额 Resource quota
  • 满足Pod的特殊关系要求,包括Pod 亲和与反亲和
  • 满足Node限制条件要求,包括Node Selector, 污点和容忍
  • 实现集群资源合理利用

调度队列Scheduling Queue 产生的原因:任务优先级的确定

  • 单个对象:kubectl explain pod.spec.priority, 值越高,优先级越高,默认为0
  • 优先级分类:kubectl explain pod.spec.priorityClassName

调度过程

当Scheduler通过API server 的watch接口监听到新建Pod副本的信息后,它会检查所有符合该Pod要求

的Node列表,开始执行Pod调度逻辑。调度成功后将Pod绑定到目标节点上。

Scheduler在整个系统中承担了承上启下的作用,承上是负责接收创建的新Pod,为安排一个落脚的地

(Node),启下是安置工作完成后,目标Node上的kubelet服务进程接管后继工作,负责Pod生命周期的

后半生。

具体来说,Scheduler的作用是将待调度的Pod安装特定的调度算法和调度策略绑定到集群中的某个合适

的Node上,并将绑定信息传给API server 写入etcd中。

若没有任何一个节点能满足Pod的资源需求,则该Pod将被置于Pending状态,直至出现合适的节点

整个调度过程中涉及三个对象,分别是:待调度的Pod列表,可以运行Pod的Node列表,以及调度算法

和策略。

调度功能

这个过程在实际的生产环境中,需要考虑的问题就有很多

  • 如何保证全部的节点调度的公平性?要知道并不是所有节点资源配置都是一样的
  • 如何保证每个节点都能被分配资源?
  • 集群资源如何能够被高效利用?
  • 集群资源如何才能被最大化使用?
  • 如何保证 Pod 调度的性能和效率?
  • 用户是否可以根据自己的实际需求定制自己的调度策略?

考虑到实际环境中的各种复杂情况,kubernetes 的调度器采用插件化的形式实现,可以方便用户进行定

制或者二次开发,我们可以自定义一个调度器并以插件形式和 kubernetes 进行集成。

调度模型

kubernetes 管理平台自动出现以来,其资源调度策略一直在发展中。

单从资源的调度框架的层面来分析,先后经历了两个调度的阶段:传统调度阶段、新型调度阶段

调度模型

kubernetes的调度框架 经历了两个大的阶段的模型

传统调度模型

最初的调度器基于一套预定义的规则和算法,例如资源请求和节点容量的匹配。

调度决策分为两个阶段:预选(Predicates)阶段和优先级(Priorities)阶段。

预选用于过滤不合适的节点,优先级用于对剩余的节点打分并选择最优节点。

预选策略(predicate)

预选即挑选出可以运行的节点, 过滤掉不合格的节点

遍历nodelist,排除不满足条件的节点,剩余为符合要求的候选节点,k8s内置了多种预选规则供用

户选择。

只要有一个预选算法不满足,该Pod就不能被调度至该节点上

如没有Node符合Predicates策略规则,那该Pod就会被挂起,直到有Node能够满足。

优选策略(priority)

优选即评分高的优先

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

选定(select)

如果最高得分有多个节点,select就会从中随机选择一个节点。

扩展支持

虽然kubernetes 提供了非常多的预选策略函数和优选策略函数,但仍可能需要实现定制更多的功能扩

充,支持两种扩展方案。

  • 多调度器 - 通过自定义调度器的方式,让用户自己来选择使用哪一种调度策略(需要用户深度参与)
  • 扩展接口 - 通过扩展的调度接口,将相关的扩展功能整合到当前的调度策略中(每次都要编译执行)

新型调度模型

Kubernetes 1.18 引入了一个新的调度框架,这标志着调度器架构的重大变化。该框架旨在提高调度器

的可扩展性和灵活性,允许开发者以插件的形式扩展和定制调度行为。

  • 插件机制:新的调度框架引入了插件机制,调度过程被分解成多个可插拔的阶段(如过滤、打分、预绑定等),用户可以编写自定义插件来扩展或替换调度器的默认行为。
  • 调度阶段:调度过程被划分为多个阶段,包括预过滤(Pre-filter)、过滤(Filter)、后过滤(Post-filter)、打分(Score)、绑定(Bind)等。开发者可以针对这些阶段创建自定义插件。

新型的调度框架是在传统调度逻辑的基础上,进行了整合再拆分。

Kubernetes 调度框架主要由以下几个组件组成:

调度队列 (Scheduling Queue): 存放待调度的 Pod。

调度插件 (Scheduling Plugins): 在调度过程中用于筛选和评分节点的插件,包括过滤插件(Filter Plugins)和打分插件(Scoring Plugins)。

调度循环 (Scheduling Cycle): 调度器的运行循环,它从队列中取出 Pod 并通过调度插件处理,直到找到合适的节点。

预选 (Predicate): 决定一个节点是否有资格运行 Pod 的初步检查,包括节点资源、Pod 亲和性/反亲和性、节点选择器、污点和容忍度等。用于过滤掉不符合要求的节点

优选 (Priority): 在通过预选的节点中进一步选择最佳节点的过程,通常使用加权评分进行优先级排序。

调度器的主要步骤包括:

1.预过滤 (Pre-filtering):

在过滤前进行一些初始化工作

例如计算Pod资源需求的总和, 检查 Pod 是否具备必要的资源请求。

  1. 过滤 (Filtering):

计算节点剩余资源是否满足Pod需求

过滤掉不满足 Pod 调度要求的节点

例如资源不足的节点或存在污点但没有相应容忍的节点。

  1. 打分 (Scoring):

依据用户配置的资源分配策略(LeastAllocated,MostAllocated,RequestedCapacityRatio请求资源/

可用资源的百分比)给节点打分

选择得分最高的节点作为调度目标。

打分规则可以基于多种因素,如节点剩余资源、Pod 亲和性等。

  1. 预绑定 (Pre-binding):

在最终确定调度目标之前进行一些检查,如绑定验证或准备工作。

  1. 绑定 (Binding):

将 Pod 绑定到选定的节点。

基本流程和传统模型还是一样的:队列-预选-评分-绑定,将这个基本流程划分为了三个部分:

  • 任务队列 - 接收所有待分配的任务,任务本身会根据优先级合理调整队列的顺序。
  • 调度循环 - 通过扩展接口的方式整合大量的预选机制和评分(优选)机制,同时为了方便扩展预留了备用接口,实现预选和优选功能
  • 绑定循环 - 先通过等待线程后的预绑定PreBind队列,此队列可以防止突发的高优先级任务出现,然后再进行正常的节点任务绑定

多调度器

  • 如果默认的调度器不满足要求,还可以部署自定义的调度器。并且,在整个集群中还可以同时运行多个调度器实例
  • 通过 pod.Spec.schedulerName 来选择使用哪一个调度器(默认使用内置的调度器)。

https://github.com/kubernetes-sigs/descheduler

范例: 默认调度器

kubectl get pod myapp-5bf7bf57cd-ffntl -o yaml|grep

schedulerName

schedulerName: default-scheduler

新型 vs 传统调度

  • 所有的传统预选函数和优选函数,都变成了扩展插件的方式实现了动态的灵活绑定
  • 一切皆代码,所有的配置都可以通过代码的方式来进行实时调整,避免传统的再编译或用户强干预

调度功能

调度函数解析

无论是传统的调度功能还是新型的调度功能,都是基于各种调度的功能函数实现

默认情况下,开启了一部分通用

参数解析参考资料

#预选函数:

https://github.com/kubernetes/kubernetes/blob/release1.17/pkg/scheduler/algorithm/predicates/predicates.go

https://github.com/kubernetes/kubernetes/tree/master/pkg/scheduler/algorithm/predicates

#优选函数:

https://github.com/kubernetes/kubernetes/blob/release1.17/pkg/scheduler/algorithm/priorities/priorities.go

https://github.com/kubernetes/kubernetes/tree/master/pkg/scheduler/algorithm/priorities

常见的预选函数和优选函数

下图中绿色部分为默认的插件,蓝色部分为可选的插件

预选算法

经典调度器的预选算法分类

存储约束条件

名称中带有Disk或Volume的算法

Pod间的特殊关系限制

MatchInterPodAffinity

Pod同Node的限制约束

名称中带有Node的算法

General Predicates

Pod的散置性要求

CheckServiceAffinity

EvenPodsSpread

调度策略

调度策略有一些基于资源限制实现,有一些是基于关系实现调度

调度策略 解析

节点调度 为即将执行的任务,选择合理的节点

Pod调度 为即将执行的任务,分配合理的逻辑关联搭配

污点调度 避免调度到某些节点

拓扑调度 在任务已确定调度的前提下,合理的分配任务,实现资源的高效利用

调度分类

名称 解析

亲和与反亲和 满足条件后,就分配或者远离指定的节点。

硬亲和与软亲和 不会遵循绝对的条件阈值,而是根据实际情况,做出倾向性策略硬亲和会考量预选和优选策略,而软亲和仅考虑优选策略

Pod 调度实现

节点调度

节点调度说明

https://kubernetes.io/zh-cn/docs/concepts/scheduling-eviction/assign-pod-node/

节点调度即将Pod调度到哪个节点

Pod 资源对于节点的调度方式:

pod.spec.nodeName

直接指定目标节点

pod.spec.nodeSelector <mapstringstring>

直接指定节点挑选条件

使用节点选择器过滤节点标签

直接将应用部署到指定的节点

如果有多个条件,节点必须同时符合才会调度,即并且的关系

pods.spec.affinity

亲缘性判定

节点亲和 - 根据任务的配置倾向,选择合适的节点来进行资源的配置

注意:调度策略仅在调度时候有用,一旦调度成功后,后续如果节点状态即使无法满足调度要求,也不会影响正常运行的pod

nodeName 和 nodeSelector

https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/assign-pods-nodes/

nodeName和nodeSelector 将 Pod 分配给指定节点

nodeName 直接指定节点的名称,nodeName 可以忽略节点的污点

如果指定的节点异常,会导致Pod 处于Pending状态

kubectl explain deploy.spec.template.spec.nodeName

kubectl explain pod.spec.nodeName

kubectl explain deployment.spec.template.spec.nodeName

nodeSelector 将Pod调度到有指定label的节点,如果没有指定label的节点,将无法调度,Pod 处于

Pending状态

nodeSelector 如果指定多个label,节点必须同时拥有多个label才会调度

nodeSelector 不能忽略节点的污点

如果指定标签的节点异常,会导致Pod 处于Pending状态

注意: nodeSelector 方式如果已经将Pod调度至某个节点, 即使后续再将此节点的label删除,也不会移除

上面的Pod,即只在调度时有效,调度后将不再关注

kubectl explain pod.spec.nodeSelector

#kubectl explain deployment.spec.template.spec.nodeSelector

范例: 调度Pod应用到指定的节点

定制资源配置文件

复制代码
[root@master1 scheduler]# cat scheduler-pod-nodename.yaml    
apiVersion: apps/v1
kind: Deployment
metadata:
  name: controller-deployment-demo-2
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myapp2
  template:
    metadata:
      labels:
        app: myapp2        #注意:此处labels和selector中label必须相同
    spec:
      nodeName: node1.org   #指定调度到指定的节点名称,可以支持master1.org忽略污点
      containers:
      - name: deployment-2
        image: registry.cn-beijing.aliyuncs.com/wangxiaochun/pod-test:v0.1

#配置解析:nodeName 将应用调度到node1.org节点主机上

#应用资源配置文件

kubectl apply -f scheduler-pod-nodename.yaml

#查看效果

复制代码
root@master1 argocd]# kubectl get pod -o wide                
NAME                                            READY   STATUS    RESTARTS   AGE     IP             NODE        NOMINATED NODE   READINESS GATES
controller-deployment-demo-2-67fdd67478-4x7r9   1/1     Running   0          8m14s   10.244.1.229   node1.org   <none>           <none>

如果节点不可用,k8s会自动对节点添加污点,导致Pod处于pending状态

node1节点不可用

复制代码
[root@master1 scheduler]# cat scheduler-pod-nodename.yaml             
apiVersion: apps/v1
kind: Deployment
metadata:
  name: controller-deployment-demo-2
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myapp2
  template:
    metadata:
      labels:
        app: myapp2        #注意:此处labels和selector中label必须相同
    spec:
      nodeName: master1.org   #指定调度到指定的节点名称,可以支持master1.org忽略污点
      containers:
      - name: deployment-2
        image: registry.cn-beijing.aliyuncs.com/wangxiaochun/pod-test:v0.1

kubectl apply -f scheduler-pod-nodename.yaml

复制代码
[root@master1 scheduler]# 
[root@master1 scheduler]# kubectl get pod -o wide
NAME                                            READY   STATUS    RESTARTS   AGE   IP            NODE          NOMINATED NODE   READINESS GATES
controller-deployment-demo-2-5984594788-xr4pw   1/1     Running   0          16s   10.244.0.40   master1.org   <none>           <none>

kubectl delete -f scheduler-pod-nodename.yaml

范例: 使用nodeSelector调度应用到特定的节点上

复制代码
[root@master1 scheduler]# cat scheduler-pod-nodeselector.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: controller-deployment-demo-2
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myapp2
  template:
    metadata:
      labels:
        app: myapp2        #注意:此处labels和selector中label必须相同
    spec:
      nodeSelector:
        node: gpu
      containers:
      - name: deployment-2
        image: registry.cn-beijing.aliyuncs.com/wangxiaochun/pod-test:v0.1

#配置解析:将应用调度到包含node标签,并且值为gpu的节点主机上

#应用资源配置文件

kubectl apply -f scheduler-pod-nodeselector.yaml

#查看效果

kubectl get node --show-labels | grep gpu

#结果显示:由于没有适配标签的节点,当前的pod状态是 pending

复制代码
[root@master1 scheduler]# kubectl get pod 
NAME                                            READY   STATUS    RESTARTS   AGE
controller-deployment-demo-2-794898fdf4-j8q7b   0/1     Pending   0          37s

#查看原因

kubectl describe pod controller-deployment-demo-2-794898fdf4-j8q7b

复制代码
  Warning  FailedScheduling  64s   default-scheduler  0/4 nodes are available: 1 node(s) had untolerated taint(s), 3 node(s) didn't match Pod's node affinity/selector. no new claims to deallocate, preemption: 0/4 nodes are available: 4 Preemption is not helpful for scheduling.

#为node1节点添加标签

kubectl label node node1.org node=gpu

复制代码
[root@master1 scheduler]# kubectl get pod 
NAME                                            READY   STATUS    RESTARTS   AGE
controller-deployment-demo-2-794898fdf4-j8q7b   1/1     Running   0          2m24s

#结果显示:一旦有节点满足需求,pod就被调度到指定的node节点上

#为node1节点清除标签

kubectl label node node1.org node-

#Pod仍然还在节点上,说明nodeSelector仅在调度前有效

复制代码
root@master1 scheduler]# kubectl get pod 
NAME                                            READY   STATUS    RESTARTS   AGE
controller-deployment-demo-2-794898fdf4-j8q7b   1/1     Running   0          3m38s
[root@master1 scheduler]# 

#清理环境

节点亲和性 node affinity

https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/assign-pods-nodes-using-node-affinity/

节点调度支持亲和性调度,表示Pod和节点的亲和性,即将Pod调度至具有某些标签的节点

节点亲和性nodeAffinity 和 nodeSelector 功能相似,但支持更丰富条件,相当于增强版的nodeSelector

节点亲和性只针对调度期间Pod有效,调度后条件发生变化,则无效

节点亲和性nodeAffinity不能忽略节点的污点

注意: Kubernetes中并没有反亲和的概念,但使用污点技术可以实现类似功能

节点亲和分为软亲和和硬亲和

kubectl explain pod.spec.affinity.nodeAffinity

节点软亲和

#倾向性,可以违反,打分逻辑,进行排序,属于优先机制

preferredDuringSchedulingIgnoredDuringExecution

节点硬亲和

#强制,不能违反,定义明确条件:必须满足,或必须不能满足,属于预选机制

requiredDuringSchedulingIgnoredDuringExecution

节点硬亲和

硬亲和是强制条件,如果不满足,将无法启动pod

节点硬亲和属性解析

属性解析

kubectl explain pod.spec.affinity.nodeAffinity

kubectl explain deployment.spec.template.spec.affinity.nodeAffinity

kubectl explain daemonset.spec.template.spec.affinity.nodeAffinity

复制代码
requiredDuringSchedulingIgnoredDuringExecution #硬亲和性,必须满足亲和性才能部署或不部
署。
 nodeSelectorTerms:   #节点选择术语
    - matchExpressions  #匹配表达式,可用如下多种运算符,如果有多个matchExpressions,只要有
一个满足即可调度,即or关系
       key
       value
       operator
        
     operator有以下几种
     In #label 的值在某个列表中存在即可,即只要匹配其中一个值即可,即or关系
     NotIn #label 的值不在某个列表中,可以实现反亲和功能
     Gt #label 的值大于某个值,只支持数据字比较
     Lt #label 的值小于某个值,只支持数据字比较
     Exists #某个 label 存在
     DoesNotExist #某个 label 不存在,可以实现反亲和
      
     matchFields       #匹配字段,可以不定义标签值
            
requiredDuringSchedulingIgnoredDuringExecution中的IgnoredDuringExecution #表示在
Pod运行期间,节点的标签发生变化,导致亲和策略不能满足时,仍会继续运行Pod
#如果一个nodeSelectorTerms中只定义了一个matchExpressions中指定多个key来匹配多个条件,则所
有的key对应的条件都必须满足才会调度到对应的节点,即and的关系
#如果一个nodeSelectorTerms中定义了多个matchExpressions列表指定了多个条件,则只要满足其中一
个matchExpressions条件,就会被调度到相应的节点上,即or的关系

节点硬亲和案例

范例: 只有节点包含env标签值是dev 或者 test 才允许部署pod

复制代码
[root@master1 scheduler]# cat scheduler-pod-node-required-affinity.yaml                                               
apiVersion: apps/v1
kind: Deployment
metadata:
  name: controller-deployment-demo-2
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myapp2
  template:
    metadata:
      labels:
        app: myapp2        #注意:此处labels和selector中label必须相同
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: env
                operator: In
                values:
                - dev              #多个值中,只要有一个值匹配即可,即或者关系
                - test
      containers:
      - name: deployment-2
        image: registry.cn-beijing.aliyuncs.com/wangxiaochun/pod-test:v0.1
[root@master1 scheduler]# 

#应用资源定义文件

kubectl apply -f scheduler-pod-node-required-affinity.yaml

#查看效果,如果没有满足硬亲和条件的节点,Pod处于Pending状态

复制代码
root@master1 scheduler]# kubectl get pod 
NAME                                            READY   STATUS    RESTARTS   AGE
controller-deployment-demo-2-549df5db5d-m2nbd   0/1     Pending   0          4s

#任一节点添加标签

kubectl label node node1.org env=test

#验证结果

复制代码
root@master1 scheduler]# kubectl get pod -o wide 
NAME                                            READY   STATUS    RESTARTS   AGE   IP            NODE        NOMINATED NODE   READINESS GATES
controller-deployment-demo-2-549df5db5d-m2nbd   1/1     Running   0          49s   10.244.1.35   node1.org   <none>           <none>
[root@master1 scheduler]# 

#结果显示:只有节点上存在符合标签后,该资源才会被创建到指定节点

#清理环境

kubectl label node node1.org env-

kubectl delete -f scheduler-pod-node-required-affinity.yaml

节点软亲和

软亲和不是必要的强制条件,只是一种偏好而已, 尽量部署在满足条件的节点上

如果实在所有节点都不满足软亲和的条件,也可以支持调度运行pod

节点软亲和属性解析

属性解析

kubectl explain pod.spec.affinity.nodeAffinity

kubectl explain deploy.spec.template.spec.affinity.nodeAffinity

复制代码
preferredDuringSchedulingIgnoredDuringExecution #软亲和性,能满足最好,不满足也没关系。
  - weight       #权重值,1-100,值越高优先级越高
   preference   #偏好设置
     matchExpressions
     matchFields
  
#对于满足所有调度要求的每个节点,调度程序将通过迭代此字段的元素计算总和
#并在节点与对应的节点匹配时将"权重"添加到总和。
#注意:与硬亲和属性不同,这里需要注意的是软亲和属性是一个列表对象,而preference不是一个列表项。

节点软亲和案例

范例: 只要节点包含env标签值是dev 或者 test就优先部署pod

#资源文件内容

复制代码
[root@master1 scheduler]# cat scheduler-pod-node-preferred-affinity.yaml             
apiVersion: apps/v1
kind: Deployment
metadata:
  name: controller-deployment-demo-2
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myapp2
  template:
    metadata:
      labels:
        app: myapp2        #注意:此处labels和selector中label必须相同
    spec:
      affinity:
        nodeAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 50
            preference:
              matchExpressions:
              - key: env
                operator: In
                values:
                - test
          - weight: 20
            preference:
              matchExpressions:
              - key: env
                operator: In
                values:
                - dev
      containers:
      - name: deployment-2
        image: registry.cn-beijing.aliyuncs.com/wangxiaochun/pod-test:v0.1

#注意:软亲和的权重配置是与硬亲和的区别所在。

#准备节点标签

kubectl label node node1.org env=dev

kubectl label node node2.org env=test

kubectl get nodes -l env

复制代码
root@master1 scheduler]# kubectl get nodes -l env
NAME        STATUS     ROLES    AGE   VERSION
node1.org   Ready      <none>   17d   v1.35.6
node2.org   NotReady   <none>   17d   v1.35.6

kubectl apply -f scheduler-pod-node-preferred-affinity.yaml

#观察结果

复制代码
[root@master1 scheduler]# kubectl get pod -o wide
NAME                                          READY   STATUS    RESTARTS   AGE   IP            NODE        NOMINATED NODE   READINESS GATES
controller-deployment-demo-2-9fd995f9-tvkqc   1/1     Running   0          15s   10.244.1.36   node1.org   <none>           <none>
[root@master1 scheduler]# 

#结果显示:由于 node2上的标签权重较高,所以就交给了node2

#清除node2节点标签后,重启资源对象

kubectl label node node2.org env-

#清理环境

kubectl delete -f scheduler-pod-node-preferred-affinity.yaml

#再次创建pod

kubectl apply -f scheduler-pod-node-preferred-affinity.yaml

#观察结果

复制代码
[root@master1 scheduler]# kubectl get pod -o wide
NAME                                          READY   STATUS    RESTARTS   AGE   IP            NODE        NOMINATED NODE   READINESS GATES
controller-deployment-demo-2-9fd995f9-r6mh6   1/1     Running   0          17s   10.244.1.37   node1.org   <none>           <none>

#结果显示:这个时候,资源调度到了node1上

#清理环境

kubectl label node node2.org env-

kubectl label node node1.org env-

kubectl delete -f scheduler-pod-node-preferred-affinity.yaml

亲和性和资源限制

介绍

默认的的调度策略分为预选和优选

资源限制属于预选,是强制性的,一旦满足不了,就不再继续,而优选,仅仅是选择一个最好的,而非

强制,前提是预选必须通过

比如:即使亲和性的标签能满足需求,如果资源的需求不满足的话,仍然不会去调度资源。

范例

#注意:软亲和的权重配置是与硬亲和的区别所在。

#应用资源定义文件

复制代码
[root@master1 scheduler]# cat  scheduler-pod.yaml               
apiVersion: apps/v1
kind: Deployment
metadata:
  name: node-resourcefits-affinity
spec:
  replicas: 2
  selector:
    matchLabels:
      app: podtest
  template:
    metadata:
      labels:
        app: podtest
    spec:
      containers:
      - name: podtest
        image: registry.cn-beijing.aliyuncs.com/wangxiaochun/pod-test:v0.1
        imagePullPolicy: IfNotPresent
        resources:
          requests:
            cpu: 2
            memory: 2Gi
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: env
                operator: Exists

#应用资源定义文件

kubectl apply -f scheduler-pod.yaml

#查看效果

复制代码
[root@master1 scheduler]# kubectl get pod -o wide
NAME                                          READY   STATUS    RESTARTS   AGE   IP       NODE     NOMINATED NODE   READINESS GATES
node-resourcefits-affinity-7bd99c776f-2vfk8   0/1     Pending   0          12s   <none>   <none>   <none>           <none>
node-resourcefits-affinity-7bd99c776f-qkpbd   0/1     Pending   0          12s   <none>   <none>   <none>           <none>
[root@master1 scheduler]# 

#查看原因

kubectl describe pod node-resourcefits-affinity-7bd99c776f-qkpbd

复制代码
Events:
  Type     Reason            Age   From               Message
  ----     ------            ----  ----               -------
  Warning  FailedScheduling  59s   default-scheduler  0/4 nodes are available: 1 node(s) had untolerated taint(s), 3 node(s) didn't match Pod's node affinity/selector. no new claims to deallocate, preemption: 0/4 nodes are available: 4 Preemption is not helpful for scheduling.

#添加label

kubectl delete -f scheduler-pod.yaml

kubectl label node node2.org env=test

kubectl label node node1.org env=dev

#重新部署

kubectl apply -f scheduler-pod.yaml

#仍然不成功

复制代码
[root@master1 scheduler]# kubectl get pod -o wide             
NAME                                          READY   STATUS    RESTARTS   AGE   IP       NODE     NOMINATED NODE   READINESS GATES
node-resourcefits-affinity-7bd99c776f-2tldt   0/1     Pending   0          2s    <none>   <none>   <none>           <none>
node-resourcefits-affinity-7bd99c776f-jrx47   0/1     Pending   0          2s    <none>   <none>   <none>           <none>

#查看原因是资源不足

kubectl describe pod node-resourcefits-affinity-7bd99c776f-jrx47

复制代码
  Warning  FailedScheduling  68s   default-scheduler  0/4 nodes are available: 1 node(s) didn't match Pod's node affinity/selector, 1 node(s) had untolerated taint(s), 2 Insufficient cpu, 2 Insufficient memory. no new claims to deallocate, preemption: 0/4 nodes are available: 1 No preemption victims found for incoming pod, 3 Preemption is not helpful for scheduling.

#结果显示:节点资源不足,所以无法正常的资源调度

#查看主机资源

kubectl describe node node2.org

#调配资源限制

复制代码
[root@master1 scheduler]# cat  scheduler-pod.yaml                 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: node-resourcefits-affinity
spec:
  replicas: 2
  selector:
    matchLabels:
      app: podtest
  template:
    metadata:
      labels:
        app: podtest
    spec:
      containers:
      - name: podtest
        image: registry.cn-beijing.aliyuncs.com/wangxiaochun/pod-test:v0.1
        imagePullPolicy: IfNotPresent
        resources:
          requests:
            cpu: 0.2
            memory: 200Mi
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: env
                operator: Exists
[root@master1 scheduler]# 

#重新应用资源配置

kubectl apply -f scheduler-pod.yaml

#查看效果

kubectl get pod -o wide

复制代码
[root@master1 scheduler]# kubectl get pod -o wide
NAME                                          READY   STATUS    RESTARTS   AGE   IP             NODE        NOMINATED NODE   READINESS GATES
node-resourcefits-affinity-5c94bc867c-6q4cg   1/1     Running   0          8s    10.244.3.169   node2.org   <none>           <none>
node-resourcefits-affinity-5c94bc867c-8r66l   1/1     Running   0          7s    10.244.3.168   node2.org   <none>           <none>

kubectl label node node2.org env-

kubectl label node node1.org env-

kubectl delete -f scheduler-pod.yaml

Pod 调度

所谓的pod调度,指 pod 彼此之间的亲和性和反亲和性,也就是说,哪些pod应该在一起或者不在同一

个位置(拓扑域)。

是否在同一个位置是通过拓扑域来定义

拓扑域

在Kubernetes中,拓扑域是指按节点的标签定义和分组的节点集合

例如,区域、可用区、机架、主机名都可以作为将节点划分成不同拓扑域的依据

Kubernetes使用TopologyKey(拓扑键)来定义将节点划分至不同拓扑域的标准

topologyKey 的值决定是否是同一个位置

Pod 亲和性 podAffinity

Pod 亲和性说明

Pod 亲和性就是将多个pod调度在同一个位置(拓扑域)上

比如:kubernetes 集群的节点分布在不同的区域或者不同的机房,当服务A和服务B需要高效的交流数

据,要求部署在同一个区域或者同一机房,甚至同一个节点

Pod 亲和性允许跨 namespace 的Pod 间实现亲和

Pod 亲和性分为: 硬亲和和软亲和

属性解析

#亲和:设定一个Pod是倾向与其它Pod运行同一个节点上

kubectl explain deployment.spec.template.spec.affinity.podAffinity

kubectl explain pod.spec.affinity.podAffinity

#反亲和:设定一个Pod是倾向与其它Pod不要运行同一个节点上

kubectl explain deployment.spec.template.spec.affinity.podAntiAffinity

kubectl explain pod.spec.affinity.podAntiAffinity

复制代码
requiredDuringSchedulingIgnoredDuringExecution  #硬亲和性
       labelSelector    #选择跟哪组标签匹配的Pod亲和
       namespaces #选择哪个命名空间进行匹配,如果namespace值为空,表示所有namespace,如果没
有namespace表示当前namespace
       topologyKey #用于判断是否是同一个位置,指定节点上的哪个键,这是一个必选项,不
能为空
        #注意:这三个条件是一个逻辑与的关系
   preferredDuringSchedulingIgnoredDuringExecution #软亲和性
       podAffinityTerm    #与权重关联的亲和选项,这是一个必选项
           labelSelector 
       namespaces #表示仅在指定的命名空间中查找
       topologyKey #在指定节点上的标签关键字,这是一个必选项,不能为空
       weight #权重,这是一个必选项

Pod 亲和性案例

范例: 硬亲和,创建两个在不同主机上的pod,然后创建一个第三个pod要求和有标签env=test的pod部署在一起

#两个节点准备标签,确保Pod调度至不同的节点

kubectl label node node1.org env=dev

kubectl label node node2.org env=test

#创建资源配置实现两个有不同标签的Pod

复制代码
[root@master1 scheduler]# cat scheduler-pod-affinity-base.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: controller-deployment-demo-dev
spec:
  replicas: 1
  selector:
    matchLabels:
      env: dev
  template:
    metadata:
      labels:
        env: dev        #注意:此处labels和selector中label必须相同
    spec:
      nodeSelector: #确保Pod调度至不同的节点
        env: dev
      containers:
      - name: deployment-2
        image: registry.cn-beijing.aliyuncs.com/wangxiaochun/pod-test:v0.1
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: controller-deployment-demo-test
spec:
  replicas: 1
  selector:
    matchLabels:
      env: test
  template:
    metadata:
      labels:
        env: test        #注意:此处labels和selector中label必须相同
    spec:
      nodeSelector:
        env: test
      containers:
      - name: deployment-2
        image: registry.cn-beijing.aliyuncs.com/wangxiaochun/pod-test:v0.1

#应用资源配置文件

kubectl apply -f scheduler-pod-affinity-base.yaml

#确认效果

kubectl get pod -o wide

复制代码
[root@master1 scheduler]# kubectl get pod -o wide
NAME                                               READY   STATUS    RESTARTS   AGE   IP             NODE        NOMINATED NODE   READINESS GATES
controller-deployment-demo-dev-55947b644-dl5cr     1/1     Running   0          74s   10.244.1.62    node1.org   <none>           <none>
controller-deployment-demo-test-7685c5779d-mfxbx   1/1     Running   0          74s   10.244.3.172   node2.org   <none>           <none>

#创建资源配置文件

复制代码
[root@master1 scheduler]# cat scheduler-pod-affinity-required.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: controller-deployment-demo-1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myapp2
  template:
    metadata:
      labels:
        app: myapp2
    spec:
      containers:
      - name: deployment-2
        image: registry.cn-beijing.aliyuncs.com/wangxiaochun/pod-test:v0.1
      affinity:
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: env
                operator: In
                values:
                - test
            namespaces:
            - default
            topologyKey: kubernetes.io/hostname

#属性解析: topologyKey的来源

kubectl get nodes -o yaml | grep kubernetes.io/hostname

复制代码
[root@master1 scheduler]# kubectl get nodes -o yaml | grep kubernetes.io/hostname
      kubernetes.io/hostname: master1.org
      kubernetes.io/hostname: node1.org
      kubernetes.io/hostname: node2.org
      kubernetes.io/hostname: node3.org

#指定 kubernetes.io/hostname 和主机名效果是一样的,只不过一个是自动获取,一个是手工指定,此处

表示使用主机名做为调度pod亲和性在"同一位置"的依据

#应用资源配置文件

kubectl apply -f scheduler-pod-affinity-required.yaml

#查看效果

kubectl get pod --show-labels -o wide

复制代码
[root@master1 scheduler]# kubectl get pod --show-labels -o wide 
NAME                                               READY   STATUS    RESTARTS   AGE   IP             NODE        NOMINATED NODE   READINESS GATES   LABELS
controller-deployment-demo-1-567fbcbc94-hrgfb      1/1     Running   0          5s    10.244.3.173   node2.org   <none>           <none>            app=myapp2,pod-template-hash=567fbcbc94
controller-deployment-demo-dev-55947b644-dl5cr     1/1     Running   0          23m   10.244.1.62    node1.org   <none>           <none>            env=dev,pod-template-hash=55947b644
controller-deployment-demo-test-7685c5779d-mfxbx   1/1     Running   0          23m   10.244.3.172   node2.org   <none>           <none>            env=test,pod-template-hash=7685c5779d
[root@master1 scheduler]# 
[root@master1 scheduler]# 

#结果显示:由于是硬亲和状态,所以,直接调度到对应亲和Pod所在的节点

#清理环境

kubectl delete -f scheduler-pod-affinity-required.yaml

范例: 软亲和, 多个满足条件的pod中选择最合适的Pod所在节点

#创建资源配置文件

复制代码
[root@master1 scheduler]# 
[root@master1 scheduler]# cat scheduler-pod-affinity-preferred.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: controller-deployment-demo-1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myapp2
  template:
    metadata:
      labels:
        app: myapp2
    spec:
      containers:
      - name: deployment-2
        image: registry.cn-beijing.aliyuncs.com/wangxiaochun/pod-test:v0.1
      affinity:
        podAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 60
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: env
                  operator: In
                  values:
                  - dev
              namespaces:
              - default
              topologyKey: kubernetes.io/hostname
          - weight: 30
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: env
                  operator: In
                  values:
                  - test
              namespaces:
              - default
              topologyKey: kubernetes.io/hostname

#应用资源配置文件

kubectl apply -f scheduler-pod-affinity-preferred.yaml

#查看效果

复制代码
[root@master1 scheduler]# kubectl get pod --show-labels -o wide
NAME                                               READY   STATUS    RESTARTS      AGE   IP             NODE        NOMINATED NODE   READINESS GATES   LABELS
controller-deployment-demo-1-75ffc97bf4-grmqb      1/1     Running   0             27s   10.244.1.88    node1.org   <none>           <none>            app=myapp2,pod-template-hash=75ffc97bf4
controller-deployment-demo-dev-55947b644-dl5cr     1/1     Running   1 (12m ago)   18h   10.244.1.70    node1.org   <none>           <none>            env=dev,pod-template-hash=55947b644
controller-deployment-demo-test-7685c5779d-mfxbx   1/1     Running   1 (12m ago)   18h   10.244.3.174   node2.org   <none>           <none>            env=test,pod-template-hash=7685c5779d

#结果显示:优先选择的是 node1节点上的Pod

翻译

搜索

复制

#配置解析:将应用调度到包含node标签,并且值为gpu的节点主机上

复制代码
[root@master1 scheduler]# kubectl get pod -o wide
NAME                                          READY   STATUS    RESTARTS   AGE   IP             NODE        NOMINATED NODE   READINESS GATES
node-resourcefits-affinity-5c94bc867c-6q4cg   1/1     Running   0          8s    10.244.3.169   node2.org   <none>           <none>
node-resourcefits-affinity-5c94bc867c-8r66l   1/1     Running   0          7s    10.244.3.168   node2.org   <none>           <none>

kubectl delete -f scheduler-pod-affinity-required.yaml

复制代码
root@master1 scheduler]# cat scheduler-pod-affinity-preferred.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: controller-deployment-demo-1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myapp2
  template:
    metadata:
      labels:
        app: myapp2
    spec:
      containers:
      - name: deployment-2
        image: registry.cn-beijing.aliyuncs.com/wangxiaochun/pod-test:v0.1
      affinity:
        podAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 60
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: env
                  operator: In
                  values:
                  - devops
              namespaces:
              - default
              topologyKey: kubernetes.io/hostname
          - weight: 30
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: env
                  operator: In
                  values:
                  - test
              namespaces:
              - default
              topologyKey: kubernetes.io/hostname
[root@master1 scheduler]# 

kubectl apply -f scheduler-pod-affinity-preferred.yaml

#查看效果,选择次优的节点node2

kubectl get pod --show-labels -o wide

复制代码
[root@master1 scheduler]# kubectl get pod --show-labels -o wide
NAME                                               READY   STATUS    RESTARTS      AGE   IP             NODE        NOMINATED NODE   READINESS GATES   LABELS
controller-deployment-demo-1-5db6f874d6-n4dhf      1/1     Running   0             5s    10.244.3.179   node2.org   <none>           <none>            app=myapp2,pod-template-hash=5db6f874d6
controller-deployment-demo-dev-55947b644-dl5cr     1/1     Running   1 (17m ago)   18h   10.244.1.70    node1.org   <none>           <none>            env=dev,pod-template-hash=55947b644
controller-deployment-demo-test-7685c5779d-mfxbx   1/1     Running   1 (17m ago)   18h   10.244.3.174   node2.org   <none>           <none>            env=test,pod-template-hash=7685c5779d
[root@master1 scheduler]# 

#清理环境

kubectl delete -f scheduler-pod-affinity-preferred.yaml

范例: 硬亲和实现 Pod必须把数据存储到redis中,必须在一起

#定义资源配置文件

复制代码
[root@master1 scheduler]# cat scheduler-pod-affinity-redis-required.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis
spec:
  replicas: 1
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
      - name: redis
        image: registry.cn-beijing.aliyuncs.com/wangxiaochun/redis:7.0
        imagePullPolicy: IfNotPresent
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: pod-affinity-required
spec:
  replicas: 4
  selector:
    matchLabels:
      app: pod-test
  template:
    metadata:
      labels:
        app: pod-test
    spec:
      containers:
      - name: pod-test
        image: registry.cn-beijing.aliyuncs.com/wangxiaochun/pod-test:v0.1
        imagePullPolicy: IfNotPresent
      affinity:
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - {key: app, operator: In, values: ["redis"]}
            topologyKey: kubernetes.io/hostname

#结果都运行在同一个节点

kubectl get pod -o wide --show-labels

redis在哪个节点就调度到哪个节点

范例:将Pod调度到同一个机架的服务器上

kubectl label node node1.org rack=rack01

kubectl label node node2.org rack=rack01

复制代码
[root@master1 scheduler]# cat scheduler-pod-affinity-redis-required02.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis
spec:
  replicas: 1
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      nodeSelector:
        rack: rack01
      containers:
      - name: redis
        image: registry.cn-beijing.aliyuncs.com/wangxiaochun/redis:7.0
        imagePullPolicy: IfNotPresent
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: pod-affinity-required
spec:
  replicas: 4
  selector:
    matchLabels:
      app: pod-test
  template:
    metadata:
      labels:
        app: pod-test
    spec:
      containers:
      - name: pod-test
        image: registry.cn-beijing.aliyuncs.com/wangxiaochun/pod-test:v0.1
        imagePullPolicy: IfNotPresent
      affinity:
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - {key: app, operator: In, values: ["redis"]}
            #topologyKey: kubernetes.io/hostname
            topologyKey: rack #根据机架确定位置

kubectl apply -f scheduler-pod-affinity-redis-required02.yaml

按照节点有标签就调度pod

复制代码
[root@master1 scheduler]# kubectl get pod -o wide
NAME                                    READY   STATUS    RESTARTS   AGE   IP             NODE        NOMINATED NODE   READINESS GATES
pod-affinity-required-9cc477f85-8vjz9   1/1     Running   0          89s   10.244.1.145   node1.org   <none>           <none>
pod-affinity-required-9cc477f85-9jmnl   1/1     Running   0          89s   10.244.3.191   node2.org   <none>           <none>
pod-affinity-required-9cc477f85-drd8d   1/1     Running   0          89s   10.244.1.144   node1.org   <none>           <none>
pod-affinity-required-9cc477f85-g2f54   1/1     Running   0          89s   10.244.3.190   node2.org   <none>           <none>
redis-565f4c9cd7-7dpqk                  1/1     Running   0          89s   10.244.1.146   node1.org   <none>           <none>

范例: 软亲和, pod要把数据存储到redis中,原则上最好在一起,实在不行也可以调度到其他节点

#定义资源配置文件

复制代码
[root@master1 scheduler]# cat scheduler-pod-affinity-redis-preferred.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis-preferred
spec:
  replicas: 1
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      nodeName: node1.org
      containers:
      - name: redis
        image: registry.cn-beijing.aliyuncs.com/wangxiaochun/redis:7.0
        imagePullPolicy: IfNotPresent
        resources:
          requests:
            cpu: 30m
            memory: 30Mi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: pod-affinity-preferred
spec:
  replicas: 6
  selector:
    matchLabels:
      app: pod-test
  template:
    metadata:
      labels:
        app: pod-test
    spec:
      containers:
      - name: pod-test
        image: registry.cn-beijing.aliyuncs.com/wangxiaochun/pod-test:v0.1
        imagePullPolicy: IfNotPresent
        resources:
          requests:
            cpu: 30m
            memory: 50Mi
      affinity:
        podAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - {key: app, operator: In, values: ["redis"]}
              topologyKey: kubernetes.io/hostname
          - weight: 50
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - {key: app, operator: In, values: ["redis"]}
              topologyKey: env          #根据env标签确认是否为同一个节点

#属性解析:如果第一条件满足不了,那就用第二条件,第二条件不满足,就用其他条件

#应用

kubectl apply -f scheduler-pod-affinity-redis-preferred.yaml

#可以看到pod分布到多个节点

复制代码
[root@master1 scheduler]# kubectl get pod -o wide                        
NAME                                      READY   STATUS    RESTARTS   AGE   IP             NODE        NOMINATED NODE   READINESS GATES
pod-affinity-preferred-69bdd98999-6jlk6   1/1     Running   0          79s   10.244.1.152   node1.org   <none>           <none>
pod-affinity-preferred-69bdd98999-b88x4   1/1     Running   0          79s   10.244.3.194   node2.org   <none>           <none>
pod-affinity-preferred-69bdd98999-drqmw   1/1     Running   0          79s   10.244.3.195   node2.org   <none>           <none>
pod-affinity-preferred-69bdd98999-h8x78   1/1     Running   0          79s   10.244.1.151   node1.org   <none>           <none>
pod-affinity-preferred-69bdd98999-k2d8b   1/1     Running   0          79s   10.244.3.193   node2.org   <none>           <none>
pod-affinity-preferred-69bdd98999-zdpt7   1/1     Running   0          79s   10.244.3.196   node2.org   <none>           <none>
redis-preferred-5d76988d8-4zb8p           1/1     Running   0          79s   10.244.1.150   node1.org   <none>           <none>

kubectl delete -f scheduler-pod-affinity-redis-preferred.yaml

Pod 反亲和性 podAntiAffinity

Pod 反亲和性说明

所谓的反亲和,其实就是满足条件,就不能pod调度在一起,与亲和正好相反

注意:反亲和的优化级低于NodeName

反亲和功能:

避免单点失败

kubernetes 集群的节点分布在不同的区域或者不同的机房, 当服务A需要做冗余操作,那么多个服务A必须在不同的位置

Redis集群的Pod 分别部署至不同的节点

避免资源竞争和冲突

使用hostNetwork, 避免端口冲突

Pod 反亲和性分为: 硬反亲和和软反亲和

属性解析

kubectl explain pod.spec.affinity.podAntiAffinity

复制代码
#硬反亲和
   requiredDuringSchedulingIgnoredDuringExecution #硬反亲和性
       labelSelector #选择跟哪组具有此标签的Pod反亲和
       namespaces #选择哪个命名空间进行匹配,如果namespace值为空,表示所有namespace,如果没
有namespace表示当前namespace
       topologyKey #指定节点上的哪个键做为同一位置的条件,这是一个必选项
        #注意:上面三个条件是一个逻辑与的关系
#软反亲和        
   preferredDuringSchedulingIgnoredDuringExecution #软反亲和性
       podAffinityTerm    #与权重关联的亲和选项,这是一个必选项
           labelSelector 
           namespaces 
           topologyKey 
       weight #权重,这是一个必选项
#注意:Pod反亲和性场景,当应用服务A和数据库服务B要求尽量不要在同一台节点上的时候。

Pod 反亲和性案例

范例: 硬性反亲和性,两个pod不能放在一起

#准备工作,准备两个有不同标签的Pod

#创建资源配置实现两个有不同标签的Pod

复制代码
[root@master1 scheduler]# cat scheduler-pod-affinity-base.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: controller-deployment-demo-dev
spec:
  replicas: 1
  selector:
    matchLabels:
      env: dev
  template:
    metadata:
      labels:
        env: dev        #注意:此处labels和selector中label必须相同
    spec:
      nodeSelector: #确保Pod调度至不同的节点
        env: dev
      containers:
      - name: deployment-2
        image: registry.cn-beijing.aliyuncs.com/wangxiaochun/pod-test:v0.1
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: controller-deployment-demo-test
spec:
  replicas: 1
  selector:
    matchLabels:
      env: test
  template:
    metadata:
      labels:
        env: test        #注意:此处labels和selector中label必须相同
    spec:
      nodeSelector:
        env: test
      containers:
      - name: deployment-2
        image: registry.cn-beijing.aliyuncs.com/wangxiaochun/pod-test:v0.1

#应用资源配置文件

kubectl apply -f scheduler-pod-affinity-base.yaml

复制代码
[root@master1 scheduler]# kubectl get pod -o wide
NAME                                               READY   STATUS    RESTARTS   AGE   IP             NODE        NOMINATED NODE   READINESS GATES
controller-deployment-demo-dev-55947b644-zw28d     1/1     Running   0          12s   10.244.1.153   node1.org   <none>           <none>
controller-deployment-demo-test-7685c5779d-b6ccv   1/1     Running   0          12s   10.244.3.197   node2.org   <none>           <none>

#创建资源配置文件

复制代码
[root@master1 scheduler]# cat  scheduler-pod-antiaffinity-required.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: controller-deployment-demo-1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myapp2
  template:
    metadata:
      labels:
        app: myapp2
    spec:
      containers:
      - name: deployment-2
        image: registry.cn-beijing.aliyuncs.com/wangxiaochun/pod-test:v0.1
      affinity:
        podAntiAffinity: #反亲和
          requiredDuringSchedulingIgnoredDuringExecution:        #硬反亲和
          - labelSelector:
              matchExpressions:
              - key: env
                operator: In
                values:
                - dev
            topologyKey: kubernetes.io/hostname

kubectl label node node1.org rack-

kubectl label node node2.org rack-

#应用资源配置文件

kubectl apply -f scheduler-pod-antiaffinity-required.yaml

#查看效果, 肯定不会部署到有dev标签的pod所在的节点上

复制代码
[root@master1 scheduler]# kubectl get pod -o wide
NAME                                               READY   STATUS    RESTARTS   AGE     IP             NODE        NOMINATED NODE   READINESS GATES
controller-deployment-demo-1-56bfccf4f5-56kk4      1/1     Running   0          6s      10.244.3.198   node2.org   <none>           <none>
controller-deployment-demo-dev-55947b644-zw28d     1/1     Running   0          6m26s   10.244.1.153   node1.org   <none>           <none>
controller-deployment-demo-test-7685c5779d-b6ccv   1/1     Running   0          6m26s   10.244.3.197   node2.org   <none>           <none>
[root@master1 scheduler]# 
[root@master1 scheduler]# 
复制代码
[root@master1 scheduler]# kubectl get node --show-labels     
NAME          STATUS     ROLES           AGE   VERSION   LABELS
master1.org   Ready      control-plane   27d   v1.35.6   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=master1.org,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node.kubernetes.io/exclude-from-external-load-balancers=
node1.org     Ready      <none>          27d   v1.35.6   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,env=dev,kubernetes.io/arch=amd64,kubernetes.io/hostname=node1.org,kubernetes.io/os=linux
node2.org     Ready      <none>          27d   v1.35.6   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,env=test,kubernetes.io/arch=amd64,kubernetes.io/hostname=node2.org,kubernetes.io/os=linux
node3.org     NotReady   <none>          27d   v1.35.6   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node3.org,kubernetes.io/os=linux

#清理环境

kubectl delete -f scheduler-pod-antiaffinity-required.yaml

范例: 软性反亲和性,最好不要哪些pod放在一起

#创建资源配置文件

复制代码
[root@master1 scheduler]# cat scheduler-pod-antiaffinity-preferred.yaml  
apiVersion: apps/v1
kind: Deployment
metadata:
  name: controller-deployment-demo-1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myapp2
  template:
    metadata:
      labels:
        app: myapp2
    spec:
      containers:
      - name: deployment-2
        image: registry.cn-beijing.aliyuncs.com/wangxiaochun/pod-test:v0.1
      affinity:
        podAntiAffinity: #反亲和
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 60
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: env
                  operator: In
                  values:
                  - dev
              topologyKey: kubernetes.io/hostname
          - weight: 30
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: env
                  operator: In
                  values:
                  - test
              topologyKey: kubernetes.io/hostname

#应用资源配置文件

kubectl apply -f scheduler-pod-antiaffinity-preferred.yaml

#查看效果

复制代码
[root@master1 scheduler]# 
[root@master1 scheduler]# kubectl get pod --show-labels -o wide                      
NAME                                               READY   STATUS    RESTARTS   AGE   IP             NODE        NOMINATED NODE   READINESS GATES   LABELS
controller-deployment-demo-1-7969c68f44-n9c4p      1/1     Running   0          3s    10.244.5.51    node3.org   <none>           <none>            app=myapp2,pod-template-hash=7969c68f44
controller-deployment-demo-dev-55947b644-zw28d     1/1     Running   0          45m   10.244.1.153   node1.org   <none>           <none>            env=dev,pod-template-hash=55947b644
controller-deployment-demo-test-7685c5779d-b6ccv   1/1     Running   0          45m   10.244.3.197   node2.org   <none>           <none>            env=test,pod-template-hash=7685c5779d
[root@master1 scheduler]# 

#结果显示:所有满足条件的都不能部署了

#清理环境

kubectl delete -f scheduler-pod-antiaffinity-preferred.yaml

kubectl delete -f scheduler-pod-affinity-base.yaml

节点污点和Pod容忍

污点Taint和容忍tolerations介绍

生产中可能希望让新的pod基于node节点的属性,创建在某些节点或不能创建在某些节点之上通过前面的节点调度,可以实现将Pod运行在指定的节点,

但是如果想让Pod不要运行在哪此节点上,Kubernetes 却没有提供所谓的节点反亲和性功能

但是有一种类似的策略:污点和容忍度。可以实现类似的功能

节点亲和性是 Pod 的一种属性,它使 Pod 被吸引到一类特定的节点 (这可能出于一种偏好,也可能是硬性要求)。

污点(Taint) 则相反------它使节点能够排斥一类特定的 Pod,是定义在节点的属性

容忍度(Toleration) 是应用于 Pod 上的属性。容忍度允许调度器调度 Pod到有污点的节点上。 容忍度允许调度但并不保证调度:作为其功能的一部分, 调度器也会评估其他参数。

术语解析

污点(taints)

定义在node节点上的键值属性数据。主要作用是让节点拒绝pod,尤其是不符合node规则的pod。

容忍度

(tolerations)

定义在Pod上的键值属性数据,用于配置可容忍的污点,调度器将其调度至容忍

Taint(污点)和 Toleration(容忍)是相互配合的,可以用来避免 pod 被分配到不合适的节点上

每个节点上都可以拥有一个或多个 taint ,这表示对于那些不能容忍这些 taint 的 pod,是不会被该节点

接受的。

比如:A节点是集群中很重要的节点,不要将无关的pod来这里运行,那么就可以在A上面添加一些污点

标识。

如果节点只打上Taint,但Pod上没有添加容忍度,则默认不会调度Pod至该节点

作用来源

我们可以使用PodToleratesNodeTaints预选策略和TaintTolerationPriority优选函数完成该机制

内置的污点功能

Kubernetes自1.6起支持使用污点自动标识问题节点,它通过节点控制器在特定条件下自动为节点添加污点信息实现。它们都使用NoExecute效用标识,因此非能容忍此类污点的现在Pod对象也会遭到驱

逐。

节点状态相关的某种条件为真时,节点控制器会自动给节点添加一个污点

目前,内建使用的此类污点有如下几个。

node.kubernetes.io/not-ready:节点进入NotReady状态时被自动添加的污点。

node.alpha.kubernetes.io/unreachable:节点进入NotReachable状态时被自动添加的污点。

node.kubernetes.io/out-of-disk:节点进入OutOfDisk状态时被自动添加的污点。

node.kubernetes.io/memory-pressure:节点内存资源面临压力。

node.kubernetes.io/disk-pressure:节点磁盘资源面临压力

node.kubernetes.io/pid-pressure:节点PID资源紧张

node.kubernetes.io/network-unavailable:节点网络不可用

node.kubernetes.io/unschedulable:节点不可用于调度

node.cloudprovider.kubernetes.io/uninitialized:kubelet由外部的云环境程序启动时,它自动

为节点添加此污点,待到云控制器管理器中的控制器初始化此节点时再将其删除。

不过,Kubernetes的核心组件通常都要容忍此类的污点,以确保其相应的DaemonSet控制器能够无视

此类污点于节点上部署相应的关键Pod对象,例如kube-proxy或kube-flannel等。

属性解析

Taint 污点属性

Taint是节点上的属性,Taints如下定义

复制代码
kubectl explain node.spec.taints(对象列表)
   key #定义一个key=value:effect
   value #定义一个值,这是一个可选项
 effect #pod不能容忍这个污点时,有三个行为值:NoSchedule、PreferNoSchedule、
NoExecute
 timeAdded  #记录污点被添加的具体时间,此属性仅与NoExecute类型的污点相关,在使用kubectl 
taint命令添加或删除污点时,Kubernetes会自动处理TimeAdded属性的更新或移除,此字段由系统自动添
加,无需人为管理

污点处理方式effect的属性值(优先级从低到高)

属性 解析

PreferNoSchedule

处于在调度优选阶段,属于柔性约束,即调度器尽量确保不会将那些不能

容忍此污点的Pod对象调度至当前节点,除非不存在其他任何能够容忍此污

点的节点可用;添加该类效用的污点同样对节点上现存的Pod对象不产生影

响。

NoSchedule

处于在调度预选阶段,不能容忍此污点的新Pod对象,不可以调度至当前节

点,属于强制型约束关系,但新添加的污点对节点上现存的Pod对象不产生

影响

NoExecute

不能容忍此污点的新Pod对象不可调度至当前节点,属于强制型约束关系

节点上现存的Pod因节点污点变动或Pod容忍度变动而不再满足匹配条件

时,Pod对象将可能会被驱逐

可能会对节点上已有的业务Pod产生如下影响

1)不能容忍该污点的Pod将立即被驱逐

2)若Pod能够容忍该污点,且Pod的容忍度上未定义tolerationSeconds,

则Pod可以一直运行于该节点

3)若Pod能够容忍该污点,且Pod的容忍度上定义了tolerationSeconds,

则Pod运行指定的时长后即会被驱逐

范例: 查看属性

#查看节点上的污点

#所有master节点默认都有污点

复制代码
[root@master1 ~]# kubectl describe node master1.org |grep Taint     
Taints:             node-role.kubernetes.io/control-plane:NoSchedule

#所有worker节点默认都没有污点

复制代码
[root@master1 ~]# kubectl describe node node1.org |grep Taint     
Taints:             <none>
复制代码
[root@master1 ~]#kubectl get node master1.wang.org -o yaml
...
 taints: #污点
  - effect: NoSchedule #效用标识
   key: node-role.kubernetes.io/master
...

污点管理命令

命令格式

#添加污点:

#指定节点添加污点

kubectl taint node <node-name> <key>=<value>:<effect> ...

kubectl taint node node1.org title=sre:NoSchedule

kubectl get node node1.org -o yaml |grep taint -iA 10

复制代码
[root@master1 ~]# kubectl get node node1.org -o yaml |grep taint -iA 10
  taints:
  - effect: NoSchedule
    key: title
    value: sre
status:
  addresses:
  - address: 192.168.3.63
    type: InternalIP
  - address: node1.org
    type: Hostname
  allocatable:

kubectl taint node node1.org study=nohard:NoSchedule

复制代码
[root@master1 workload]# kubectl get node node1.org -o yaml |grep taint -iA 10
  taints:
  - effect: NoSchedule
    key: study
    value: nohard
  - effect: NoSchedule
    key: title
    value: sre
status:
  addresses:
  - address: 192.168.3.63
    type: InternalIP
复制代码
[root@master1 workload]# cat controller-deployment2.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: controller-deployment-demo-2
spec:
  replicas: 2
  selector:
    matchLabels:
      app: myapp2
  template:
    metadata:
      labels:
        app: myapp2        #注意:此处labels和selector中label必须相同
    spec:
      containers:
      - name: deployment-2
        image: registry.cn-beijing.aliyuncs.com/wangxiaochun/pod-test:v0.2

kubectl apply -f controller-deployment2.yaml

只调度到node2,node1有污点不调度

复制代码
[root@master1 workload]# kubectl get pod -o wide
NAME                                           READY   STATUS    RESTARTS   AGE   IP             NODE        NOMINATED NODE   READINESS GATES
controller-deployment-demo-2-57ff4f59b-j8wmg   1/1     Running   0          30s   10.244.3.205   node2.org   <none>           <none>
controller-deployment-demo-2-57ff4f59b-sk8dt   1/1     Running   0          30s   10.244.3.206   node2.org   <none>           <none>
[root@master1 workload]# 

Tolerations 容忍度属性

Tolerations 是Pod上的属性,Tolerations如下定义

复制代码
kubectl explain deployment.spec.template.spec.tolerations
kubectl explain pod.spec.tolerations
   key    #被容忍的key
   value    #被容忍key的值,当operator为exists时,此value无需定义
   operator        #Equal(等值比较)和 Exists 存在比较
   effect #可以容忍的节点的行为值,必须和Traint中的effect值相同或高于才能容
忍                      
   tolerationSeconds #被驱逐的宽限时间,默认是0就是立即被驱逐

容忍度的判断方式

在Pod对象上定义容忍度时,它支持两种操作符operator

一种是等值比较Equal

表示Pod的容忍度与节点的污点必须在key、value和effect三者都完全匹配才可以容忍,此为默认

一种是存在性判断Exists

如果定义了key和effect,表示只要存在key和effect二者相同时才容忍,但不关注value就可容忍,必

须不能定义value

kubectl get ds -n metallb-system -o yaml|grep -i -A 10 tolerations

复制代码
--
        tolerations:
        - effect: NoSchedule
          key: node-role.kubernetes.io/master
          operator: Exists
        - effect: NoSchedule
          key: node-role.kubernetes.io/control-plane
          operator: Exists
        volumes:
        - name: memberlist
          secret:
            defaultMode: 420

kubectl get node master1.org -o yaml|grep -i -A 10 taints

复制代码
[root@master1 ~]# kubectl get node master1.org -o yaml|grep -i -A 10  taints
  taints:
  - effect: NoSchedule
    key: node-role.kubernetes.io/control-plane
status:
  addresses:
  - address: 192.168.3.60
    type: InternalIP
  - address: master1.org
    type: Hostname
  allocatable:
    cpu: "2"

如果只定义了effect并没有定义key,表示指定effect的所有key都能容忍

kubectl get ds -n kube-system kube-flannel -o yaml |grep -i -A10 tolerations

复制代码
--
      tolerations:
      - effect: NoSchedule
        operator: Exists
      volumes:
      - hostPath:
          path: /run/flannel
          type: ""
        name: run
      - hostPath:
          path: /opt/cni/bin
          type: ""

如果key和effect都没有定义,表示容忍所有污点

kubectl get ds -n kube-system kube-proxy -o yaml |grep -i -A3 tolerations:

复制代码
[root@master1 ~]# kubectl get ds -n kube-system kube-proxy -o yaml |grep -i -A3 tolerations:
      tolerations:
      - operator: Exists
      volumes:
      - configMap:

容忍度的匹配原则

一个节点可以配置使用多个污点,而一个Pod对象也可以有多个容忍度

Kubernetes 处理多个污点和容忍度的过程就像一个过滤器:从一个节点的所有污点开始遍历, 过滤掉

那些 Pod 中存在与之相匹配的容忍度的污点。余下未被过滤的污点的 effect 值决定了 Pod 是否会被分

配到该节点

一个节点上如果有多个污点Taint,对每个Taint 都需要容忍才能部署Pod

将一个Pod对象的容忍度套用到特定节点的污点之上进行匹配度检测时,将遵循如下逻辑

遍历该节点的所有污点,并过滤掉容忍度可匹配到的污点

余下未被过滤掉的污点,则由污点的effect来判定其满足状态

对于剩余的不能匹配到容忍度的所有污点中,若存在至少一个污点使用了NoSchedule效用标

识,则拒绝调度当前Pod至该节点

对于不能匹配到容忍度的所有污点,若都不具有NoSchedule效用标识,但至少有一个污点使

用了PreferNoScheduler效用标准,则调度器会尽量避免将当前Pod对象调度至该节点

如果至少有一个不能匹配容忍度的污点使用了NoExecute效用标识,节点将立即驱逐当前Pod

对象,或者不允许该Pod调度至给定的节点;而且,即便容忍度可以匹配到使用了NoExecute

效用标识的污点,若在Pod上定义容忍度时同时使用tolerationSeconds属性定义了容忍时

限,则在超出时限后当前Pod也将会被节点所驱逐

添加容忍度

复制代码
[root@master1 ~]# kubectl get deployment
NAME                           READY   UP-TO-DATE   AVAILABLE   AGE
controller-deployment-demo-2   2/2     2            2           9h

Exists并且没有key和value,可以容忍所有污点,可以支持

复制代码
root@master1 workload]# cat controller-deployment2.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: controller-deployment-demo-2
spec:
  replicas: 2
  selector:
    matchLabels:
      app: myapp2
  template:
    metadata:
      labels:
        app: myapp2
    spec:
      containers:
      - name: deployment-2
        image: registry.cn-beijing.aliyuncs.com/wangxiaochun/pod-test:v0.2
      tolerations:
      - operator: Exists

kubectl apply -f controller-deployment2.yaml

啥都可以容忍

复制代码
[root@master1 workload]# kubectl get pod -o wide

NAME                                            READY   STATUS    RESTARTS   AGE   IP             NODE          NOMINATED NODE   READINESS GATES
controller-deployment-demo-2-84458587c5-cczk4   1/1     Running   0          95s   10.244.1.202   node1.org     <none>           <none>
controller-deployment-demo-2-84458587c5-cm2zm   1/1     Running   0          95s   10.244.5.57    node3.org     <none>           <none>
controller-deployment-demo-2-84458587c5-kj794   1/1     Running   0          95s   10.244.0.56    master1.org   <none>           <none>
controller-deployment-demo-2-84458587c5-nwzgp   1/1     Running   0          95s   10.244.3.211   node2.org     <none>           <none>
复制代码
[root@master1 workload]# cat  controller-deployment2.yaml   
apiVersion: apps/v1
kind: Deployment
metadata:
  name: controller-deployment-demo-2
spec:
  replicas: 4
  selector:
    matchLabels:
      app: myapp2
  template:
    metadata:
      labels:
        app: myapp2
    spec:
      containers:
      - name: deployment-2
        image: registry.cn-beijing.aliyuncs.com/wangxiaochun/pod-test:v0.2
      tolerations:
      - effect: NoSchedule
        operator: Exists
        key: title
      - key: study
        operator: Exists

kubectl apply -f controller-deployment2.yaml

不调度到master1.org

复制代码
[root@master1 workload]# kubectl get pod -o wide
NAME                                           READY   STATUS    RESTARTS   AGE   IP             NODE        NOMINATED NODE   READINESS GATES
controller-deployment-demo-2-ff9cdbb4d-c76mx   1/1     Running   0          13s   10.244.5.59    node3.org   <none>           <none>
controller-deployment-demo-2-ff9cdbb4d-g8fwg   1/1     Running   0          13s   10.244.3.212   node2.org   <none>           <none>
controller-deployment-demo-2-ff9cdbb4d-h27sc   1/1     Running   0          13s   10.244.5.58    node3.org   <none>           <none>
controller-deployment-demo-2-ff9cdbb4d-vdm5j   1/1     Running   0          13s   10.244.1.203   node1.org   <none>           <none>

#删除污点:

kubectl taint node <node-name> <key>:\-

kubectl taint node node1.org study-

复制代码
[root@master1 workload]# kubectl get node node1.org -o yaml |grep taint -iA 10      
  taints:
  - effect: NoSchedule
    key: title
    value: sre
status:
  addresses:
  - address: 192.168.3.63
    type: InternalIP
  - address: node1.org
    type: Hostname
  allocatable:

kubectl taint node node1.org title-

#cordon封锁节点,即添加污点node.kubernetes.io/unschedulable:NoSchedule,只影响新创建Pod调度

kubectl cordon <node-name>

kubectl cordon node1.org

添加污点

复制代码
root@master1 workload]# kubectl get node node1.org -o yaml|grep -i -A 10  taints
  taints:
  - effect: NoSchedule
    key: node.kubernetes.io/unschedulable
    timeAdded: "2026-07-17T00:33:29Z"
  unschedulable: true
status:
  addresses:
  - address: 192.168.3.63
    type: InternalIP
  - address: node1.org
    type: Hostname
复制代码
[root@master1 workload]# cat  controller-deployment2.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: controller-deployment-demo-2
spec:
  replicas: 4
  selector:
    matchLabels:
      app: myapp2
  template:
    metadata:
      labels:
        app: myapp2
    spec:
      containers:
      - name: deployment-2
        image: registry.cn-beijing.aliyuncs.com/wangxiaochun/pod-test:v0.2

kubectl apply -f controller-deployment2.yaml

复制代码
[root@master1 workload]# kubectl get pod  -o wide
NAME                                           READY   STATUS    RESTARTS   AGE   IP             NODE        NOMINATED NODE   READINESS GATES
controller-deployment-demo-2-57ff4f59b-84hsc   1/1     Running   0          11s   10.244.3.217   node2.org   <none>           <none>
controller-deployment-demo-2-57ff4f59b-m49k6   1/1     Running   0          11s   10.244.3.218   node2.org   <none>           <none>
controller-deployment-demo-2-57ff4f59b-n4wtp   1/1     Running   0          11s   10.244.3.219   node2.org   <none>           <none>
controller-deployment-demo-2-57ff4f59b-rjtnh   1/1     Running   0          11s   10.244.3.220   node2.org   <none>           <none>

#删除污点node.kubernetes.io/unschedulable:NoSchedule

kubectl uncordon <node-name>

kubectl uncordon node1.org

kubectl get node node1.org -o yaml|grep -i -A 10 taints

kubectl delete -f controller-deployment2.yaml

kubectl apply -f controller-deployment2.yaml

复制代码
[root@master1 workload]# kubectl get pod  -o wide
NAME                                           READY   STATUS    RESTARTS   AGE   IP             NODE        NOMINATED NODE   READINESS GATES
controller-deployment-demo-2-57ff4f59b-2n7cb   1/1     Running   0          37s   10.244.1.229   node1.org   <none>           <none>
controller-deployment-demo-2-57ff4f59b-4xhmv   1/1     Running   0          37s   10.244.3.225   node2.org   <none>           <none>
controller-deployment-demo-2-57ff4f59b-lgpgt   1/1     Running   0          37s   10.244.3.226   node2.org   <none>           <none>
controller-deployment-demo-2-57ff4f59b-v6mld   1/1     Running   0          37s   10.244.1.228   node1.org   <none>           <none>

kubectl cordon node1.org

复制代码
[root@master1 workload]# cat controller-deployment2.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: controller-deployment-demo-2
spec:
  replicas: 6
  selector:
    matchLabels:
      app: myapp2
  template:
    metadata:
      labels:
        app: myapp2
    spec:
      containers:
      - name: deployment-2
        image: registry.cn-beijing.aliyuncs.com/wangxiaochun/pod-test:v0.2

kubectl apply -f controller-deployment2.yaml

只影响新创建Pod调度

复制代码
[root@master1 workload]# kubectl get pod  -o wide
NAME                                           READY   STATUS    RESTARTS   AGE    IP             NODE        NOMINATED NODE   READINESS GATES
controller-deployment-demo-2-57ff4f59b-2n7cb   1/1     Running   0          8m2s   10.244.1.229   node1.org   <none>           <none>
controller-deployment-demo-2-57ff4f59b-4xhmv   1/1     Running   0          8m2s   10.244.3.225   node2.org   <none>           <none>
controller-deployment-demo-2-57ff4f59b-c25gl   1/1     Running   0          7s     10.244.3.227   node2.org   <none>           <none>
controller-deployment-demo-2-57ff4f59b-lgpgt   1/1     Running   0          8m2s   10.244.3.226   node2.org   <none>           <none>
controller-deployment-demo-2-57ff4f59b-sqcqk   1/1     Running   0          7s     10.244.3.228   node2.org   <none>           <none>
controller-deployment-demo-2-57ff4f59b-v6mld   1/1     Running   0          8m2s   10.244.1.228   node1.org   <none>           <none>

kubectl uncordon node1.org

旧的pod还在

#强制驱逐节点上Pod

#方法1: drain 排空节点上面的 Pod,同时添加污点

node.kubernetes.io/unschedulable:NoSchedule

#注意:要删除node.kubernetes.io/unschedulable:NoSchedule污点,需要执行kubectl uncordon <node-name>

#--ignore-daemonsets 默认kubectl drain删除daemonset资源会报错,加此项可以忽略错误提示

kubectl drain <nodename> --ignore-daemonsets --delete-emptydir-data --force

kubectl drain node1.org --ignore-daemonsets --force

#取消强制驱逐节点上Pod

kubectl uncordon <node-name>

kubectl uncordon node1.org

#方法2:使用污点NoExecute实现

kubectl taint nodes node1.org name=ming:NoExecute

#驱逐节点上的pod

kubectl drain <node_name> --ignore-daemonsets

拓扑调度

Pod 拓扑分布约束说明

https://kubernetes.io/zh-cn/docs/concepts/scheduling-eviction/topology-spreadconstraints/

拓扑分布约束可以控制Pod的调度实现平均分布的程度

在定义pod亲和性和反亲和时,有一个 topologyKey的属性,但是默认情况下,pod的亲和性调度,仅

仅针对单一的拓扑场景,也就是说,要么所有的pod都在这里,要么所有的pod都不要在这里,这样会

导致应用过于集中,反而导致物理资源的浪费。

如果希望在进行正常pod亲和性调度的时候,能够自动识别多个不同的调度节点,然后均匀的分散到多

个节点中,从而实现资源的有效利用。通过拓扑调度来实现的功能

注意:此功能 k8s 1.19版本才支持,v1.18 之前版本要用 Pod 拓扑扩展,需要在 API 服务器 和调度器中

启用 EvenPodsSpread 特性。

拓扑域

在Kubernetes中,拓扑域是指按节点的标签定义和分组的节点集合

例如,区域、可用区、机架、主机名都可以作为将节点划分成不同拓扑域的依据

Kubernetes使用TopologyKey(拓扑键)来定义将节点划分至不同拓扑域的标准topologyKey 的值决定是否是同一个位置

具有和 Pod的 topologyKey 标签的对应值相同的 Node 被分在一个拓扑域,topologyKey 标签的对应值不同的 Node 被分在另一个拓扑域。即 同一个 topologyKey 标签对应有几个不同的值,就会

划分为几个拓扑域

敏置偏差Skew

偏差Skew 是当前每一个拓扑域Pod数量和Pod数量最少的拓扑域之间的Pod数量的差值

公式:当前 i 拓扑域 skewi = 拓扑域i中标签匹配的 Pod 个数 - min{拓扑域\*中标签匹配的

Pod 个数}

当前第i拓扑域 的skewi 值不能大于最大偏差maxSkew

Pod分布约束中允许的最大偏差maxSkew及妥协策略

Pod分散约束策略基于允许的最大偏差(maxSkew)来定义Pod在拓扑域中的分散逻辑

当无法满足最大偏差约束时,whenUnsatisfable则用于定义妥协策略

DoNotSchedule(默认):不予调度,Pod将处于Pending状态,即硬限制

ScheduleAnyway:根据每个节点的skew值打分排序后进行调度,即软限制

属性解析

复制代码
kubectl explain pod.spec.topologySpreadConstraints
   labelSelector #根据 LabelSelector来计算出匹配的pod的数量
   maxSkew     #描述了 pod 可能不均匀分布的程度,不同拓扑域之间的各节点pod数量比
例的最大限制
   topologyKey #用于指定带有此标签的值相同的节点在同一拓扑域中。
   whenUnsatisfiable #指示如果 Pod 不满足扩展约束,如何处理它。默认值是DoNotSchedule
#maxSkew 默认值是1,即各个不同的拓扑中的Pod总数量之差最大不能超过1,如果这个值越大,则pod的分
布可能会是不均匀的。
#注意:节点上没有定义topologyKey,则Pod不会调度到该节点上。

案例分析

labelSelector 用于查找匹配的 Pod。对于每个拓扑,我们计算 与此标签选择器匹配的 Pod 数

量。在上面的例子中,给定 labelSelector为"app: foo","zone1"中的匹配数字为2;而 "Zone2"中

的数字为 0。

topologyKey 是在节点标签中定义拓扑的键。如果两个 Node 的 label 同时具有该 key 并且 label

值相同,就说它们在同一个拓扑域。在上图中,指定 topologyKey 为 zone, 具有 "zone=zone1"

标签的 Node 被分在一个拓扑域,具有 "zone=zone2" 标签的 Node 被分在另一个拓扑域。

maxSkew 描述了 Pod 在不同拓扑域中Skew不均匀分布的最大程度,maxSkew 的取值必须大于

0。每个拓扑域都有一个 skew,计算的公式是:skewi = 拓扑域i中匹配的 Pod 个数 - min{拓

扑域\*中匹配的 Pod 个数}。skew 不能大于maxSkew ,在上图中,我们新建一个带有 "app=foo"

label 的 Pod:

如果将传入的 Pod 放在 "zone1",则 "zone1" 上的偏斜 skew 值将变为 3 ( 在"zone1"中匹配

的 Pod为3个;在"zone2"上匹配的全局最小值为 0 个 Pod),违反了"maxSkew: 1"约束。

如果将传入的 Pod 放置在 "zone2",则 "zone1" 中 的 skew 值变为 1, "zone2" 上的偏斜

skew 值为 0 (zone2 有 1 个匹配的 Pod,拥有全局最小匹配 Pod 数的拓扑域正是 zone2 自己

), 则满足"maxSkew: 1"约束

案例

范例: 均匀分散到多个节点中

#准备工作,为所有节点添加标识,#注意:Master节点上没有定义topologyKey,所以Pod不会调度到该节

点上。

kubectl label node node1.org zone=zoneA

kubectl label node node2.org zone=zoneB

kubectl label node node3.org zone=zoneA

复制代码
[root@master1 scheduler]# kubectl get node --show-labels
NAME          STATUS   ROLES           AGE   VERSION   LABELS
master1.org   Ready    control-plane   28d   v1.35.6   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=master1.org,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node.kubernetes.io/exclude-from-external-load-balancers=
node1.org     Ready    <none>          28d   v1.35.6   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,env=dev,kubernetes.io/arch=amd64,kubernetes.io/hostname=node1.org,kubernetes.io/os=linux,zone=zoneA
node2.org     Ready    <none>          28d   v1.35.6   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,env=test,kubernetes.io/arch=amd64,kubernetes.io/hostname=node2.org,kubernetes.io/os=linux,zone=zoneB
node3.org     Ready    <none>          28d   v1.35.6   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node3.org,kubernetes.io/os=linux,zone=zoneA

定制资源配置文件

复制代码
[root@master1 scheduler]# cat  scheduler-pod-topology-test.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: pod-affinity-preferred
spec:
  replicas: 10
  selector:
    matchLabels:
      foo: bar
  template:
    metadata:
      labels:
        foo: bar
    spec:
      tolerations:
      - operator: Exists #包括所有有污点的节点    
      containers:
      - name: pod-test
        image: registry.cn-beijing.aliyuncs.com/wangxiaochun/pod-test:v0.1
        imagePullPolicy: IfNotPresent
      topologySpreadConstraints:
      - maxSkew: 2
        #topologyKey: kubernetes.io/hostname
        topologyKey: zone  
        whenUnsatisfiable: DoNotSchedule
        labelSelector:
          matchLabels:
            foo: bar

#应用资源配置文件

kubectl apply -f scheduler-pod-topology-test.yaml

#清理环境

kubectl delete -f scheduler-pod-topology-test.yaml

kubectl label node node1.org zone-

kubectl label node node2.org zone-

kubectl label node node3.org zone-

优先级调度

集群资源不够用时候怎么办?

  • 先到先得策略(FIFO):简单、相对公平、上手快
  • 优先级策略(Priority): 符合日常公司业务特点, 优先级值越大,优先级越高

优先级PodPriority和抢占Preemption

  • v1.14-stable
  • PodPriority 和 Preemption 默认开启 ON

优先级PodPriority 调度过程(发生在Pending Pod出队列开始调度):

  • Pod2(低优先级)和Pod1(高优先级)先后进入调度队列,但均未被调度
  • 当进行一轮调度时,PriorityQueue会优先将优先级更大的Pod1弹出队列进行调度
  • 调度成功后,对此Pod1进行Bind,并开始下一轮调度Pod2

优先级抢占Preemption过程(发生在调度失败):

  • Pod2先进行调度,调度成功后分配至Node1上运行
  • 之后Pod1再进行调度,由于Node1资源不足出现了调度失败,此时进入抢占流程
  • 在经过抢占算法计算后,选中了Pod2作为Pod1的承让者
  • 驱逐了Node1上运行的Pod2,并将Pod1调度至Node1

配置流程

  • 创建PriorityClass并指定value, value越大优先越高
  • 为各个Pod配置上不同的priorityClassName

一些内置优先

内置默认优先级

DefaultPriorityWhenNoDefaultClassExists=0

用户可配置的最大优先级限制

源码定义

HighestUserDefinablePriority= 1000000000

系统级别优先级

SystemCriticalPriority=2000000000

Kubernetes 的优先级值范围是 0 到 10^9 - 1(即 999,999,999),而 2000000000 超出了这个常

规范围

这是一个硬编码的特殊值,专门用于系统最核心的组件(如 kubelet、API Server 等),确保系统关

键组件的 Pod 不会被 Kubernetes 的调度机制(如资源压力下的驱逐)优先终止,即使在集群资源

极度紧张时,也能最大限度保证系统基础功能的可用性

内置系统级别优先级

system-cluster-critical 2000000000

system-node-critical 2000001000

这是两个公共的优先级类,主要用来确保 Kubernetes 系统的关键组件或者关键插件总是能够优先

被调度,比如 coredns 等

复制代码
// HighestUserDefinablePriority is the highest priority for user defined priority 
classes. Priority values larger than 1 billion are reserved for Kubernetes system 
use.
HighestUserDefinablePriority = int32(1000000000)
// SystemCriticalPriority is the beginning of the range of priority values for 
critical system components.
SystemCriticalPriority = 2 * HighestUserDefinablePriority

范例: 查看优先级类

#默认的最高级的优先级类

kubectl get priorityclasses.scheduling.k8s.io

复制代码
[root@master1 ~]#kubectl get priorityclasses.scheduling.k8s.io 
NAME                     VALUE       GLOBAL-DEFAULT   AGE
system-cluster-critical   2000000000   false           161d
system-node-critical      2000001000   false           161d

范例: 自定义的优先级

复制代码
[root@master1 scheduler]# cat scheduler-PriorityClass-high.yaml
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata :
  name: high
value: 10000
globalDefault: false
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata :
  name: lower
value: 100
globalDefault: false

kubectl apply -f scheduler-PriorityClass-high.yaml

kubectl get priorityclasses.scheduling.k8s.i

复制代码
root@master1 scheduler]# kubectl get priorityclasses.scheduling.k8s.i
NAME                      VALUE        GLOBAL-DEFAULT   AGE   PREEMPTIONPOLICY
high                      10000        false            9s    PreemptLowerPriority
lower                     100          false            9s    PreemptLowerPriority
system-cluster-critical   2000000000   false            28d   PreemptLowerPriority
system-node-critical      2000001000   false            28d   PreemptLowerPriority

#使用自定义的优先级

复制代码
[root@master1 workload]# cat controller-deployment2.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: controller-deployment-demo-2
spec:
  replicas: 6
  selector:
    matchLabels:
      app: myapp2
  template:
    metadata:
      labels:
        app: myapp2
    spec:
      priorityClassName: high
      containers:
      - name: deployment-2
        image: registry.cn-beijing.aliyuncs.com/wangxiaochun/pod-test:v0.2

kubectl apply -f controller-deployment2.yaml

#查看优先级

复制代码
[root@master1 workload]# kubectl get pod controller-deployment-demo-2-b98b67896-6fcsh -o yaml |grep priority
  priority: 10000
  priorityClassName: high

节点压力驱逐 Node-pressure Eviction

什么是节点压力驱逐

https://kubernetes.io/zh-cn/docs/concepts/scheduling-eviction/node-pressureeviction/

节点压力驱逐是 kubelet 主动终止 Pod 以回收节点上资源的过程

在 kubernetes 的每个集群节点上,都有一个 kubelet 服务,它会监控集群节点的内存、磁盘空间和文件系统的 inode 等资源。

当这些资源中的一个或者多个达到特定的消耗水平,kubelet 可以主动驱逐节点上一个或者多个 Pod,以回收资源防止出现资源饥饿

kubelet 会主动给节点打上一个或者多个污点标记,这些标记的 effect 为 NoExecute

内存比较紧张,会打上 node.kubernetes.io/memory-pressure

磁盘比较紧张,会打上 node.kubernetes.io/disk-pressure

pid 比较紧张,会打上 node.kubernetes.io/pid-pressure

而如果该节点上,已有一些 Pod 在运行,并且这些 Pod 没有配置以上三种对应的容忍度,则 kubelet

会开始驱逐的流程,一个一个的驱逐,直到节点不再有存在资源压力为止,才会清除污点,结束驱逐。

在节点压力驱逐期间,kubelet 将所选 Pod 的 PodPhase 设置为 Failed。这将终止 Pod。

通常还会带上一个 tolerationSeconds ,它意思是在污点出现后,Pod 还可以正常工作多少时间,也就是延迟多久再进行驱逐。

除了以上污点之外,还有其他常见的

而这些污点的 effect 通常为 NoSchedule,以防新的 Pod 调度过来,却无法正常工作。

驱逐信号

驱逐信号是特定资源在特定时间点的当前状态,表示驱逐的条件。

kubelet 使用驱逐信号,通过将信号与驱逐条件进行比较来做出驱逐决定, 驱逐条件是节点上应该可用资源的最小量。

kubelet 使用以下驱逐信号

驱逐信号 描述

memory.available

memory.available := node.status.capacitymemory -

node.stats.memory.workingSet

nodefs.available nodefs.available := node.stats.fs.available

nodefs.inodesFree nodefs.inodesFree := node.stats.fs.inodesFree

imagefs.available imagefs.available := node.stats.runtime.imagefs.available

imagefs.inodesFree

imagefs.inodesFree :=

node.stats.runtime.imagefs.inodesFree

pid.available

pid.available := node.stats.rlimit.maxpid -

node.stats.rlimit.curproc

在上表中, 描述 列显示了 kubelet 如何获取信号的值。每个信号支持百分比值或者是字面值。 kubelet

计算相对于与信号有关的总量的百分比值。

memory.available 的值来自 cgroupfs,而不是像 free -m 这样的工具。 这很重要,因为 free -m

在容器中不起作用,如果用户使用 节点可分配资源 这一功能特性,资源不足的判定是基于 cgroup 层次

结构中的用户 Pod 所处的局部及 cgroup 根节点作出的。 这个脚本 重现了 kubelet 为计算

memory.available 而执行的相同步骤。 kubelet 在其计算中排除了 inactive_file(即非活动 LRU 列表

上基于文件来虚拟的内存的字节数), 因为它假定在压力下内存是可回收的。

kubelet 支持以下文件系统分区:

    1. nodefs :节点的主要文件系统,用于本地磁盘卷、emptyDir、日志存储等。 例如, nodefs 包含/var/lib/kubelet/ 。
    1. imagefs :可选文件系统,供容器运行时存储容器镜像和容器可写层。

驱逐条件

你可以为 kubelet 指定自定义驱逐条件,以便在作出驱逐决定时使用。

驱逐条件的形式为

复制代码
[eviction-signal][operator][quantity]
#eviction-signal 是要使用的[驱逐信号]。
#operator 是你想要的[关系运算符] 比如 `<`(小于)。
#quantity 是驱逐条件数量,例如 `1Gi`。 `quantity` 的值必须与 Kubernetes 使用的数量表示相
匹配。 你可以使用文字值或百分比(`%`)。

例如,如果一个节点的总内存为 10Gi 并且你希望在可用内存低于 1Gi 时触发驱逐, 则可以将驱逐条件

定义为 memory.available<10% 或 memory.available< 1G 。 你不能同时使用二者。

你可以配置软和硬驱逐条件。

软驱逐条件

软驱逐条件将驱逐条件与管理员所必须指定的宽限期配对 。 在超过宽限期之前,kubelet 不会驱逐

Pod。 如果没有指定的宽限期,kubelet 会在启动时返回错误。

你可以既指定软驱逐条件宽限期,又指定 Pod 终止宽限期的上限,给 kubelet 在驱逐期间使用。 如果你

指定了宽限期的上限并且 Pod 满足软驱逐阈条件,则 kubelet 将使用两个宽限期中的较小者。 如果你没

有指定宽限期上限,kubelet 会立即杀死被驱逐的 Pod,不允许其体面终止。

你可以使用以下标志来配置软驱逐条件:

  • eviction-soft :一组驱逐条件,如 memory.available<1.5Gi , 如果驱逐条件持续时长超过指定的宽限期,可以触发 Pod 驱逐。
  • eviction-soft-grace-period :一组驱逐宽限期, 如 memory.available=1m30s ,定义软驱逐条件在触发 Pod 驱逐之前必须保持多长时间。
  • eviction-max-pod-grace-period :在满足软驱逐条件而终止 Pod 时使用的最大允许宽限期(s为单位),超过此值就强制杀死

硬驱逐条件

硬驱逐条件没有宽限期 。当达到硬驱逐条件时, kubelet 会立即杀死 pod,而不会正常终止以回收紧缺

的资源。

你可以使用 eviction-hard 标志来配置一组硬驱逐条件, 例如 memory.available<1Gi 。

kubelet 具有以下默认硬驱逐条件:

  • memory.available<100Mi
  • nodefs.available<10%
  • imagefs.available<15%
  • nodefs.inodesFree<5% (Linux 节点)

只有在没有更改任何参数的情况下,硬驱逐阈值才会被设置成这些默认值。 如果你更改了任何参数的

值,则其他参数的取值不会继承其默认值设置,而将被设置为零。 为了提供自定义值,你应该分别设置

所有阈值。

驱逐监测间隔

kubelet 根据其配置的 housekeeping-interval (默认为 10s )评估驱逐条件

范例: 基于查看驱逐条件信息

https://kubernetes.io/zh-cn/docs/tasks/administer-cluster/kubelet-config-file/

驱逐条件配置

kube-system/kubelet-config ConfigMap :是集群统一管理的 kubelet 基准配置 (存放在 etcd中)。

/var/lib/kubelet/config.yaml :是节点上 kubelet 进程实际加载使用的本地配置文件

关系:ConfigMap 是源头,本地文件是副本 ------ 集群通过节点组件把 ConfigMap 同步到节点本地,

kubelet 最终读取本地文件生效。

Kubeadm 部署的集群 (最常用)中,有一个专门的组件负责同步:

kubelet-config → 节点 → /var/lib/kubelet/config.yaml

  1. 集群管理员 /kubeadm 把标准配置写入 kubelet-config ConfigMap

  2. 节点上的 kubelet 服务 + kubeadm 节点组件 会监听这个 ConfigMap

  3. 当 ConfigMap 变更时,自动同步覆盖节点本地的 /var/lib/kubelet/config.yaml

复制代码
同步方法
1) 节点上执行:拉取最新CM覆盖本地config.yaml
kubeadm upgrade node phase kubelet-config
# 重启kubelet生效
systemctl restart kubelet
2) kubeadm upgrade apply 集群版本全集群升级时,全节点自动执行上面同步逻辑,统一刷新本地
配置。
  1. kubelet 重启 / 热加载后,使用新的本地配置生效

注意:不要直接改 /var/lib/kubelet/config.yaml:kubeadm 组件会定时从 CM 重新拉取覆盖,修改会被还原。

单节点有两种配置方案:

方案 1:systemd 追加启动参数(最推荐、永久生效、不怕 CM 刷新覆盖)

复制代码
1. 指定节点修改
mkdir -p /etc/systemd/system/kubelet.service.d/
cat > /etc/systemd/system/kubelet.service.d/20-eviction-override.conf <<EOF
[Service]
Environment="KUBELET_EXTRA_ARGS=--eviction-hard=memory.available<100Mi"
EOF
2.
systemctl daemon-reload
systemctl restart kubelet

方案 2:指定独立本地配置文件Kubernetes

复制代码
1、复制原有配置到自定义文件
cp /var/lib/kubelet/config.yaml /var/lib/kubelet/node-local-config.yaml
# 修改evictionHard
vi /var/lib/kubelet/node-local-config.yaml
evictionHard:
 memory.available: "100Mi"
 nodefs.available: "10%"
 nodefs.inodesFree: "5%"
 imagefs.available: "15%"
2、systemd 指定 --config 指向新文件
cat > /etc/systemd/system/kubelet.service.d/20-localconfig.conf <<EOF
[Service]
Environment="KUBELET_EXTRA_ARGS=--config=/var/lib/kubelet/node-localconfig.yaml"
EOF
3.
systemctl daemon-reload
systemctl restart kubelet

#kubeadm安装方法查看kubelet配置文件路径

复制代码
[root@master1 ~]# systemctl cat kubelet.service |grep config.yaml
Environment="KUBELET_CONFIG_ARGS=--config=/var/lib/kubelet/config.yaml"

ps aux|grep kubelet

#kubeadm安装方法默认没有配置evictionHard

grep evictionHard /var/lib/kubelet/config.yaml

cat /var/lib/kubelet/config.yaml

kubectl get cm kubelet-config -n kube-system -o yaml

复制代码
[root@master1 ~]# kubectl get cm   kubelet-config  -n kube-system -o yaml
apiVersion: v1
data:
  kubelet: |
    apiVersion: kubelet.config.k8s.io/v1beta1
    authentication:
      anonymous:
        enabled: false
      webhook:
        cacheTTL: 0s
        enabled: true
      x509:
        clientCAFile: /etc/kubernetes/pki/ca.crt
    authorization:
      mode: Webhook
      webhook:
        cacheAuthorizedTTL: 0s
        cacheUnauthorizedTTL: 0s
    cgroupDriver: systemd
    clusterDNS:
    - 10.96.0.10
    clusterDomain: cluster.local
    containerRuntimeEndpoint: ""
    cpuManagerReconcilePeriod: 0s
    crashLoopBackOff: {}
    evictionPressureTransitionPeriod: 0s
    fileCheckFrequency: 0s
    healthzBindAddress: 127.0.0.1
    healthzPort: 10248
    httpCheckFrequency: 0s
    imageMaximumGCAge: 0s
    imageMinimumGCAge: 0s
    kind: KubeletConfiguration
    logging:
      flushFrequency: 0
      options:
        json:
          infoBufferSize: "0"
        text:
          infoBufferSize: "0"
      verbosity: 0
    memorySwap: {}
    nodeStatusReportFrequency: 0s
    nodeStatusUpdateFrequency: 0s
    rotateCertificates: true
    runtimeRequestTimeout: 0s
    shutdownGracePeriod: 0s
    shutdownGracePeriodCriticalPods: 0s
    staticPodPath: /etc/kubernetes/manifests
    streamingConnectionIdleTimeout: 0s
    syncFrequency: 0s
    volumeStatsAggPeriod: 0s
kind: ConfigMap
metadata:
  annotations:
    kubeadm.kubernetes.io/component-config.hash: sha256:26488e9fc7c5cb5fdda9996cda2e6651a9af5febce07ea02de11bd3ef3f49e9c
  creationTimestamp: "2026-06-18T07:37:31Z"
  name: kubelet-config
  namespace: kube-system
  resourceVersion: "248"
  uid: a35432c8-33fd-4003-9b04-52e1463a8b3d

kubelet 驱逐时 Pod 的选择

节点内存不足行为

如果节点在 kubelet 能够回收内存之前遇到内存不足(OOM)事件, 则节点依赖 oom_killer 来响应。

kubelet 根据 Pod 的服务质量(QoS)为每个容器设置一个 oom_score_adj 值

服务质量 oom_score_adj (-1000,1000)

Guaranteed高优先级 -997

BestEffort低优先级 1000

Burstable中优先级 min(max(2, 1000 - (1000 * memoryRequestBytes) / machineMemoryCapacityBytes), 999)

说明:

kubelet 还将具有 `system-node-critical` 优先级(https://kubernetes.io/zhcn/docs/concepts/scheduling-eviction/pod-priority-preemption/#pod-priority) 的

Pod 中的容器 `oom_score_adj` 值设为 `-997`

如果 kubelet 在节点遇到 OOM 之前无法回收内存, 则 oom_killer 根据它在节点上使用的内存百分

比计算 oom_score , 然后加上 oom_score_adj 得到每个容器有效的 oom_score 。 然后它会杀死得

分最高的容器。

这意味着低 QoS Pod 中相对于其调度请求消耗内存较多的容器,将首先被杀死。

与 Pod 驱逐不同,如果容器被 OOM 杀死, kubelet 可以根据其 RestartPolicy 重新启动它

如果 kubelet 回收节点级资源的尝试没有使驱逐信号低于条件, 则 kubelet 开始驱逐最终用户 Pod。

kubelet 使用以下参数来确定 Pod 驱逐顺序:

    1. Pod 的资源使用是否超过其请求
    1. Pod 优先级
    1. Pod 相对于请求的资源使用情况

因此,kubelet 按以下顺序排列和驱逐 Pod:

    1. 首先考虑资源使用量超过其请求的 BestEffort 或 Burstable Pod。 这些 Pod 会根据它们的优先级以及它们的资源使用级别超过其请求的程度被逐出。
    1. 资源使用量少于请求量的 Guaranteed Pod 和 Burstable Pod 根据其优先级被最后驱逐。

仅当 Guaranteed Pod 中所有容器都被指定了请求和限制并且二者相等时,才保证 Pod 不被驱逐。 这

些 Pod 永远不会因为另一个 Pod 的资源消耗而被驱逐。 如果系统守护进程(例如 kubelet 和

journald ) 消耗的资源比通过 system-reserved 或 kube-reserved 分配保留的资源多, 并且该节

点只有 Guaranteed 或 Burstable Pod 使用的资源少于其上剩余的请求, 那么 kubelet 必须选择驱逐

这些 Pod 中的一个以保持节点稳定性并减少资源匮乏对其他 Pod 的影响。 在这种情况下,它会选择首

先驱逐最低优先级的 Pod。

当 kubelet 因 inode 或 PID 不足而驱逐 Pod 时, 它使用优先级来确定驱逐顺序,因为 inode 和 PID 没

有请求。

最小驱逐回收

在某些情况下,驱逐 Pod 只会回收少量的紧俏资源。 这可能导致 kubelet 反复达到配置的驱逐条件并触发多次驱逐。

你可以使用 --eviction-minimum-reclaim 标志或 kubelet 配置文件 为每个资源配置最小回收量。 当

kubelet 注意到某个资源耗尽时,它会继续回收该资源,直到回收到你所指定的数量为止。

例如,以下配置设置最小回收量:

#https://kubernetes.io/zh-cn/docs/tasks/administer-cluster/kubelet-config-file/

复制代码
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
evictionHard:
 memory.available: "500Mi"
 nodefs.available: "1Gi"
 imagefs.available: "100Gi"
evictionMinimumReclaim:
 memory.available: "0Mi"
 nodefs.available: "500Mi"
 imagefs.available: "2Gi"

#说明

在这个例子中,如果 `nodefs.available` 信号满足驱逐条件, kubelet 会回收资源,直到信号达到

`1Gi` 的条件, 然后继续回收至少 `500Mi` 直到信号达到 `1.5Gi`。

类似地,kubelet 会回收 `imagefs` 资源,直到 `imagefs.available` 信号达到 `102Gi`。