kubernetes 中的抢占功能是调度器比较重要的feature,但是真正使用起来还是比较危险,否则很容易把低优先级的pod给无辜kill。为了提高GPU集群的资源利用率,决定勇于尝试一番该featrue。当然使用之前还是得阅读一下相关的代码做到心里有数,出了问题也方便定位修复。

基本原理

优先级与抢占是为了确保一个高优先级的pod在调度失败后,可以通过"挤走" 低优先级的pod,腾出空间后保证它可以调度成功。 我们首先需要在集群中声明PriorityClass来定义优先等级数值和抢占策略,

apiVersion: scheduling.k8s.io/v1 kind: PriorityClass metadata:   name: high value: 10000 preemptionPolicy: Never globalDefault: false description: "This priority class should be used for high priority service pods." --- apiVersion: scheduling.k8s.io/v1 kind: PriorityClass metadata:   name: low value: -999 globalDefault: false description: "This priority class should be used for log priority service pods."

如上所示定义了两个PriorityClass对象。然后就可以在pod中声明使用它了:

apiVersion: apps/v1 kind: Deployment metadata:   labels:     run: nginx   name: high-nginx spec:   replicas: 1   selector:     matchLabels:       run: nginx   template:     metadata:       labels:         run: nginx     spec:       containers:       - image: nginx         imagePullPolicy: Always         name: nginx         resources:           limits:             cpu: "500m"       priorityClassName: high

这个 Pod 通过 priorityClassName 字段。声明了要使用名叫 high-priority 的 PriorityClass。当这个 Pod 被提交给 Kubernetes 之后,Kubernetes 的 Priority AdmissionController 就会自动将这个 Pod 的spec.priority 字段设置为10000。
如下:

  preemptionPolicy: Never   priority: 10000   priorityClassName: high

Pod创建好之后,调度器就会根据创建的priority进行调度的决策,首先会在等待队列中优先调度,如果调度失败就会进行抢占: 依次遍历所有的node找出最适合的node,将该nodename填充在spec.nominatedNodeName字段上,然后等待被抢占的pod全都退出后再次尝试调度到该node之上。具体的逻辑请自行阅读相关代码,此处不在赘述。

生产环境使用方式

  • v1.14版本的kubernetes该feature已经GA,默认开启,但此时我们往往没有做好准备,如果直接给pod设置优先级会导致很多意料之外的抢占,造成故障。 (参见How a Production Outage Was Caused Using Kubernetes Pod Priorities)。所以建议在初次使用的时候还是先显式关闭抢占,只设置优先级,等集群中所有的pod都有了各自的优先级之后再开启,此时所有的抢占都是可预期的。可以通过kube-scheduler 配置文件中的disablePreemption: true进行关闭
  • 调度器是根据优先级pod.spec.priority数值来决定优先级的,而用户是通过指定pod.sepc.priorityclass的名字来为pod选择优先级的,此时就需要Priority AdmissionController根据priorityclass name为pod自动转换并设置对应的priority数值。我们需要确保该admissionController开启,如果你的kube-apiserver中还是通过--admission-control flag来指定admissionoController的话需要手动添加Priority admissonController,如果是通过--enable-admission-plugins来指定的话,无需操作,该admissionController默认开启。
  • 按照集群规划创建对应的