K8S调度管理
摘要:本文详细介绍了Kubernetes中Pod调度的多种控制方式。主要包括:1)节点选择器(nodeSelector/nodeName)实现Pod定向调度;2)Pod亲和性(podAffinity)与反亲和性(podAntiAffinity)实现Pod协同或隔离部署;3)污点(Taints)与容忍(Tolerations)机制控制节点是否接受Pod调度。文章通过YAML示例演示了硬性/软性调度约
一、node节点选择器
我们在创建pod资源的时候,pod会根据schduler进行调度,那么默认会调度到随机的一个工作节点,如果我们想要pod调度到指定节点或者调度到一些具有相同特点的node节点,怎么办呢?
可以使用pod中的nodeName或者nodeSelector字段指定要调度到的node节点。
1、nodeName
指定pod节点运行在哪个具体node上
[root@k8s-master ~]# cat pod-node.yaml
apiVersion: v1
kind: Pod
metadata:
name: demo-pod1
namespace: default
labels:
app: myapp
env: dev
spec:
nodeName: k8s-node1
containers:
- name: nginx-nodename
ports:
- containerPort: 80
image: nginx
imagePullPolicy: IfNotPresent
[root@k8s-master ~]# kubectl apply -f pod-node.yaml
#查看pod调度到哪个节点
[root@k8s-master ~]# kubectl get pods -o wide
2、nodeSelector
指定pod调度到具有哪些标签的node节点上
#给node节点打标签,打个具有disk=ceph的标签
[root@k8s-master ~]# kubectl label nodes k8s-node1 node=worker01
#查看节点的详细信息
[root@hd1 node]# kubectl describe nodes k8s-node1
#定义pod的时候指定要调度到具有 node=worker01标签的node上
[root@k8s-master ~]# cat pod-1.yaml
apiVersion: v1
kind: Pod
metadata:
name: demo-pod-1
namespace: default
labels:
app: myapp
env: dev
spec:
nodeSelector:
node: worker01
containers:
- name: nginx-nodename
ports:
- containerPort: 8080
image: tomcat:8.5-jre8-alpine
imagePullPolicy: IfNotPresent
[root@k8s-master ~]# kubectl apply -f pod-1.yaml
#查看pod调度到哪个节点
[root@k8s-master ~]# kubectl get pods -o wide
二、Pod亲和性
生产上为了保证应用的高可用性,需要将同一应用的不同pod分散在不同的宿主机上,以防宿主机出现宕机等情况导致pod重建,影响到业务的连续性。要想实现这样的效果,需要用到k8s自带的pod亲和性和反亲和性特性。
Pod 的亲和性与反亲和性有两种类型:
requiredDuringSchedulingIgnoredDuringExecution ##一定满足
preferredDuringSchedulingIgnoredDuringExecution ##尽量满足
podAffinity(亲和性):pod和pod更倾向腻在一起,把相近的pod结合到相近的位置,如同一区域,同一机架,这样的话pod和pod之间更好通信,比方说有两个机房,这两个机房部署的集群有1000台主机,那么我们希望把nginx和tomcat都部署同一个地方的node节点上,可以提高通信效率;
podAntiAffinity(反亲和性):pod和pod更倾向不腻在一起,如果部署两套程序,那么这两套程序更倾向于反亲和性,这样相互之间不会有影响。
第一个pod随机选则一个节点,做为评判后续的pod能否到达这个pod所在的节点上的运行方式,这就称为pod亲和性;我们怎么判定哪些节点是相同位置的,哪些节点是不同位置的;我们在定义pod亲和性时需要有一个前提,哪些pod在同一个位置,哪些pod不在同一个位置,这个位置是怎么定义的,标准是什么?以节点名称为标准,这个节点名称相同的表示是同一个位置,节点名称不相同的表示不是一个位置。
1、 常用字段解析
[root@k8s-master ~]# kubectl explain pods.spec.affinity.podAffinity
FIELDS:
preferredDuringSchedulingIgnoredDuringExecution <[]Object>
requiredDuringSchedulingIgnoredDuringExecution <[]Object>
#硬亲和性
requiredDuringSchedulingIgnoredDuringExecution
#软亲和性
preferredDuringSchedulingIgnoredDuringExecution
[root@k8s-master ~]# kubectl explain pods.spec.affinity.podAffinity.requiredDuringSchedulingIgnoredDuringExecution
FIELDS:
labelSelector <Object>
namespaces <[]string>
topologyKey <string> -required-
#位置拓扑的键,这个是必须字段
#topologyKey
#怎么判断是不是同一个位置?
#使用rack的键是同一个位置
#rack=rack1
#使用row的键是同一个位置
#row=row1
#要判断pod跟别的pod亲和,跟哪个pod亲和,需要靠labelSelector,通过labelSelector选则一组能作为亲和对象的pod资源
#labelSelector:
#labelSelector需要选则一组资源,那么这组资源是在哪个名称空间中呢,通过namespace指定,如果不指定namespaces,那么就是当前创建pod的名称空间
#namespace:
[root@k8s-master ~]# kubectl explain pods.spec.affinity.podAffinity.requiredDuringSchedulingIgnoredDuringExecution.labelSelector
FIELDS:
matchExpressions <[]Object>
matchLabels <map[string]string>
#匹配表达式
#matchExpressions <[]Object>
2、 案例演示
2.1 pod节点亲和性
[root@k8s-master ~]# cat pod-required-affinity-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-first
labels:
app2: myapp2
tier: frontend
spec:
containers:
- name: myapp
image: nginx
imagePullPolicy: IfNotPresent
---
apiVersion: v1
kind: Pod
metadata:
name: pod-second
labels:
app: backend
tier: db
spec:
containers:
- name: busybox
image: busybox:1.28
imagePullPolicy: IfNotPresent
command: ["sh","-c","sleep 3600"]
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- {key: app2, operator: 'In', values: ["myapp2"]}
topologyKey: kubernetes.io/hostname
上面表示创建的pod必须与拥有app=myapp2标签的pod在一个节点上
[root@k8s-master ~]# kubectl apply -f pod-required-affinity-demo.yaml
[root@k8s-master ~]# kubectl get pods -o wide
2.2 pod节点反亲和性
[root@hd1 node]# kubectl delete -f pod-required-affinity-demo.yaml
[root@k8s-master ~]# cat pod-required-anti-affinity-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod1-first
labels:
app1: myapp1
tier: frontend
spec:
containers:
- name: myapp
image: nginx
imagePullPolicy: IfNotPresent
---
apiVersion: v1
kind: Pod
metadata:
name: pod2-second
labels:
app: backend
tier: db
spec:
containers:
- name: busybox
image: busybox:1.28
imagePullPolicy: IfNotPresent
command: ["sh","-c","sleep 3600"]
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app1
operator: 'In'
values:
- myapp1
topologyKey: kubernetes.io/hostname
[root@k8s-master ~]# kubectl apply -f pod-required-anti-affinity-demo.yaml
[root@k8s-master ~]# kubectl get pods -o wide
2.3 换一个topologykey
[root@k8s-master ~]# kubectl label nodes k8s-node1 zone=foo
[root@k8s-master ~]# kubectl label node k8s-node2 zone=foo --overwrite
[root@k8s-master ~]# cat pod-first-required-anti-affinity-demo-1.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod3-first
labels:
app3: myapp3
tier: frontend
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
imagePullPolicy: IfNotPresent
[root@k8s-master ~]# cat pod-second-required-anti-affinity-demo-2.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod3-second
labels:
app: backend
tier: db
spec:
containers:
- name: busybox
image: busybox:1.28
imagePullPolicy: IfNotPresent
command: ["sh","-c","sleep 3600"]
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- {key: app3 ,operator: 'In', values: ["myapp3"]}
topologyKey: zone
[root@k8s-master ~]# kubectl apply -f pod-first-required-anti-affinity-demo-1.yaml
[root@k8s-master ~]# kubectl apply -f pod-second-required-anti-affinity-demo-2.yaml
[root@k8s-master ~]# kubectl get pods -o wide#发现第二个节点是pending,因为两个节点是同一个位置,现在不是同一个位置的了,而且我们要求反亲和性,所以就会处于pending状态,如果在反亲和性这个位置把required改成preferred,那么也会运行。
#podaffinity:pod节点亲和性,pod倾向于哪个pod
#nodeaffinity:node节点亲和性,pod倾向于哪个node
常见topologyKey
topologyKey |
拓扑域范围 | 主要用途 |
|---|---|---|
kubernetes.io/hostname |
节点(Node) | 最常用。实现 Pod 在节点级别的分散或集中。 |
topology.kubernetes.io/zone |
可用区(Zone) | 实现跨可用区的高可用,容灾能力更强。 |
topology.kubernetes.io/region |
区域(Region) | 实现跨地域的部署,通常用于多地域容灾或全球化应用。 |
node.kubernetes.io/instance-type |
实例类型 | 根据机器硬件类型来调度 Pod,较少使用。 |
三、资源限制
resources
四、污点与容忍
前面介绍了节点亲和性调度,它可以使得我们的Pod调度到指定的Node节点上,而污点(Taints)与之相反,它可以让Node拒绝Pod的运行,甚至驱逐已经在该Node上运行的Pod
污点是Node上设置的一个属性,通常设置污点表示该节点有问题,比如磁盘要满了,资源不足,或者该Node正在升级暂时不能提供使用等情况,这时不希望再有新的Pod进来,这个时候就可以给该节点设置一个污点。
但是有的时候其实Node节点并没有故障,只是不想让一些Pod调度进来,比如这台节点磁盘空间比较大,希望是像Elasticsearch、Minio这样需要较大磁盘空间的Pod才调度进来,那么就可以给节点设置一个污点,给Pod设置容忍(Tolerations)对应污点,如果再配合节点亲和性功能还可以达到独占节点的效果
一般时候 Taints 总是与 Tolerations配合使用
1、污点(Taints)
设置污点后,一般Pod将不会调度到该节点上来。每次Pod都不会调度到master节点,那是因为搭建K8s集群的时候,K8s给master自带了一个污点。
1.1、查看污点
查看master上是否有污点,通过describe命令可以看到节点上的所有污点
[root@k8s-master ~]# kubectl describe node k8s-master
...
Taints: node-role.kubernetes.io/control-plane:NoSchedule
...
还可以通过如下命令查看
[root@k8s-master ~]# kubectl get nodes k8s-master -o go-template={{.spec.taints}}
[map[effect:NoSchedule key:node-role.kubernetes.io/control-plane]]
发现master上自带了一个没有value的污点 node-role.kubernetes.io/control-plane ,effect 为 NoSchedule,由于我们的Pod都没有容忍这个污点,所以一直都不会调度到master
1.2、污点类别
上面我们看到了污点有一个effect 属性为NoSchedule,其实还有其它两种类别分别是 PreferNoSchedule与 NoExecute
-
NoSchedule: 如果没有容忍该污点就不能调度进来。
-
PreferNoSchedule: 相当于NoExecute的一个软限制,如果没有容忍该污点,尽量不要把Pod调度进来,但也不是强制的。
-
NoExecute: 如果没有设置容忍该污点,新的Pod肯定不会调度进来, 并且已经在运行的Pod没有容忍该污点也会被驱逐。
1.3、设置污点
污点分别由key、value(可以为空)、effect 组成,设置命令如下
# 设置污点并不允许Pod调度到该节点
$ kubectl taint node k8s-node1 key=value:NoSchedule
# 设置污点尽量不要让Pod调度到该节点
$ kubectl taint node k8s-node1 key=value:PreferNoSchedule
# 设置污点,不允许Pod调度到该节点,并且且将该节点上没有容忍该污点的Pod将进行驱逐
$ kubectl taint node k8s-node1 key=value:NoExecute
1.4、删除污点
# 删除该key的所有污点
$ kubectl taint node k8s-node1 key-
# 删除该key的某一个污点
$ kubectl taint node k8s-node1 key=value:NoSchedule-
# 删除该key的某一个污点可以不写value
$ kubectl taint node k8s-node1 key:NoSchedule-
1.5、污点测试
给k8s-node1节点设置一个污点,表示只有前台web应用才能调度,后端app应用不能调度
[root@k8s-master ~]# kubectl taint node k8s-node1 web=true:NoSchedule
node/k8s-node1 tainted
编写 web-taint.yaml 内容如下,有三个Pod均没有容忍该污点
apiVersion: v1
kind: Pod
metadata:
name: web-taint1
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent # 本地有不拉取镜像
---
apiVersion: v1
kind: Pod
metadata:
name: web-taint2
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent # 本地有不拉取镜像
---
apiVersion: v1
kind: Pod
metadata:
name: web-taint3
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent # 本地有不拉取镜像
启动Pod,观察Pod是否都被调度到k8s-node2节点
# 启动三个Pod
[root@k8s-master taint]# kubectl apply -f web-taint.yaml
pod/web-taint1 applyd
pod/web-taint2 applyd
pod/web-taint3 applyd# 查看pod详情发现均被调度到k8s-node2节点
[root@k8s-master taint]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web-taint1 1/1 Running 0 13s 10.244.2.35 k8s-node2 <none> <none>
web-taint2 1/1 Running 0 13s 10.244.2.34 k8s-node2 <none> <none>
web-taint3 1/1 Running 0 13s 10.244.2.36 k8s-node2 <none> <none>
给k8s-node2节点添加污点 app=true:NoExecute,表示只有后端服务才能调度进来,并且已经在k8s-node2节点上运行的不是后端服务的节点将被驱逐
# 设置污点
[root@k8s-master taint]# kubectl taint node k8s-node2 app=true:NoExecute
node/k8s-node2 tainted
# 查看Pod详情,发现三个Pod已经被驱逐
[root@k8s-master taint]# kubectl get pod -o wide
No resources found in default namespace.
从上面可以知道我们虽然设置了污点,但是我们的节点其实很正常,既然是正常节点,那么就可以有服务运行,这就需要用到容忍机制来运行这些Pod
2、容忍(Toletations)
如果需要Pod忽略Node上的污点,就需要给Pod设置容忍,并且是需要容忍该Pod上的所有污点。
通过 kubectl explain pod.spec.tolerations 可以查看容忍的配置项
2.1、配置项
tolerations: # 数组类型,可以设置多个容忍
- key: # 污点key
operator: # 操作符,有两个选项 Exists and Equal 默认是Equal
value: # 污点value,如果使用Equal需要设置,如果是Exists就不需要设置
effect: # 可以设置为NoSchedule、PreferNoSchedule、NoExecute,如果为空表示匹配该key下所有污点,
tolerationSeconds: # 如果污点类型为NoExecute,还可以设置一个时间,表示这一个时间段内Pod不会被驱逐,过了这个时间段会立刻驱逐,0或者负数会被立刻驱逐
2.2、设置容忍
给Pod设置容忍k8s-node1节点的污点web=true:NoSchedule
编写 web-tolerations.yaml 内容如下,容忍污点web=true:NoSchedule
apiVersion: v1
kind: Pod
metadata:
name: web-tolerations
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent # 本地有不拉取镜像
tolerations:
- key: web
operator: Equal
value: "true"
effect: NoSchedule
启动web-tolerations,观察Pod是否正常运行
# 启动web-tolerations
[root@k8s-master taint]# kubectl apply -f web-tolerations.yaml
pod/web-tolerations applyd
# Pod正常运行,且运行在k8s-node1节点
[root@k8s-master taint]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web-tolerations 1/1 Running 0 8s 10.244.1.92 k8s-node1 <none> <none>
查看该Pod上的容忍
[root@k8s-master taint]# kubectl describe pod web-tolerations
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
web=true:NoSchedule
可以看到我们设置的Tolerations在里边,除了我们自己的,K8s还会给每个Pod设置两个默认Tolerations,并且Tolerations时间为五分钟,表示某些节点发生一些临时性问题,Pod能够继续在该节点上运行五分钟等待节点恢复,并不是立刻被驱逐,从而避免系统的异常波动。
编写 app-tolerations.yaml 内容如下,容忍污点app=true:NoExecute,并且只容忍60s
apiVersion: v1
kind: Pod
metadata:
name: app-tolerations
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
tolerations:
- key: app
operator: Exists
effect: NoExecute
tolerationSeconds: 60
启动app-tolerations,查看Pod是否能够正常运行,且运行在k8s-node2节点,等待60秒,查看Pod是否已被驱逐
# 启动app-tolerations
[root@k8s-master taint]# kubectl apply -f app-tolerations.yaml
pod/app-tolerations applyd# 查看详情,已经运行成功并且调度到了k8s-node2节点,此时运行59秒
[root@k8s-master taint]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
app-tolerations 1/1 Running 0 59s 10.244.2.37 k8s-node2 <none> <none># 运行60秒查看,状态已经变为Terminating
[root@k8s-master taint]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
app-tolerations 1/1 Terminating 0 60s 10.244.2.37 k8s-node2 <none> <none># 立刻再次查看,已经被驱逐
[root@k8s-master taint]# kubectl get pod -o wide
No resources found in default namespace.
如果一个节点没有任何污点,另一个节点有污点,并且Pod容忍了该污点,那么最终会调度到哪个节点呢?
删除k8s-node2节点的污点app=true:NoExecute,保留k8s-node1节点的web=true:NoSchedule,再次启动之前的web-tolerations.yaml 观察Pod所在节点
# 删除k8s-node2上的污点
[root@k8s-master taint]# kubectl taint node k8s-node2 app:NoExecute-
node/k8s-node2 untainted
# 启动Pod web-tolerations
[root@k8s-master taint]# kubectl apply -f web-tolerations.yaml
pod/web-tolerations applyd
# Pod正常运行,并且运行在k8s-node2节点
[root@k8s-master taint]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web-tolerations 1/1 Running 0 2s 10.244.2.38 k8s-node2 <none> <none>
上面表明,即使是容忍了该污点,如果有其它节点不需要容忍就可以调度的话,那还是会优先选择没有污点的机器,但是生产上往往设置污点就是为了给你这个Pod使用的,不能调度到其它节点,这个时候需要配合节点亲和性调度一起使用。
2.3、Equal 与 Exists
Exists 表示key是否存在所以无需设置value,Equal 则必须设置value需要完全匹配,默认为Equal
空的key配合Exists 可以匹配所有的key-value,也就是容忍所有的污点,生产上非常不建议使用
更多推荐


所有评论(0)