Kubernetes技术详解-从理论到实践-(17)-调度-Scheduling
1 Scheduling调度简介
Kubernetes调度就是把待运行的Pod分配到最合适的节点上的过程。
2 Scheduling调度过程
Kubernetes的调度过程主要包括:过滤器筛掉不可用节点、打分器给剩余节点排序及把Pod绑定到最高分节点。
过滤阶段常见策略为:
PodFitsResources:CPU/内存/磁盘足够
PodFitsHost:指定节点名匹配
PodFitsHostPorts:端口不冲突
MatchNodeSelector:label 满足 nodeSelector
TolerationTaints:能容忍节点污点
VolumeBinding:PV 拓扑匹配
打分阶段的主要依据为:
LeastRequestedPriority:空闲资源越多分越高
BalancedResourceAllocation:CPU/内存使用率最均衡
ImageLocalityPriority:已存在镜像层得分高
NodeAffinityPriority:亲和性权重
InterPodAffinityPriority:Pod 亲和/反亲和权重
接下来,我们主要讲解三类调度方式。
硬编码:nodeName调度和nodeSelector调度
亲和性:节点亲合性、Pod亲和性和Pod反亲和性
污点和容忍度:污点、容忍度
3 硬编码
硬编码调度是把Pod调度到指定节点的最简单手段,主要包括nodeName和nodeSelector两种。
3.1 nodeName调度
原理:直接在 Pod 里写死 spec.nodeName: <节点名称>。
流程:kube-scheduler 完全跳过;kubelet 看到本节点名匹配就启动容器。
优点:最快,无调度延迟。
缺点:节点不存在或资源不足时 Pod 无法启动;节点改名即失效。
在Pod yaml中添加nodeName字段即可将Pod调度到指定Node节点。
spec:
nodeName: worker2
3.1.1 示例:nodeName调度
(1) 资源清理
[root@master 17-scheduling]# kubectl get pod,deploy
No resources found in default namespace.
(2) 查看集群节点
有三个节点,我们想把pod调度到worker2节点
[root@master 17-scheduling]# kubectl get node
NAME STATUS ROLES AGE VERSION
master Ready control-plane,master 125m v1.29.6+k3s1
worker1 Ready <none> 125m v1.29.6+k3s1
worker2 Ready <none> 124m v1.29.6+k3s1
(3) 编写nodeName调度的资源清单文件
编写nodeName调度的Deployment资源对象文件,只有1个Pod副本。
如果想让pod调度到指定的节点,只需要在containers同级别添加一行nodeName,pod将被调度到执行的node节点上
[root@master 17-scheduling]# cat nginx-nodename.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deploy
labels:
app: nginx-dm
spec:
selector:
matchLabels:
app: nginx-dm
replicas: 1
template:
metadata:
labels:
app: nginx-dm
spec:
containers:
- name: nginx
image: nginx:1.27.3
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
nodeName: worker2 # 将Pod调度到worker2节点
(4) 创建资源对象
[root@master 17-scheduling]# kubectl apply -f nginx-nodename.yaml
deployment.apps/nginx-deploy created
(5) 查看调度情况
# 可以看到pod已经被调度到worker2
[root@master 17-scheduling]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-7c967d7ff9-mmrvz 1/1 Running 0 6s 10.42.2.65 worker2 <none> <none>
3.2 nodeSelector调度
原理:给节点打标签,例如disk=ssd;在Pod里写nodeSelector: {disk: ssd}。
流程:仍经过scheduler,过滤阶段去掉不满足标签的节点,再正常打分/绑定。
优点:节点漂移、扩缩容更灵活;可组合多个标签。
缺点:需要提前给节点维护标签。
给节点打标签,然后在Pod yaml中添加nodeSelector字段选择Node标签,即可将Pod调度到指定Node节点。
给节点打标签
kubectl label node worker1 disk=ssd
添加nodeSelector字段
# Pod 声明
spec:
nodeSelector:
disk: ssd
3.2.1 示例:nodeSelector调度
通过键值对匹配节点的标签,将pod调度到符合条件的节点上。
为节点添加和删除标签的操作如下:
为worker1节点添加单个标签,标签名为node-role=worker1kubectl label node worker1 node-role=worker1
为worker1节点添加多个标签kubectl label node worker1 node-role=worker1 disktype=ssd
删除节点的单个标签kubectl label node worker1 node-role-
删除一个节点的多个标签kubectl label node worker1 node-role- disktype-
查看节点标签kubectl get node worker1 --show-labels
下面是nodeSelector调度示例操作步骤。
(1) 清理环境
[root@master 17-scheduling]# kubectl get pod,deploy
No resources found in default namespace.
(2) 节点打标签
# 查看节点的标签
[root@master 17-scheduling]# kubectl get node --show-labels
NAME STATUS ROLES AGE VERSION LABELS
master Ready control-plane,master 155m v1.29.6+k3s1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/instance-type=k3s,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=master,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=true,node-role.kubernetes.io/master=true,node.kubernetes.io/instance-type=k3s
worker1 Ready <none> 154m v1.29.6+k3s1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/instance-type=k3s,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=worker1,kubernetes.io/os=linux,node.kubernetes.io/instance-type=k3s
worker2 Ready <none> 153m v1.29.6+k3s1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/instance-type=k3s,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=worker2,kubernetes.io/os=linux,node.kubernetes.io/instance-type=k3s
# 为worker1节点添加标签disktype=ssd
[root@master 17-scheduling]# kubectl label node worker1 disktype=ssd
node/worker1 labeled
# 再次查看标签,可以看到worker1已经包含disktype=ssd的标签
[root@master 17-scheduling]# kubectl get node --show-labels
NAME STATUS ROLES AGE VERSION LABELS
master Ready control-plane,master 156m v1.29.6+k3s1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/instance-type=k3s,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=master,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=true,node-role.kubernetes.io/master=true,node.kubernetes.io/instance-type=k3s
worker1 Ready <none> 155m v1.29.6+k3s1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/instance-type=k3s,beta.kubernetes.io/os=linux,disktype=ssd,kubernetes.io/arch=amd64,kubernetes.io/hostname=worker1,kubernetes.io/os=linux,node.kubernetes.io/instance-type=k3s
worker2 Ready <none> 154m v1.29.6+k3s1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/instance-type=k3s,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=worker2,kubernetes.io/os=linux,node.kubernetes.io/instance-type=k3s
(3) 编写具有nodeSelector调度方式的Deployment资源清单文件
nodeSelector调度方式和nodeName相似,在containers同级目录下添加nodeSelector字段,字段内容为要选择的node标签。
[root@master 17-scheduling]# cat nginx-nodeselector.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deploy
labels:
app: nginx-dm
spec:
selector:
matchLabels:
app: nginx-dm
replicas: 1
template:
metadata:
labels:
app: nginx-dm
spec:
containers:
- name: nginx
image: nginx:1.27.3
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
nodeSelector: # 选择标签为disktype=ssd的Node
disktype: ssd
(4)创建Deployment
[root@master 17-scheduling]# kubectl apply -f nginx-nodeselector.yaml
deployment.apps/nginx-deploy created
(5) 查看调度情况
可以看到pod已经被调度到worker1上。
[root@master 17-scheduling]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-bc886696f-5tzk6 1/1 Running 0 5s 10.42.1.49 worker1 <none> <none>
4 亲和性
Kubernetes的亲和性(Affinity)是一组调度规则,用来告诉调度器哪些Pod应该(或必须)跟哪些节点/其他Pod放在一起或分开。相比写死nodeName或 nodeSelector,Affinity提供更丰富的表达式、软硬策略、拓扑域概念,让调度更灵活、更弹性。
4.1 亲和性三大类别
| 类别 | 作用对象 | 典型用途 |
|---|---|---|
| NodeAffinity | 节点标签 | 把Pod调度到“满足某些标签”的节点 |
| PodAffinity | 其他Pod标签 | 把Pod与“某些Pod”放在同一拓扑域(如同机架、同可用区) |
| PodAntiAffinity | 其他Pod标签 | 把Pod与“某些Pod”分散开(高可用、避免热点) |
每种类型又分为Required(硬约束)和Preferred(软偏好)。
4.2 硬约束和软偏好
硬约束与软偏好是 Kubernetes 亲和性(Affinity) 里的两种策略强度。
| 维度 | 硬约束 (Required) | 软偏好 (Preferred) |
|---|---|---|
| 英文关键字 | requiredDuringSchedulingIgnoredDuringExecution | preferredDuringSchedulingIgnoredDuringExecution |
| 语义 | 必须满足——找不到符合条件的节点就一直Pending | 尽量满足——先挑符合的节点,没有也能调度(退而求其次) |
| 调度失败结果 | Pod 处于Pending,直到出现匹配节点 | Pod 仍可被调度到不匹配节点,仅打分低 |
| 使用场景 | 强制合规、硬件要求、许可证限制 | 性能优化、能耗偏好、就近访问 |
硬约束举例:
Pod必须落在SSD磁盘节点,否则不调度。若集群无disk-type=ssd的节点,Pod一直Pending。
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: disk-type
operator: In
values: ["ssd"]
软偏好举例:
优先选SSD,没有也行。如果找不到SSD节点,仍可调度到普通磁盘节点,仅打分低。
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100 # 权重越高越优先
preference:
matchExpressions:
- key: disk-type
operator: In
values: ["ssd"]
4.3 NodeAffinity节点亲和性
节点亲和性是指根据节点的标签来控制pod的调度,将pod调度到满足特定条件的节点上。
节点亲和性有强制亲和性(requiredDuringSchedulingIgnoredDuringExecution)和偏好亲和性(preferredDuringSchedulingIgnoredDuringExecution)两种。
强制亲和性是硬性要求,硬约束,必须有满足条件的节点,pod才会被调度,否则无法启动。
偏好亲和性是软性要求,软偏好,调度器尽量将pod调度到满足条件的节点上,但如果没有这样的节点,pod仍然可以在不满足条件的节点上启动。
4.3.1 示例:节点强制亲和性
以下示例为节点强制亲和性演示,当有特定条件节点时Pod将被调度到该节点,当没有特定条件的节点时Pod将一直Pending。
(1) 环境清理
删除上个例子中为worker1添加的标签,删除deploy资源
[root@master 17-scheduling]# kubectl label node worker1 disktype-
node/worker1 unlabeled
[root@master 17-scheduling]# kubectl delete deploy nginx-deploy
deployment.apps "nginx-deploy" deleted
[root@master 17-scheduling]# kubectl get pod,deploy
No resources found in default namespace.
(2) 节点打标签
# 为worker1添加一个node-role=worker1的标签
[root@master 17-scheduling]# kubectl label node worker1 node-role=worker1
node/worker1 labeled
# 查看标签,可以看到worker1的LABELS包含刚才添加的标签
[root@master 17-scheduling]# kubectl get node --show-labels
NAME STATUS ROLES AGE VERSION LABELS
master Ready control-plane,master 175m v1.29.6+k3s1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/instance-type=k3s,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=master,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=true,node-role.kubernetes.io/master=true,node.kubernetes.io/instance-type=k3s
worker1 Ready <none> 174m v1.29.6+k3s1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/instance-type=k3s,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=worker1,kubernetes.io/os=linux,node-role=worker1,node.kubernetes.io/instance-type=k3s
worker2 Ready <none> 173m v1.29.6+k3s1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/instance-type=k3s,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=worker2,kubernetes.io/os=linux,node.kubernetes.io/instance-type=k3s
(3) 编写具有节点亲和性功能的Deployment资源清单文件
节点亲和性,affinity字段,本规则为节点亲和性nodeAffinity,requiredDuringSchedulingIgnoredDuringExecution说明这是一个强制亲和性规则,匹配规则为key(node-role)有worker1值的这种节点。
[root@master 17-scheduling]# cat nginx-nodeaffinity.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deploy
labels:
app: nginx-dm
spec:
selector:
matchLabels:
app: nginx-dm
replicas: 1
template:
metadata:
labels:
app: nginx-dm
spec:
containers:
- name: nginx
image: nginx:1.27.3
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node-role
operator: In
values: ["worker1"]
(4) 创建Deployment资源
[root@master 17-scheduling]# kubectl apply -f nginx-nodeaffinity.yaml
deployment.apps/nginx-deploy created
(5) 查看Pod调度情况
可以看到Pod被调度到了符合节点强制亲和性的worker1节点上。
[root@master 17-scheduling]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-574f6599cb-4g5jr 1/1 Running 0 6s 10.42.1.50 worker1 <none> <none>
[root@master 17-scheduling]# kubectl get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deploy 1/1 1 1 19s
(6) 删除Deployment,为后面步骤做准备
[root@master 17-scheduling]# kubectl delete deploy nginx-deploy
deployment.apps "nginx-deploy" deleted
[root@master 17-scheduling]# kubectl get node
NAME STATUS ROLES AGE VERSION
master Ready control-plane,master 176m v1.29.6+k3s1
worker1 Ready <none> 175m v1.29.6+k3s1
worker2 Ready <none> 174m v1.29.6+k3s1
(7) 将Deployment资源修改为调度到无对应标签的Node上
修改nginx-nodeaffinity.yaml文件,将values中的worker1改为woker3,我们没有为任何节点添加过node-type=worker3的标签,pod应该不会正常启动。
[root@master 17-scheduling]# vim nginx-nodeaffinity.yaml
[root@master 17-scheduling]# cat nginx-nodeaffinity.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deploy
labels:
app: nginx-dm
spec:
selector:
matchLabels:
app: nginx-dm
replicas: 1
template:
metadata:
labels:
app: nginx-dm
spec:
containers:
- name: nginx
image: nginx:1.27.3
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node-role
operator: In
values: ["worker3"]
(8) 再次创建Deployment
[root@master 17-scheduling]# kubectl apply -f nginx-nodeaffinity.yaml
deployment.apps/nginx-deploy created
(9) 查看Pod调度状态
# 可以看到Pod处于Pending状态
[root@master 17-scheduling]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-8d76fc9cd-9ctnp 0/1 Pending 0 7s <none> <none> <none> <none>
# 查看Pod详情,可以看到其Pending状态为找不到符合要求的Node
[root@master 17-scheduling]# kubectl describe pod nginx-deploy-8d76fc9cd-9ctnp
Name: nginx-deploy-8d76fc9cd-9ctnp
(。。。部分内容略。。。)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 18s default-scheduler 0/3 nodes are available: 3 node(s) didn't match Pod's node affinity/selector. preemption: 0/3 nodes are available: 3 Preemption is not helpful for scheduling.
4.4 PodAffinity Pod亲和性
Pod亲和性允许根据其他Pod的标签来控制Pod的调度。可以指定Pod只能调度到符合条件的Pod相同的节点上。
和Node亲和性相似,Pod亲和性也有强制亲和性(requiredDuringSchedulingIgnoredDuringExecution)与偏好亲和性(preferredDuringSchedulingIgnoredDuringExecution)两种,下面的例子演示Pod强制亲和性。
4.4.1 示例:Pod强制亲和性
(1) 查看集群状态
[root@master 17-scheduling]# kubectl get node
NAME STATUS ROLES AGE VERSION
master Ready control-plane,master 3h16m v1.29.6+k3s1
worker1 Ready <none> 3h15m v1.29.6+k3s1
worker2 Ready <none> 3h14m v1.29.6+k3s1
[root@master 17-scheduling]# kubectl get pod,deploy
No resources found in default namespace.
(2) 编写一个普通的Deployment资源文件
先创建一个标签为app=nginx-dm的pod,后面创建pod亲和性规则,调度到和带app=nginx-dm标签的pod相同的节点上。
[root@master 17-scheduling]# cat nginx-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deploy
labels:
app: nginx-dm
spec:
selector:
matchLabels:
app: nginx-dm
replicas: 1
template:
metadata:
labels:
app: nginx-dm
spec:
containers:
- name: nginx
image: nginx:1.27.3
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
(3) 创建Deployment
[root@master 17-scheduling]# kubectl apply -f nginx-deploy.yaml
deployment.apps/nginx-deploy created
(4) 查看Pod调度情况
由于没有任何规则,本Pod由K3s自动调度,可以看到Pod被调度到worker1节点,通过kubectl --show-labels参数可以查看Pod标签。
# Pod被调度到worker1上
[root@master 17-scheduling]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-7c7bbf4cc9-mzmvn 1/1 Running 0 4s 10.42.1.51 worker1 <none> <none>
# kubectl --show-labels参数可以查看Pod的标签
[root@master 17-scheduling]# kubectl get pod -o wide --show-labels
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS
nginx-deploy-7c7bbf4cc9-mzmvn 1/1 Running 0 16s 10.42.1.51 worker1 <none> <none> app=nginx-dm,pod-template-hash=7c7bbf4cc9
(5) 创建带Pod亲和性规则的Deployment
创建带Pod亲和性规则的Deployment,期望调度到标签为app=nginx-dm的Pod所在节点上。
[root@master 17-scheduling]# cat nginx-podaffinity.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-pa-deploy
labels:
app: nginx-pa-dm
spec:
replicas: 1
selector:
matchLabels:
app: nginx-pa-dm
template:
metadata:
labels:
app: nginx-pa-dm
spec:
containers:
- name: nginx
image: nginx:1.27.3
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values: ["nginx-dm"]
topologyKey: kubernetes.io/hostname
(6) 创建带Pod亲和性规则的Deployment
[root@master 17-scheduling]# kubectl apply -f nginx-podaffinity.yaml
deployment.apps/nginx-pa-deploy created
(7) 查看Pod调度情况
可以看到已经被调度到与标签为app=nginx-dm的pod所在节点上。
[root@master 17-scheduling]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-7c7bbf4cc9-mzmvn 1/1 Running 0 63s 10.42.1.51 worker1 <none> <none>
nginx-pa-deploy-94c5cbd48-pj5mw 1/1 Running 0 8s 10.42.1.52 worker1 <none> <none>
(8) 删除刚才的Deployment资源
删除刚才的nginx-pa-deploy资源,为后面Pod硬约束亲和性验证做准备
[root@master 17-scheduling]# kubectl get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deploy 1/1 1 1 109s
nginx-pa-deploy 1/1 1 1 54s
[root@master 17-scheduling]# kubectl delete deploy nginx-pa-deploy
deployment.apps “nginx-pa-deploy” deleted
(9) 修改nginx-podaffinity.yaml资源清单文件
修改文件,改为与标签为app in nginx-dm1的Pod调度到同一个节点,当然,系统不存在这种标签的Pod,期望结果为Pod一直处于Pending状态。
修改pod亲和性规则,修改后的内容见下
[root@master 17-scheduling]# vim nginx-podaffinity.yaml
[root@master 17-scheduling]# cat nginx-podaffinity.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-pa-deploy
labels:
app: nginx-pa-dm
spec:
replicas: 1
selector:
matchLabels:
app: nginx-pa-dm
template:
metadata:
labels:
app: nginx-pa-dm
spec:
containers:
- name: nginx
image: nginx:1.27.3
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values: ["nginx-dm1"] # 这里由nginx-dm改为了nginx-dm1
topologyKey: kubernetes.io/hostname
(10) 再次创建Deployment资源对象
[root@master 17-scheduling]# kubectl apply -f nginx-podaffinity.yaml
deployment.apps/nginx-pa-deploy created
(11) 查看Pod调度情况
由于集群中不存在标签为app in nginx-dm1的Pod,因此本Pod无法找到对应的Node,无法调度,将一直处于Pending状态。通过kubectl describe可以查看Pod失败详情。
# Pod处于Pending状态
[root@master 17-scheduling]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-7c7bbf4cc9-mzmvn 1/1 Running 0 2m49s 10.42.1.51 worker1 <none> <none>
nginx-pa-deploy-85f6d847ff-9vdk8 0/1 Pending 0 6s <none> <none> <none> <none>
# 查看pod详情,发现是由于不满足pod亲和性规则导致
[root@master 17-scheduling]# kubectl describe pod nginx-pa-deploy-85f6d847ff-9vdk8
Name: nginx-pa-deploy-85f6d847ff-9vdk8
(中间略。。。)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 13s default-scheduler 0/3 nodes are available: 3 node(s) didn't match pod affinity rules. preemption: 0/3 nodes are available: 3 Preemption is not helpful for scheduling.
4.5 PodAntiAffinity Pod反亲和性
与Pod亲和性相反,Pod反亲和性可以根据其他Pod的标签,避免将Pod调度到与有这些标签的Pod所在节点上。
和Node亲和性、Pod亲和性相似,Pod反亲和性也有强制反亲和性(requiredDuringSchedulingIgnoredDuringExecution)与偏好反亲和性(preferredDuringSchedulingIgnoredDuringExecution)两种,下面的例子演示Pod强制反亲和性。
4.5.1 示例:Pod强制反亲和性
(1) 先清理之前示例创建的资源
[root@master 17-scheduling]# kubectl get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deploy 1/1 1 1 19m
nginx-pa-deploy 0/1 1 0 16m
[root@master 17-scheduling]# kubectl delete deploy nginx-deploy nginx-pa-deploy
deployment.apps "nginx-deploy" deleted
deployment.apps "nginx-pa-deploy" deleted
[root@master 17-scheduling]# kubectl get pod,deploy
No resources found in default namespace.
[root@master 17-scheduling]# kubectl get node
NAME STATUS ROLES AGE VERSION
master Ready control-plane,master 3h36m v1.29.6+k3s1
worker1 Ready <none> 3h35m v1.29.6+k3s1
worker2 Ready <none> 3h34m v1.29.6+k3s1
(2) 创建一个普通的Deployment
创建一个带有app=nginx-dm标签的Deployment,包含1个Pod副本。
[root@master 17-scheduling]# cat nginx-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deploy
labels:
app: nginx-dm
spec:
selector:
matchLabels:
app: nginx-dm
replicas: 1
template:
metadata:
labels:
app: nginx-dm
spec:
containers:
- name: nginx
image: nginx:1.27.3
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
(3) 创建Deployment资源
[root@master 17-scheduling]# kubectl apply -f nginx-deploy.yaml
deployment.apps/nginx-deploy created
(4) 查看Pod调度情况
Pod创建成功,可以看到被调度到了worker2上。
这里的Pod调度和亲和性、反亲和性无关,因为Deployment资源清单文件还没有和亲和性、反亲和性有关的描述。
[root@master 17-scheduling]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-7c7bbf4cc9-5r2zs 1/1 Running 0 4s 10.42.2.66 worker2 <none> <none>
(5) 编写具有反亲和性规则的Deployment资源清单文件
Pod反亲和性与Pod亲和性规则非常相似,下面仅把nginx-podaffinity.yaml的spec.template.spec.affinity.podAffinity改为了podAntiAffinity。
[root@master 17-scheduling]# cat nginx-podantiaffinity.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-paa-deploy
labels:
app: nginx-paa-dm
spec:
replicas: 1
selector:
matchLabels:
app: nginx-paa-dm
template:
metadata:
labels:
app: nginx-paa-dm
spec:
containers:
- name: nginx
image: nginx:1.27.3
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
affinity:
podAntiAffinity: # pod亲和性例子中,这个字段为podAffnitify
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values: ["nginx-dm"]
topologyKey: kubernetes.io/hostname
(6) 创建具有反亲和性规则的Deployment
[root@master 17-scheduling]# kubectl apply -f nginx-podantiaffinity.yaml
deployment.apps/nginx-paa-deploy created
(7) 查看Pod调度情况
可以看到新创建的Pod被调度到了与标签为app=nginx-dm的Pod不同的节点上,这就是Pod反亲和性的作用。
[root@master 17-scheduling]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-7c7bbf4cc9-5r2zs 1/1 Running 0 45s 10.42.2.66 worker2 <none> <none>
nginx-paa-deploy-f4d6c8585-fvslw 1/1 Running 0 4s 10.42.1.53 worker1 <none> <none>
(8) 如果我们把app=nginx-dm的Deployment副本改为2,Pod将调度到两个节点上。由于本集群有三个工作节点,再次运行具有反亲和性规则的Deployment,将只能调度到不运行app=nginx-dm的节点上。这里不再演示。
4.6 亲和性使用场景
节点亲和性(Node Affinity)
资源隔离:将某些节点专门用于运行特定类型的工作负载,例如GPU负载或数据库,设置节点亲和性的pod才会被调度到该节点。
高可用性:可以将多个实例分布在不同的节点上, 以提高容错能力。
区域亲和性:可以通过设置节点亲和性将pod调度到特定的区域或机架上。
Pod 亲和性(Pod Affinity)
减少网络延迟:可以设置pod亲和性,让相关的pod调度到同一节点,如缓存和数据库pod设置亲和性,可以调度到同一节点。
共享资源:将需要资源共享的Pod放在同一节点,可提高性能。
Pod反亲和性(Pod Anti-Affinity)
高可用性:设置反亲和性,将多个实例分布在不同的节点,以提高容错能力。
避免资源竞争:将资源密集型的pod分散到不同的node,减少资源竞争。
5 污点(Taints)和容忍度(Tolerations)
污点和容忍度是用于控制Pod调度的机制,允许将某些节点标记为不受欢迎,并指定哪些Pod可以容忍这些标记。
为节点打了污点,则只有配置了容忍度的Pod才会调度到该节点,其他Pod不会被调度到该节点。
5.1 污点(Taints)
污点是节点的属性,用于标记节点,使得默认情况下没有容忍度的pod无法调度到该节点。
污点是键值对数据,其格式为key=value:effect,这是k3s中的声明式授权规则的一种表示方式,常用于定义访问控制策略。
其中,key=value表示授权规则的条件,key是键,value是值,effect表示应该执行什么操作。
5.1.1 污点effect的三种取值和作用
NoSchedule:硬排斥,Pod不能被调度到该节点上,但已经运行在该节点上的Pod不会被驱逐。
PreferNoSchedule:软排斥,调度器会尽量避免将Pod调度到该节点上,但不是强制的。
NoExecute:Pod不能被调度到该节点上,而且已经运行在该节点上的Pod也会被驱逐。
5.1.2 污点操作
为节点添加单个污点kubectl taint node master node-type=dev:NoExecute
为节点添加多个污点kubectl taint node master node-type1=dev1:NoExecutekubectl taint node master node-type2=dev2:NoExecute
为节点删除单个污点kubectl taint node master node-type1=dev1:NoExecute-
为节点删除多个污点kubectl taint node master node-type1- node-type2-
查看节点的污点,master节点没有污点,worker1节点有两个污点
[root@master ~]# kubectl describe node master
Name: master
(。。。部分内容略。。。)
Taints: <none>
(。。。部分内容略。。。)
[root@master ~]# kubectl describe node worker1
Name: worker1
(。。。部分内容略。。。)
Taints: node-type1=dev1:NoExecute
node-type2=dev2:NoExecute
(。。。部分内容略。。。)
5.2 容忍度(Tolerations)
容忍度是Pod的属性,用于声明Pod可以容忍哪些污点。如果Pod的容忍度与节点的污点匹配,Pod就可以被调度到该节点上。
5.2.1 示例:污点的驱逐效果
以下示例演示集群中的三个节点,各自运行着1个Pod,对其中一个Node打污点,该Node上的Pod根据污点规则,被驱逐到其他节点。
(1) 查看集群信息
# 集群中有三个节点
[root@master 5-deployment]# kubectl get node
NAME STATUS ROLES AGE VERSION
master Ready control-plane,master 5h7m v1.29.6+k3s1
worker1 Ready <none> 5h6m v1.29.6+k3s1
worker2 Ready <none> 5h5m v1.29.6+k3s1
# 目前还没有Deployment
[root@master 5-deployment]# kubectl get deploy
No resources found in default namespace.
(2) 创建Deployment资源清单文件
创建一个具有3个副本的Deployment资源清单文件。
[root@master 5-deployment]# cat nginx-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deploy
labels:
app: nginx-dm
spec:
selector:
matchLabels:
app: nginx-dm
replicas: 3
template:
metadata:
labels:
app: nginx-dm
spec:
containers:
- name: nginx
image: nginx:1.27.3
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
(3) 创建Deployment资源
# 创建资源
[root@master 5-deployment]# kubectl apply -f nginx-deploy.yaml
deployment.apps/nginx-deploy created
# 创建成功后,可以看到三个pod分别被调度到master、worker1和worker2上。
[root@master 5-deployment]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-7c7bbf4cc9-8h4mc 1/1 Running 0 5s 10.42.1.56 worker1 <none> <none>
nginx-deploy-7c7bbf4cc9-kvvmd 1/1 Running 0 5s 10.42.0.6 master <none> <none>
nginx-deploy-7c7bbf4cc9-p78zr 1/1 Running 0 5s 10.42.2.67 worker2 <none> <none>
(4) 为节点添加污点
为节点master添加污点,效果为NoExecute,Pod不能调度到该节点上,且已经运行在该节点上的Pod也会被驱逐。
[root@master 5-deployment]# kubectl taint node master node-type=dev:NoExecute
node/master tainted
(5) 查看驱逐效果
可以看到master节点上已经没有运行的pod,worker2节点新增了一个pod。
[root@master 5-deployment]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-7c7bbf4cc9-8h4mc 1/1 Running 0 30s 10.42.1.56 worker1 <none> <none>
nginx-deploy-7c7bbf4cc9-p78zr 1/1 Running 0 30s 10.42.2.67 worker2 <none> <none>
nginx-deploy-7c7bbf4cc9-zf666 1/1 Running 0 3s 10.42.2.68 worker2 <none> <none>
5.3 综合示例:污点和容忍度
(1) 查看集群情况
# 集群中有三个节点
[root@master 17-scheduling]# kubectl get node
NAME STATUS ROLES AGE VERSION
master Ready control-plane,master 5h26m v1.29.6+k3s1
worker1 Ready <none> 5h25m v1.29.6+k3s1
worker2 Ready <none> 5h25m v1.29.6+k3s1
# master节点有一个污点
[root@master 17-scheduling]# kubectl describe node master
Name: master
(。。。部分内容略。。。)
Taints: node-type=dev:NoExecute
(。。。部分内容略。。。)
# 目前有三个没有设置容忍度规则的Pod,都未被调度到master节点上
[root@master 17-scheduling]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-7c7bbf4cc9-8h4mc 1/1 Running 0 19m 10.42.1.56 worker1 <none> <none>
nginx-deploy-7c7bbf4cc9-p78zr 1/1 Running 0 19m 10.42.2.67 worker2 <none> <none>
nginx-deploy-7c7bbf4cc9-zf666 1/1 Running 0 18m 10.42.2.68 worker2 <none> <none>
(2) 编写带Pod容忍度规则的Deployment资源清单文件
创建一个带Pod容忍度规则的Deployment,该Deployment有3个副本。容忍度规则为匹配node-type=dev:NoExecute污点的节点。
tolerationSeconds表示容忍时间,超过这个时间,将继续根据容忍度规则匹配污点,如果能匹配上,则继续在该节点上运行Pod,直到超时后重新检查,如果容忍度不能匹配上该节点,Pod将被驱逐。
[root@master 17-scheduling]# cat nginx-tolerations.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-tol-deploy
labels:
app: nginx-tol-dm
spec:
selector:
matchLabels:
app: nginx-tol-dm
replicas: 3
template:
metadata:
labels:
app: nginx-tol-dm
spec:
containers:
- name: nginx
image: nginx:1.27.3
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
tolerations:
- key: "node-type"
operator: "Equal"
value: "dev"
effect: "NoExecute"
tolerationSeconds: 60
(3) 创建带Pod容忍度规则的Deployment
[root@master 17-scheduling]# kubectl apply -f nginx-tolerations.yaml
deployment.apps/nginx-tol-deploy created
(4) 查看Pod调度情况
创建的2个pod,分别调度到了2个节点,master/worker1/worker2。master设置了污点,Pod设置了容忍度,两者能匹配上,所以Pod才会调度到master节点。
[root@master 17-scheduling]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-7c7bbf4cc9-8h4mc 1/1 Running 0 19m 10.42.1.56 worker1 <none> <none>
nginx-deploy-7c7bbf4cc9-p78zr 1/1 Running 0 19m 10.42.2.67 worker2 <none> <none>
nginx-deploy-7c7bbf4cc9-zf666 1/1 Running 0 19m 10.42.2.68 worker2 <none> <none>
nginx-tol-deploy-748ff4b455-cxnbf 1/1 Running 0 6s 10.42.1.59 worker1 <none> <none>
nginx-tol-deploy-748ff4b455-p6rvd 1/1 Running 0 6s 10.42.2.69 worker2 <none> <none>
nginx-tol-deploy-748ff4b455-tz8s2 1/1 Running 0 6s 10.42.0.7 master <none> <none>
(5) 查看容忍时间tolerationSeconds效果
# Pod在master节点上运行了60s,到达tolerationSeconds设置的时间,因此节点上的Pod先被停止运行,再继续进行容忍度和污点的匹配,如果能匹配上,则重新创建新的Pod,运行在本节点上。
[root@master 17-scheduling]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-7c7bbf4cc9-8h4mc 1/1 Running 0 20m 10.42.1.56 worker1 <none> <none>
nginx-deploy-7c7bbf4cc9-p78zr 1/1 Running 0 20m 10.42.2.67 worker2 <none> <none>
nginx-deploy-7c7bbf4cc9-zf666 1/1 Running 0 20m 10.42.2.68 worker2 <none> <none>
nginx-tol-deploy-748ff4b455-2kkjr 0/1 ContainerCreating 0 0s <none> master <none> <none>
nginx-tol-deploy-748ff4b455-cxnbf 1/1 Running 0 60s 10.42.1.59 worker1 <none> <none>
nginx-tol-deploy-748ff4b455-p6rvd 1/1 Running 0 60s 10.42.2.69 worker2 <none> <none>
nginx-tol-deploy-748ff4b455-tz8s2 1/1 Terminating 0 60s 10.42.0.7 master <none> <none>
# master节点的污点没有任何变化,因此新创建的Pod仍可以在本节点运行。
[root@master 17-scheduling]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-7c7bbf4cc9-8h4mc 1/1 Running 0 20m 10.42.1.56 worker1 <none> <none>
nginx-deploy-7c7bbf4cc9-p78zr 1/1 Running 0 20m 10.42.2.67 worker2 <none> <none>
nginx-deploy-7c7bbf4cc9-zf666 1/1 Running 0 20m 10.42.2.68 worker2 <none> <none>
nginx-tol-deploy-748ff4b455-2kkjr 1/1 Running 0 4s 10.42.0.8 master <none> <none>
nginx-tol-deploy-748ff4b455-cxnbf 1/1 Running 0 64s 10.42.1.59 worker1 <none> <none>
nginx-tol-deploy-748ff4b455-p6rvd 1/1 Running 0 64s 10.42.2.69 worker2 <none> <none>
# 下一个60s,继续删除master节点上的Pod,再次进行容忍度检查。
[root@master 17-scheduling]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-7c7bbf4cc9-8h4mc 1/1 Running 0 21m 10.42.1.56 worker1 <none> <none>
nginx-deploy-7c7bbf4cc9-p78zr 1/1 Running 0 21m 10.42.2.67 worker2 <none> <none>
nginx-deploy-7c7bbf4cc9-zf666 1/1 Running 0 21m 10.42.2.68 worker2 <none> <none>
nginx-tol-deploy-748ff4b455-5nffz 0/1 ContainerCreating 0 1s <none> master <none> <none>
nginx-tol-deploy-748ff4b455-cxnbf 1/1 Running 0 2m1s 10.42.1.59 worker1 <none> <none>
nginx-tol-deploy-748ff4b455-p6rvd 1/1 Running 0 2m1s 10.42.2.69 worker2 <none> <none>
# Pod又被调度到了master上
# 如果想让Pod在到达tolerationSeconds时间后不被调度到master上,可以动态调整Pod的容忍度:
# kubectl patch pod pod-name -p '{"spec":{"tolerations":[]}}'
[root@master 17-scheduling]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-7c7bbf4cc9-8h4mc 1/1 Running 0 21m 10.42.1.56 worker1 <none> <none>
nginx-deploy-7c7bbf4cc9-p78zr 1/1 Running 0 21m 10.42.2.67 worker2 <none> <none>
nginx-deploy-7c7bbf4cc9-zf666 1/1 Running 0 21m 10.42.2.68 worker2 <none> <none>
nginx-tol-deploy-748ff4b455-5nffz 1/1 Running 0 3s 10.42.0.9 master <none> <none>
nginx-tol-deploy-748ff4b455-cxnbf 1/1 Running 0 2m3s 10.42.1.59 worker1 <none> <none>
nginx-tol-deploy-748ff4b455-p6rvd 1/1 Running 0 2m3s 10.42.2.69 worker2 <none> <none>
(6) 改变master节点的污点情况
为master节点新增一个污点
[root@master 17-scheduling]# kubectl taint node master node-locate=area1:NoExecute
node/master tainted
(7) 再次查看Pod调度情况
由于Pod的容忍度无法匹配所有的污点,Pod不会被调度到master节点运行,已经运行的Pod也被驱逐到了其他节点。
[root@master 17-scheduling]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-7c7bbf4cc9-8h4mc 1/1 Running 0 21m 10.42.1.56 worker1 <none> <none>
nginx-deploy-7c7bbf4cc9-p78zr 1/1 Running 0 21m 10.42.2.67 worker2 <none> <none>
nginx-deploy-7c7bbf4cc9-zf666 1/1 Running 0 21m 10.42.2.68 worker2 <none> <none>
nginx-tol-deploy-748ff4b455-cxnbf 1/1 Running 0 2m9s 10.42.1.59 worker1 <none> <none>
nginx-tol-deploy-748ff4b455-hldfr 1/1 Running 0 6s 10.42.2.70 worker2 <none> <none>
nginx-tol-deploy-748ff4b455-p6rvd 1/1 Running 0 2m9s 10.42.2.69 worker2 <none> <none>
(8) 调整Pod容忍度
如果想让Pod重新调度到master节点,可以修改Pod的容忍度。
Pod容忍度可以动态调整,也可以修改yaml文件调整。
动态调整使用kubectl语句:kubectl patch pod my-pod -p '{"spec":{"tolerations":[{"key":"node-locate","operator":"Equal","value":"area1","effect":"NoExecute"}]}}'
下面为静态调整,即修改yaml文件:
[root@master 17-scheduling]# cp nginx-tolerations.yaml nginx-tolerations2.yaml
[root@master 17-scheduling]# vim nginx-tolerations2.yaml
[root@master 17-scheduling]# cat nginx-tolerations2.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-tol2-deploy
labels:
app: nginx-tol2-dm
spec:
selector:
matchLabels:
app: nginx-tol2-dm
replicas: 3
template:
metadata:
labels:
app: nginx-tol2-dm
spec:
containers:
- name: nginx
image: nginx:1.27.3
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
tolerations:
- key: "node-type"
operator: "Equal"
value: "dev"
effect: "NoExecute"
tolerationSeconds: 60
- key: "node-locate" #添加一个新的容忍度规则
operator: "Equal"
value: "area1"
effect: "NoExecute"
(9) 更新Pod配置
[root@master 17-scheduling]# kubectl apply -f nginx-tolerations2.yaml
deployment.apps/nginx-tol2-deploy created
(10) 再次查看Pod调度情况
可以看到,master上又有Pod被调度过来。
如果Node设置了多个污点,则Pod也需要设置多个容忍度规则。只有所有的污点都被容忍,Pod才会被调度到节点上。
[root@master 17-scheduling]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deploy-7c7bbf4cc9-8h4mc 1/1 Running 0 46m 10.42.1.56 worker1 <none> <none>
nginx-deploy-7c7bbf4cc9-p78zr 1/1 Running 0 46m 10.42.2.67 worker2 <none> <none>
nginx-deploy-7c7bbf4cc9-zf666 1/1 Running 0 45m 10.42.2.68 worker2 <none> <none>
nginx-tol-deploy-748ff4b455-cxnbf 1/1 Running 0 26m 10.42.1.59 worker1 <none> <none>
nginx-tol-deploy-748ff4b455-hldfr 1/1 Running 0 7m51s 10.42.2.70 worker2 <none> <none>
nginx-tol-deploy-748ff4b455-p6rvd 1/1 Running 0 26m 10.42.2.69 worker2 <none> <none>
nginx-tol2-deploy-8dcc4488d-2ckmd 1/1 Running 0 5s 10.42.0.26 master <none> <none>
nginx-tol2-deploy-8dcc4488d-7h6cx 1/1 Running 0 5s 10.42.2.71 worker2 <none> <none>
nginx-tol2-deploy-8dcc4488d-x6zz9 1/1 Running 0 5s 10.42.1.60 worker1 <none> <none>
5.4 污点和容忍度使用场景
污点(Taints)和容忍度(Tolerations)在Kubernetes中非常灵活,适用于多种场景。以下举一些常见的应用场景:
- 资源预留
当希望某些节点专门用于运行特定的系统Pod或关键应用时,可以使用污点来防止其他Pod被调度到这些节点上。
如:为运行数据库的节点添加污点,然后在数据库pod添加容忍度,只有数据库相关的pod才会被调度到这些节点上。 - 节点隔离
当希望将某些类型的工作负载隔离到特定的节点上时,可以使用污点和容忍度来实现。
如:为运行GPU的节点添加污点,只有设置容忍度的pod才会被调度到GPU节点上。 - 自动驱逐
当节点资源不足时,希望能自动驱逐某些Pod,就可以使用带有NoExecute效果的污点。
如:希望在节点内存压力大时自动驱逐低优先级的Pod,为节点添加标签memory-pressure=true:NoExecute,低优先级的pod将被驱逐。 - 维护模式
当需要对节点进行维护时,可以使用污点来驱逐节点上的Pod。 - 多租户环境
在多租户环境中,可以使用污点和容忍度来隔离不同租户的Pod。
更多推荐


所有评论(0)