k8s的调度机制

Kubernetes的资源模型与资源管理

所有跟调度和资源管理的属性都是属于pod对象的字段,其中最重要的部分,就是pod的cpu和内存配置,如下所示

  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: frontend
  5. spec:
  6. containers:
  7. - name: db
  8. image: mysql
  9. env:
  10. - name: MYSQL_ROOT_PASSWORD
  11. value: "password"
  12. resources:
  13. requests:
  14. memory: "64Mi"
  15. cpu: "250m"
  16. limits:
  17. memory: "128Mi"
  18. cpu: "500m"
  19. - name: wp
  20. image: wordpress
  21. resources:
  22. requests:
  23. memory: "64Mi"
  24. cpu: "250m"
  25. limits:
  26. memory: "128Mi"
  27. cpu: "500m"

在Kubernetes中:

  • cpu称为“可压缩资源”,可压缩资源不足时,pod只会“饥饿”,不会退出
  • 内存成为“不可压缩资源”,不可压缩资源不足时,pod会因为oom(out-of-memory)被内核kill掉
  • pod可以由多个container组成,所以cpu和内存资源的限额,是要配置在每个container的定义上的,而pod整体资源配置,就由这些container的配置累加得到

limits和requests的区别很简单,在调度的时候,kube-scheduler只会按照requests的值进行计算。而在真正设置Cgroups限制的时候,kubelet则会按照limits的值来进行设置
requests+limits的做法,其实是Borg的思想,即:实际场景中,大多数作业使用到的资源其实远小于它所请求的资源限额,基于这种假设,用户可以声明一个相对小的requests值供调度器使用,而kubernetes真正设置给容器Cgroups的,则是相对较大的limtis值。

QoS模型

三个级别,衔接上面

  • Guaranteed:pod里每个container都同时设置了requests和limits,并且requests和limits的值相等的时候,这个pod就属于Guaranteed级别

    apiVersion: v1
    kind: Pod
    metadata:
      name: qos-demo
    namespace: qos-example
    spec:
      containers:
        - name: qos-demo-ctr
          image: nginx
        resources:
            limits:
              memory: "200Mi"
            cpu: "700m"
          requests:
              memory: "200Mi"
            cpu: "700m"
    
  • Burstable:当pod不满足Guaranteed的条件,但至少有一个Contianer设置了requests。那么这个pod就会被划分到Burstable类别

    apiVersion: v1
    kind: Pod
    metadata:
      name: qos-demo2
    namespace: qos-example
    spec:
      containers:
        - name: qos-demo-2-ctr
          image: nginx
        resources:
            limits:
              memory: "200Mi"
          requests:
              memory: "100Mi"
    
  • BestEffort:如果pod既没有设置requests,也没有设置limits,那么它的QoS类别就是BestEffort

    apiVersion: v1
    kind: Pod
    metadata:
      name: qos-demo3
    namespace: qos-example
    spec:
      containers:
        - name: qos-demo-3-ctr
          image: nginx
    

    QoS划分的主要应用场景,是当宿主机资源紧张的时候,kubelet对pod进行Eviction(即资源回收)时需要用到的。当Eviction发生时,kublet具体会挑Pod进行删除操作,按如下级别:
    BestEffort并且,Kubernetes会保证只有当Guaranteed类别的Pod的资源使用量超过了其Limits的限制,或者宿主机本身正处于Memory Pressure状态时,Guaranteed的Pod才可能被选中进行Eviction操作

cpuset的设置

一个实际生产中非常有用的特性,衔接上面
在使用容器时,可以通过设置cpuset把容器绑定到某个cpu的核上,而不是像cpushare那样共享cpu的计算能力,这样cpu之间进行上下文切换的次数大大减小,容器里应用的性能会得到大幅提升
实现方法

  • Pod必须是Guaranteed的QoS类型
  • 将Pod的CPU资源的requests和limits设置为一个相等的数值
  • 如下例子:
    spec:
      containers:
    - name: nginx
        image: nginx
      resources:
          limits:
            memory: "200Mi"
          cpu: "2"
        requests:
            memory: "200Mi"
          cpu: "2"
    
    这样,该Pod就会绑定在2个独占的cpu核上,具体是哪两个cpu,由kubelet分配

基于上面的情况,建议将DaemonSet(亦或者类似的)的Pod都设置为Guaranteed的QoS模型,否则一旦因为资源紧张被回收,又立即会在宿主机上重建出来,这样资源回收的动作就没有意义了。

Kubernetes默认的调度策略

image.png

调度机制的工作原理示意图

默认的几种调度策略

  1. GeneralPredicates:这一组过滤规则,负责的是最基础的调度策略,计算的就是宿主机的CPU和内存资源是否够用
  2. Volume相关的顾虑规则:负责的是跟容器持久化Volume相关的调度策略
  3. 宿主机相关的过滤规则:这一组规则,主要考察待调度Pod是否满足Node本身的某些条件。比如PodToleratesNodeTaints,负责检查的就是我们前面常用到的Node的“污点”机制。
  4. Pod相关的过滤规则:这一组规则,跟GeneralPredicates大多数是重合的。其中比较特殊的是PodAffinityPredicate。这个规则的作用,是检查待调度Pod与Node上的已有Pod之间的亲密(affinity)和反亲密(anti-affinity)关系

在具体执行的时候,当开始调度一个Pod时,Kubernetes调度器会同时启动16个Goroutine,来并发地为集群里所有Node计算Predicates,最后返回可以开始运行这个Pod的宿主机列表。
在Predicates阶段完成了节点的“过滤之后”,Priorities阶段的工作就是为这些节点打分。这里打分的范围是0-10分,得分最高的节点就是最后被Pod绑定的最佳节点。
Priorities里最常用的一个打分规则,是LeastRequestedPriority。它的计算方法,可以简单地总结为如下所示的公式

score = (cpu((capaticy-sum(requested))10/capacity) +memory((capacit-sum(requested))10/capacity))/2

这个算法实际上就是在选择空闲资源(CPU和Memory)最多的宿主机
与LeastReuqestedPriority一起发挥作用的,还有BalancedResourceAllocation。它的计算公式如下所示

score = 10 - variance(capuFraction,memoryFraction,volumeFraction)*10

每种资源的Fraction的定义是Pod请求的资源/节点上可用的资源。而variance算法的作用,则是计算每两种资源Fraction之间的“距离”。而最后选择的,则是资源Fraction差距最小的节点。也就是调度完成后,所有节点里各种资源分配最均衡的那个节点,从而避免一个节点上CPU被大量分配、而Memory大量剩余的情况
此外,还有NodeAffinityPriority、TaintTolerationPriority和InterPodAffinityPriority这三种Priority。除了默认的调度策略,还有很多默认不会开启的策略,可以通过为kube-scheduler指定一个配置文件或者创建一个configMap,来配置哪些规则需要开启、哪些规则需要关闭。并且,还可以为Priorities设置权重,来控制调度器的调度行为

调度器的优先级与强制机制