本系列教程目录(已发布):
图解 K8S(01):基于ubuntu 部署最新版 k8s 集群
图解 K8S(02):认识 K8S 中的资源对象
图解 K8S(03):从 Pause 容器理解 Pod 的本质
图解 K8S(04):吃透 Pod 中的第三类容器 – init 容器
图解 K8S(05):调度利器之标签与选择器(分组调度)
图解 K8S(06):调度利器之污点与容忍度(压力驱逐)
图解 K8S(07):调度利器之亲和与反亲和(服务容灾)
将一个 Pod 分配到某一个可以满足 Pod 资源请求的节点上,这一过程称之为调度。
理想情况下,你的集群中,有足够的资源能让你创建你期望的 Pod,如此一来,你就有理由不关心你的节点的资源还剩多少,有理由不关心 K8S 调度 Pod 的细节。
可事实上,你的集群资源是有限的,为了能让节点资源得到合理分配、有效利用,需要你对节点进行规划。
比如哪些机器是高性能的机器,哪些是普通机器,哪些是专用机器,尽量避免让普通的应用跑在高性能的机器上。
除此之外,有些应用,出于高可用的考虑,还需要应用部署多个副本,并分散开在不同的域里。
而关于这些内容,可以分成三个部分:
- 标签与选择器
- 污点与容忍度
- 亲和与反亲和
1. 通俗理解污点 打个比方,你去医院里看病,在医生的诊断之后,了解了你的病情后,准备给你开点药。
但开药也讲究对症下药,同样是发烧,给大人吃的药和给小孩子吃的药是不一样的。
因此为了防止滥用,药店会给不同的退烧药就设定适用范围(作用等同于 K8S 中的污点):
- 阿司匹林:适合成年人使用(做为退烧药,禁止儿童使用)
- 布洛芬:适合儿童使用
而根据患者是儿童,那么因此会把阿司匹林给排除掉,最后选择布洛芬。
这就是污点的作用,在本例中:
- 阿司匹林 和 布洛芬,即为 K8S 中的 Node
- 药品上的适用范围,就是打在药品(K8S 中 Node) 上的污点
- 而找药的需求,就是 K8S 中的 Pod
2. 污点与标签区别 污点与标签有什么不同呢?这是我们在学习污点时首先要搞清楚的问题。
由于标签和污点工作原理的不同,他们的适用场景也不一样。
标签通常用于为 Pod 指定分组,规定了 Pod 只能调度到这些分组里的 node 中,这是一种强制的做法。
污点通常用于将 Node 设置为专用节点,默认情况下普通 Pod 是无法调度过来,仅当你在 Pod 中指定了对应的容忍度才能调度。
3. 容忍度与污点 污点 也是打在 Node 上,可以理解为对外公开自己的“缺点”(并非真的缺点),想调度到我这边的,请明示说出你可以容忍我的缺点的,不然是调度不过来的。
使用如下命令为 worker01 打上污点,而 worker02 却没有任何污点
kubectl taint nodes worker02 gpu=true:NoSchedule
正常你创建的 Pod,没有进行特殊的配置,是无法调度到 worker02 的,只能调度到 worker01,即使你使用了 nodeSelector
文章图片
只有你在 Pod 上加上了如下的容忍度(在
.spec
下),才能有可能地创建到 worker01 上tolerations:
- key: "gpu"
operator: "Equal"
value: "true"
effect: "NoSchedule"
千万要注意的是,上面说的是有可能,而不是一定。
如果要标准地调度到 gpu 的机器上,还要配合前面的 2. 如何打标签?[^1]
- 标签:实现精度地调度的需求
- 污点:避免产生不必要地浪费
文章图片
上面节点上的污点的,我再翻译一下,意思是该机器上有 GPU,没有指定需要 GPU 的容忍度 的 Pod ,不能调度过来。
如果不使用 GPU 的 Pod 也创建到有 GPU 的节点上,那就是浪费资源,这是不能理解也不能允许的。
而后面的容忍度,意思是,我可以调度到有 gpu 的机器上,如果没有指定这个配置,就无法调度到 GPU 的机器上。
如果要删除原有的污点,可以在上面添加污点的命令最后加个减号
-
kubectl taint nodes worker02 gpu=true:NoSchedule-
4. 容忍度的配置 容忍度,由几个关键字段组成:
- key:键(必填)
- value:值,当 operator 为 Equal ,value 必填,当 operator 为 Exists ,value 就不用填写
- operator:操作,可以为 Exists (存在即可匹配) 或者 Equal (value 必须与相等才算匹配)
- effect:影响,有三个选项:NoSchedule、PreferNoSchedule、NoExecute
简单来说,一个 Node 上可以设置多个污点,一个 Pod 也可以设置多个容忍度。
Kubernetes 处理多个污点和容忍度的过程就像一个过滤器:从一个节点的所有污点开始遍历, 过滤掉那些 Pod 中存在与之相匹配的容忍度的污点。余下未被过滤的污点的 effect 值决定了 Pod 是否会被分配到该节点,特别是以下情况:
- 如果未被过滤的污点中存在至少一个 effect 值为
NoSchedule
的污点, 则 Kubernetes 不会将 Pod 分配到该节点。 - 如果未被过滤的污点中不存在 effect 值为
NoSchedule
的污点, 但是存在 effect 值为PreferNoSchedule
的污点, 则 Kubernetes 会 尝试 不将 Pod 分配到该节点。 - 如果未被过滤的污点中存在至少一个 effect 值为
NoExecute
的污点, 则 Kubernetes 不会将 Pod 分配到该节点(如果 Pod 还未在节点上运行), 或者将 Pod 从该节点驱逐(如果 Pod 已经在节点上运行)。
在 kubernetes 的每个集群节点上,都有一个 kubelet 服务,它会监控集群节点的 CPU、内存、磁盘空间和文件系统的 inode 等资源。
当这些资源中的一个或者多个达到特定的消耗水平, kubelet 会主动给节点打上一个或者多个污点标记,这些标记的 effect 为 NoExecute
比如内存比较紧张的话,会打上
node.kubernetes.io/memory-pressure
比如磁盘比较紧张的话,会打上
node.kubernetes.io/disk-pressure
比如 pid 比较紧张的话,会打上
node.kubernetes.io/pid-pressure
而如果该节点上,已有一些 Pod 在运行,并且这些 Pod 没有配置以上三种对应的容忍度,则 kubelet 会开始驱逐的流程,一个一个的驱逐,直到节点不再有存在资源压力为止,才会清除污点,结束驱逐。
通常还会带上一个
tolerationSeconds
,它意思是在污点出现后,Pod 还可以正常工作多少时间,也就是延迟多久再进行驱逐。除了以上污点之外,还有其他常见的
node.kubernetes.io/not-ready
:节点未准备好。这相当于节点状态Ready
的值为 “False
”node.kubernetes.io/unreachable
:节点控制器访问不到节点. 这相当于节点状态 Ready 的值为 “Unknown”。node.kubernetes.io/network-unavailable
:节点网络不可用。node.kubernetes.io/unschedulable
: 节点不可调度。
6. 污点的进阶开发 污点的原理上面已经剖析得差不多了,在实际工作中,它被广泛应用于实现节点的专有专用。
但要实现节点的专有专用,还要有标签与选择器(nodeSelector)的配合才可以。
因此,你想要将 Pod 调度到专用节点上,你要添加 容忍度的配置,还要添加 nodeSelector 的配置。
那有没有办法,将这两个步骤,再简化成一个步骤呢?
K8S 中有一个 准入控制器 的概念,它可以理解为一个定义在 api-server 组件中的插件,当你在对对象进行操作时,这些插件可以拦截 api 的请求,并进行一些操作,
根据操作的不同,这类准入插件可以分为两类:
- MutatingAdmissionWebhook:可以变更对象的配置
- ValidatingAdmissionWebhook:可以验证对象
文章图片
如果任何一个阶段的任何控制器拒绝了该请求,则整个请求将立即被拒绝,并向终端用户返回一个错误。
而 MutatingAdmissionWebhook 可以变量对象的配置,这不正是我们所需求的吗?
我们可以自定义一个MutatingAdmissionWebhook ,当检查到 Pod 有如下的容忍度时
tolerations:
- key: "dedicated"
operator: "Equal"
value: "gpu"
effect: "NoSchedule"
就自动往 Pod 中添加如下的选择器配置,当然如果原有的 Pod 已经有了该段配置,就可以直接覆盖或跳过。
nodeSelect:
gpu: true
自定义的准入控制器,其实也不难,Kubernetes 其实本身自带了非常多地准入控制器,可以模仿一下,写起来并不麻烦,具体的代码在:src/k8s.io/kubernetes/plugin/pkg/admission/
要注意的是,有些准入控制器,即是MutatingAdmissionWebhook 也是 ValidatingAdmissionWebhook。
下边我挑选一个 Kubernetes 自带的准入控制器,带你了解一下 MutatingAdmissionWebhook 和 ValidatingAdmissionWebhook 是怎样工作的。
7. PodNodeSelector 创建一个全新的 namespace,名字叫 iswbm
kubectl create namespace iswbm
然后再使用 kubectl edit 命令,在该 namespacce 上添加 annotation 时(或者也可以通过在 apiserver 上指定对应的配置文件)
apiVersion: v1
kind: Namespace
metadata:
annotations:
scheduler.alpha.kubernetes.io/node-selector: env=test
name: iswbm
有了该 annotation 后,在该 namespace 下创建的 Pod 都只能创建到有 env=test 标签的 Node 上 – 这是 MutatingAdmissionWebhook 的部分
若 Pod 自己的 nodeSelector 和 PodNodeSelector 做完交集后,没有一个 node 满足条件,则会直接拒绝 – 这是 ValidatingAdmissionWebhook 的部分
这个实现的方式就是通过 PodNodeSelector 这个准入控制器,自动给该 namespace 下的 Pod 加上 nodeSelector 。
【大白话|图解 K8S(06)(调度利器之污点与容忍度(压力驱逐))】以上就是关于污点和容忍度的有关内容,下一篇我们讲讲亲和和反亲和。
推荐阅读
- 图解|图解 K8S(07)(调度利器之亲和与反亲和(服务容灾))
- kubernetes安装
- kubernetes与velero的第一次尝试
- 加速 Kubernetes 镜像拉取
- kubernetes|2021超全整理,128道kubenetes高频面试题汇总(带答案)
- Kubernetes|Kubernetes 中的服务发现与负载均衡
- Kubernetes|k8s 1.20.2 coredns解析问题
- 约战蓝桥|蓝桥杯十大常见天阶功法——音之呼吸.肆之型.模拟
- kubernetes|Kubernetes(k8s)---(1)集群部署