Kubernetes Pod调度 Node亲和调度
Kubernetes Pod调度说明简介Scheduler 是 Kubernetes 的调度器,主要任务是把定义的Pod分配到集群的节点上,听起来非常简单,但要考虑需要方面的问题:公平:如何保证每个节点都能被分配到资源资源高效利用:集群所有资源最大化被使用效率:调度性能要好,能够尽快的对大批量的Pod完成调度工作灵活:允许用户根据自己的需求控制调度的流程Scheduler 是作为单独的服务运行的,
Kubernetes Pod调度说明
简介
Scheduler 是 Kubernetes 的调度器,主要任务是把定义的Pod分配到集群的节点上,听起来非常简单,但要考虑需要方面的问题:
-
公平:如何保证每个节点都能被分配到资源
-
资源高效利用:集群所有资源最大化被使用
-
效率:调度性能要好,能够尽快的对大批量的Pod完成调度工作
-
灵活:允许用户根据自己的需求控制调度的流程
Scheduler 是作为单独的服务运行的,启动之后会一直监听API Server,获取 podSpec.NodeName为空的Pod,对每个Pod都会创建一个binding,表明该Pod应该放在哪个节点上。
调度过程
调度流程:
- 首先过滤掉不满足条件的节点,这个过程称为predicate
- 然后对通过的节点按照优先级的顺序,这个是priority
- 最后从中选择优先级最高的节点。如果中间有任何一步报错,则直接返回错误信息。(对于调度过程分为两部分,一部分是预选,一部分是优选)
Predicate有一系列的算法可以使用:
-
PodFitsResources:节点上剩余的资源是否大于 Pod 请求的资源
-
PodFitsHost:如果Pod指定了nodeName,检查节点名称是否和nodeName匹配
-
PodFitsHostPort:节点上已经使用的port是否和Pod申请的port冲突
-
PodSelectorMatches:过滤和Pod指定的 label 不匹配的节点
-
NoDiskConflict:已经 mount 的 volume 和 Pod 指定的volume不冲突,除非他们都是只读
如果在predicate过程中没有适合的节点,Pod会一直处于Pending状态,不断重新调度,直到有节点满足条件,经过这个步骤,如果多个节点满足条件,就会进入priority过程:按照优先级大小对节点排序,优先级由一系列键值对组成,键是该优先级的名称,值是它的权重,这些优先级选项包括:
-
LeastRequestedPriority:通过计算CPU和Memory的使用率来决定权重,使用率越低权重越高,换句话说,这个优先级倾向于资源使用率低的节点
-
BalanceResourceAllocation:节点上CPU和Memory使用率非常及接近,权重就越高,这个要和上边的一起使用,不可单独使用
-
ImageLocalityPriority:倾向于已经要使用镜像的节点,镜像的总大小值越大,权重越高
通过算法对所有的优先级项目和权重进行计算,得出最终的结果
自定义调度器
除了Kubernetes自带的调度器,也可以编写自己的调度器,通过spec.schedulername参数指定调度器的名字,可以为Pod选择某个调度器进行调度,比如下边的Pod选择my-scheduler进行调度,而不是默认的default-scheduler。
apiVersion: v1
kind: Pod
metadata:
name: scheduler-test
labels:
name: example-scheduler
spec:
schedulername: my-scheduler
containers:
- name: Pod-test
image: nginx:v1
如何满足 Pod 与 Node 关系调度
Pod 与 Node 的关系调度又称之为 Node 亲和调度,主要给大家介绍两类使用方法。
NodeSelector
第一类是 NodeSelector,这是一类相对比较简单的玩法。比如说有个场景:必须要调度 Pod 到带了 k1: v1 标签的 Node 上,这时可以在 Pod 的 spec 中填写一个 nodeSelector 要求。
nodeSelector 其实是一个 map 结构,里面可以直接写上对 node 标签的要求,比如 k1: v1。这样我的 Pod 就会强制调度到带了 k1: v1 标签的 Node 上。
NodeAffinity
NodeSelector 是一个非常简单的玩法,但这个玩法有个问题:它是一个常规性调度,假如我想优先调度,就没法用 nodeSelector 来做。于是 Kubernetes 社区又新加了一个玩法,叫做 NodeAffinity。
它和 PodAffinity 有点类似,也提供了两类调度的策略:
- 第一类是 required,必须调度到某一类 Node 上;
- 第二类是 preferred,就是优先调度到某一类 Node 上。
它的基本语法和上文中的 PodAffinity 以及 PodAntiAffinity 也是类似的。在 Operator 上,NodeAffinity 提供了比 PodAffinity 更丰富的 Operator 内容。增加了 Gt 和 Lt,数值比较的玩法。当使用 Gt 的时候,values 只能填写数字。
调度亲和性 节点(Node)亲和性
之前的nodeselector只能是等于某个标签,同时如果标签不匹配就会处于pending状态,这就需要软性的条件,上面是两块都加上了意味着pod只能放置在标签具有gpu的节点上,并且值为nvdlia-tesla,另外更加倾向于标签group的节点
这里的权重值代表更加期望。
pod.spec.affinity.nodeAffinity
-
preferredDuringSchedulingIgnoredDuringExecution:软策略
-
软策略是偏向于,更想落在某个节点上,但如果实在没有,落在其他节点也可以
-
-
requiredDuringSchedulingIgnoredDuringExecution:硬策略
-
硬策略是必须落在指定的节点上,如果不符合条件,则一直处于Pending状态
-
如果你还不太理解打个比方:
今天要分班了,我更加倾向于去张三老师的班级。这就是所谓的亲和性。如果我是Pod,张三老师所在的班级为Node,这就是所谓的节点亲和性。一种是必须要去张三老师带的班级(硬状态),还有一种是想去张三老师的班级但是不是必须的(软状态)。这就是一个软和硬的状态。软策略是想去,但是不去也可以,硬策略是非去不可。
requiredDuringSchedulingIgnoredDuringExecution硬策略
[root@k8s-master ~]# cat node-affinity-required.yaml
apiVersion: v1
kind: Pod
metadata:
name: affinity
labels:
app: node-affinity-pod
spec:
containers:
- name: with-node-affinity
image: nginx
imagePullPolicy: IfNotPresent
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname #键为节点名称
operator: NotIn #不是
values:
- k8s-master #node节点名称
- key: kubernetes.io/hostname #键为节点名称
operator: NotIn #不是
values:
- k8s-node1 #node节点名称
[root@k8s-master ~]# kubectl apply -f node-affinity-required.yaml
pod/affinity created、
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution: #硬亲和
nodeSelectorTerms:
- matchExpressions:
- key: gpu
operator: Exists
以上策略表示:此Pod不要落在node名称为k8s-master和k8s-node1的节点上。
可以看到每个节点上都有一个kubernetes.io/hostname=xxxx,这里就是典型的标签的键值和键值
[root@k8s-master ~]# kubectl get node --show-labels
NAME STATUS ROLES AGE VERSION LABELS
k8s-master Ready <none> 48d v1.18.3 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-master,kubernetes.io/os=linux
k8s-node1 Ready <none> 48d v1.18.3 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node1,kubernetes.io/os=linux
k8s-node2 Ready <none> 48d v1.18.3 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node2,kubernetes.io/os=linux
[root@k8s-master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
affinity 1/1 Running 0 3m46s 10.244.2.6 k8s-node2 <none>
可以看到这就是节点的硬策略,硬亲和性。
将yaml文件中,NotIn改为In
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- k8s-node3
[root@k8s-master ~]# kubectl apply -f node-affinity-required.yaml
pod/affinity created
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
affinity 0/1 Pending 0 14s
[root@k8s-master ~]# kubectl describe pod affinity
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling <unknown> default-scheduler 0/3 nodes are available: 3 node(s) didn't match node selector.
Warning FailedScheduling <unknown> default-scheduler 0/3 nodes are available: 3 node(s) didn't match node selector.
目前只有三个节点,一个master 一个node1一个node2,策略中表示此Pod在node3这个节点上,Pod创建之后,因为并不存在node3节点,所以一直处于Pending状态(因为这是一个硬策略,如果不满足就不运行了)。
preferredDuringSchedulingIgnoredDuringExecution软策略
[root@k8s-master ~]# cat node-affinity-preferred.yaml
apiVersion: v1
kind: Pod
metadata:
name: affinity-preferred
labels:
app: node-affinity-pod
spec:
containers:
- name: with-node-preferred
image: nginx
imagePullPolicy: IfNotPresent
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100 #权重为100,软策略中权重越高匹配到的机会越大
preference: #更偏向于
matchExpressions:
- key: kubernetes.io/hostname #node名称
operator: In #等于,为
values:
- k8s-node1 #node真实名称
[root@k8s-master ~]# kubectl apply -f node-affinity-preferred.yaml
pod/affinity-preferred created
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution: #软亲和
- weight: 60
preference:
matchExpressions: #带gpu标签的加60权重
- key: gpu
operator: Exists
- weight: 30
preference:
matchExpressions: #包含foo、bar标签的加30权重
- key: region
operator: In
values: ["foo","bar"]
- weight: 100这里给了权重,如果这里有多个软策略的话,软策略1和小红坐一起,那么这个权重为4 。策略2想和小丽做一起的权重为1。当有小红的情况下和小红坐一起的权重更高,所以选择小红,权重更大更加亲和,如果小红不在那么就选择小丽。
以上策略表示:此Pod更想落在node节点名称为k8s-node1的node中。
[root@k8s-master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
affinity-preferred 1/1 Running 0 55s 10.244.1.6 k8s-node1 <none> <none>
以上正常落在node1节点下。
更改一下策略,将node节点名称随便更改为不存在的node名称,例如k8s-node3
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- k8s-node3
[root@k8s-master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
affinity-preferred 1/1 Running 0 4m25s 10.244.0.17 k8s-master <none> <none>
[root@k8s-master ~]# kubectl describe pod affinity-preferred
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled <unknown> default-scheduler Successfully assigned default/affinity-preferred to k8s-master
Normal Pulling 2m kubelet, k8s-master Pulling image "nginx"
Normal Pulled 82s kubelet, k8s-master Successfully pulled image "nginx"
Normal Created 82s kubelet, k8s-master Created container with-node-preferred
Normal Started 82s kubelet, k8s-master Started container with-node-preferred
创建后,虽然它更想落在node3节点上,但因为没有这个节点,只好落在master节点中(软就是有我就去,没有也行,硬就是必须要有,没有就不去了)。
软硬策略合体
软硬可以在一起使用,先要满足硬策略以后才能去满足软策略。
apiVersion: v1
kind: Pod
metadata:
name: affinity-node
labels:
app: node-affinity-pod
spec:
containers:
- name: with-affinity-node
image: nginx:v1
imagePullPulicy: IfNotPresent
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: NotIn
values:
- k8s-node2
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: source
operator: In
values:
- hello
软硬结合达到一个更为准确的node选择,以上文件意思为此Pod必须不存在k8s-node2节点中,其他的节点都可以,但最好落在label中source的值为hello的节点。
键值运算关系
-
In:label 的值在某个列表里
-
NotIn:label 的值不在某个列表中
-
Gt:label 的值大于某个值
-
Lt:label 的值小于某个值
-
Exists:某个 label 存在
-
DoesNotExist:某个 label 不存在
如果nodeSelectorTerms下面有多个选项,满足任何一个条件就可以了;如果matchExpressions有多个选项,则必须满足这些条件才能正常调度
软策略和硬策略的方法基本类似,只是添加了权重,表示更喜欢而已,也可以接受其他,在此就不再演示
亲和性/反亲和性调度策略比较如下:
调度策略 | 匹配标签 | 操作符 | 拓扑域支持 | 调度目标 |
---|---|---|---|---|
nodeAffinity | 主机 | In,NotIn,Exists,DoesNotExists,Gt,Lt | 否 | 指定主机 |
podAffinity | Pod | In,NotIn,Exists,DoesNotExists,Gt,Lt | 是 | pod与指定pod在一拓扑域 |
podAnitAffinity | Pod | In,NotIn,Exists,DoesNotExists,Gt,Lt | 是 | pod与指定pod不在一拓扑域 |
[root@master ~]# kubectl label node node1 node-role.kubernetes.io/pg=
node/node1 labeled
[root@master ~]# kubectl get node
NAME STATUS ROLES AGE VERSION
master Ready master 60d v1.19.8
node1 Ready pg,worker 60d v1.19.8
node2 Ready worker 60d v1.19.8
[root@master ~]# kubectl label node node1 node-role.kubernetes.io/worker= node-role.kubernetes.io/worker=
error: 'node-role.kubernetes.io/worker' already has a value (), and --overwrite is false
设置节点亲和性,先打标签
[root@bots-hrx-ksm1 kubectl]# kubectl get node
NAME STATUS ROLES AGE VERSION
bots-hrx-ksw14 Ready pg,worker 15h v1.19.8
bots-hrx-ksw15 Ready pg,worker 15h v1.19.8
bots-hrx-ksw16 Ready pg,worker 15h v1.19.8
node-role.kubernetes.io/pg
node-role.kubernetes.io/worker
最后调整将所有组件放在一起
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node-role.kubernetes.io/pg
operator: In
values:
- ''
tolerations:
- key: node-role.kubernetes.io/pg
operator: Exists
更多推荐
所有评论(0)