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

生产环境中的kubernetes 优先级与抢占的更多相关文章

  1. Kubernetes 在生产环境中常用架构

    Kubernetes 在生产环境中常用架构 首先,我们来梳理下Kubernetes生产架构,其设计适用于绝大多数环境.如下图所示 在该架构中,我们可以将其分为四层,如下: Client层:即Kuber ...

  2. Kubernetes用户指南(三)--在生产环境中使用Pod来工作、管理部署

    一.在生产环境中使用Pod来工作 本节将介绍一些在生产环境中运行应用非常有用的功能. 1.持久化存储 容器的文件系统只有当容器正常运行时有效,一旦容器奔溃或者重启,所有对文件系统的修改将会丢失,从一个 ...

  3. 生产环境中使用Docker Swarm的一些建议

    译者按: 实践中会发现,生产环境中使用单个Docker节点是远远不够的,搭建Docker集群势在必行.然而,面对Kubernetes, Mesos以及Swarm等众多容器集群系统,我们该如何选择呢?它 ...

  4. Dubbo Mesh 在闲鱼生产环境中的落地实践

    本文作者至简曾在 2018 QCon 上海站以<Service Mesh 的本质.价值和应用探索>为题做了一次分享,其中谈到了 Dubbo Mesh 的整体发展思路是“借力开源.反哺开源” ...

  5. 明白生产环境中的jvm参数

    明白生产环境中的jvm参数 写代码的时候,程序写完了,发到线上去运行,跑一段时间后,程序变慢了,cpu负载高了--一堆问题出来了,所以了解一下生产环境的机器上的jvm配置是有必要的.比如说: JDK版 ...

  6. .NET跨平台之旅:在生产环境中上线第一个运行于Linux上的ASP.NET Core站点

    2016年7月10日,我们在生产环境中上线了第一个运行于Linux上的ASP.NET Core站点,这是一个简单的提供后端服务的ASP.NET Core Web API站点. 项目是在Windows上 ...

  7. 理解Docker(6):若干企业生产环境中的容器网络方案

    本系列文章将介绍 Docker的相关知识: (1)Docker 安装及基本用法 (2)Docker 镜像 (3)Docker 容器的隔离性 - 使用 Linux namespace 隔离容器的运行环境 ...

  8. .NET跨平台之旅:生产环境中第2个跑在Linux上的ASP.NET Core站点

    今天我们在生产环境中上线了第2个跑在Linux上的ASP.NET Core站点.这是一个简单的Web API站点,通过命令行的方式调用安装在Linux服务器上的程序完成操作.之前用的是nodejs,现 ...

  9. 【原】Storm Local模式和生产环境中Topology运行配置

    Storm入门教程 1. Storm基础 Storm Storm主要特点 Storm基本概念 Storm调度器 Storm配置 Guaranteeing Message Processing(消息处理 ...

随机推荐

  1. Linux Shell 基础知识(一)

    1. 本文知识结构 2. shell 基础知识 2.1 shell 简单介绍 ​ GNU bash shell 能提供对 Linux 系统的交互式访问,一般来说,使用快捷键 Ctrl + Alt + ...

  2. s-s-r + 锐-速

    环境:centos 7 1. wget --no-check-certificate https://freed.ga/github/shadowsocksR.sh; bash shadowsocks ...

  3. 二分练习题2 查找大于等于x的最小元素 题解

    题目描述 现在告诉你一个长度为 \(n\) 的有序数组 \(a_1, a_2, ..., a_n\) ,以及 \(q\) 次询问,每次询问会给你一个数 \(x\) ,对于每次询问,你需要输出数组 \( ...

  4. 使用CoordinatorLayout打造各种炫酷的效果

    使用CoordinatorLayout打造各种炫酷的效果 自定义Behavior -- 仿知乎,FloatActionButton隐藏与展示 NestedScrolling 机制深入解析 一步步带你读 ...

  5. wepy框架开发小程序遇到的node-sass问题解决方案

    一.报错图 二.解决方案 主要是windows平台缺少编译环境, 1.先运行: npm install -g node-gyp 2.然后运行cmd:(右键点击:以管理员身份运行) 输入:npm ins ...

  6. 第九周java动手动脑

    1.使用Files. walkFileTree()找出指定文件夹下所有扩展名为.txt和.java的文件. import java.io.IOException; import java.nio.fi ...

  7. Scrapy项目 - 实现斗鱼直播网站信息爬取的爬虫设计

    要求编写的程序可爬取斗鱼直播网站上的直播信息,如:房间数,直播类别和人气等.熟悉掌握基本的网页和url分析,同时能灵活使用Xmind工具对Python爬虫程序(网络爬虫)流程图进行分析.   一.项目 ...

  8. zookeeper特性与节点说明

    一.zookeeper概要.背景及作用 zookeeper产生背景: 项目从单体到分布式转变之后,将会产生多个节点之间协同的问题.如: 每天的定时任务由谁哪个节点来执行? RPC调用时的服务发现? 如 ...

  9. 规则引擎 - drools 使用讲解(简单版) - Java

    drools规则引擎 项目链接 现状: 运维同学(各种同学)通过后台管理界面直接配置相关规则,这里是通过输入框.下拉框等完成输入的,非常简单: 规则配置完毕后,前端请求后端,此时服务端根据参数(即规则 ...

  10. redis-分布式锁-刷新信号量

    为什么需要刷新信号量呢,因为信号量有过期时间: 为什么信号量需要过期时间呢,因为需要利用超时特性,解决分布式锁存在的一些固有缺陷. 而对于类似流式API来说,一般10秒的过期时间是远远不够的.因此我们 ...