ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

生产环境中的kubernetes 优先级与抢占

2019-10-21 22:52:23  阅读:336  来源: 互联网

标签:high 抢占 kubernetes priority 优先级 pod PriorityClass


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默认开启。
  • 按照集群规划创建对应的PriorityClass及其对应的抢占策略,目前支持两种策略: Never, PreemptLowerPriority。 Never可以指定不抢占其他pod, 即使该pod优先级特别高,这对于一些离线任务较为友好。 非抢占调度在v1.15中为alpha, 需要通过--feature-gates=NonPreemptingPriority=true 进行开启。
  • 在创建好了PriorityClass之后,需要防止高优先级的pod过分占用太多资源,使用resourceQuota机制来限制其使用量,避免低优先级的pod总是被高优先级的pod压制,造成资源饥饿。resoueceQuote可以通过指定scope为PriorityClass来限定某个优先级能使用的资源量:
apiVersion: v1
kind: ResourceQuota
metadata:
  name: high-priority
spec:
  hard:
    pods: "10"
  scopeSelector:
    matchExpressions:
    - operator : In
      scopeName: PriorityClass
      values: ["high"]

如上即为限制高优先级的pod最多能创建10个。operator指定了作用的对象,operator: In可以显式指定作用于的哪些priorityClass,operator: Exists则指定作用于该namespace下的所有priorityClass。

  • 有时候我们想要只有priorityClass对应的resourceQuota存在之后才能创建pod,确保所有的priorityClass的pod资源都是受控的。 如果那个namespacew没有该resourceQuota则拒绝该pod的创建,该操作可以通过指定--admission-control-config-file文件来设置,内容如下:
apiVersion: apiserver.k8s.io/v1alpha1
kind: AdmissionConfiguration
plugins:
- name: "ResourceQuota"
  configuration:
    apiVersion: resourcequota.admission.k8s.io/v1beta1
    kind: Configuration
    limitedResources:
    - resource: pods
      matchScopes:
      - scopeName: PriorityClass 
        operator: In
        values: ["high"]

该配置定义了: "high"优先级的pod只能在指定的namespaces下创建,该namespaces有作用于"high"优先级的resouceQuota,上面第四步中的resouceQuota即可满足要求。
scopeName定义了作用的对象为priorityClass, operator指定了作用的范围,可以是In操作符,指定某几个value, 也可以是Exits操作符,指定所有的PriorityClass必须有对应的quota存在, 否则该namespace就无法创建该优先级的pod,这些操作符与上面resouceQuota中定义的一一对应。通过这样就限制了一些优先级只能在有资源约束的namespace下创建。

  • 如果没有显式指定优先级,则默认的优先级值为0,需要结合业务规划决定是否有必要调整默认优先级。
  • 对于一些daemonset需要显式设置较高的优先级来防止被抢占,在部署一个新的daemonset的时候需要考虑是否会造成大规模pod的抢占。
  • 等到所有的优先级设置完毕之后就可以开启抢占功能了,此时集群中所有pending 的高优先级pod就会瞬间抢占,还是需要额外小心,确保集群中高优先级的pod不会导致低优先级的pod大规模被kill,如果我们提前设置了对应的resource quota值,则会有一定的资源约束。
  • 优先级和抢占对于资源的精细化运营考验很大,对于resource quota的设置需要十分精细,需要考虑两个维度来设置: namespace层面和priority层面,我们既希望限制namespace使用的资源,有希望某个priority使用的资源,防止低优先级的pod资源饥饿。 可以在初期只考虑namespace层面的限制,priority层面通过上层业务来保证,例如创建任务的时候保证集群中高优先级的资源使用量不超过50%等。

其他思考

笔者的线上环境中, 有些再跑的模型训练任务业务对于自动failove实现不是很好,如果中途被抢占了只能从头开始计算,需要占用额外的GPU资源,这种工作类型不允许被抢占,但是如果把他设置为高优先级又不太合适,因为它确实不是最高的优先级,优先级最高的还是在线业务,不能让它抢占在线业务。 它属于中间优先级,可以抢占低优先级的pod。 经过探索发现目前kubernetes并不支持该中类型,当前支持的抢占策略为: Never, PreemptLowerPriority都无法满足需求。所以在此基础上开发了NonPreemptible类型的抢占策略,该优先级的pod是不允许被其他人抢占的,调度还是按照优先级在队列里排队,但是一旦调度上去就无法被抢占。
这种抢占策略略显"霸道",所以需要谨慎使用,设置resouceQuota,并且只能由特定的任务使用。 并且为了不影响deamonset等优先级最高的任务,允许被某个指定Priority数值之上的pod抢占。并且随着业务的发展,这部分逻辑需要逐步去掉,之所有存在这部分逻辑是因为pod不能被中断,不能被抢占,所以还是需要使这些任务支持重启与挂起,具体来说就是: pod挂载远程磁盘并自动checkpoint,重启之后从以前的恢复点继续执行。等这些业务改造完成之后,逐步去掉这种工作抢占策略

reference

Pod Priority and Preemption
Priority in ResourceQuota
Allow PriorityClasses To Be Non-Preempting

标签:high,抢占,kubernetes,priority,优先级,pod,PriorityClass
来源: https://www.cnblogs.com/gaorong/p/11318328.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有