前一篇文章已经带你们深入了解kubernetes中调度、抢占和驱逐的相关概念。这篇文章主要详细介绍Pod的几种常用调度策略,并根据实战操作来加深原理理解,在生产环境也经常使用。
Pod 的调度策略主要包括:
nodeSelector 亲和性与反亲和性 nodeName Pod 拓扑分布约束 污点与容忍性
1. Pod调度概述
调度器通过 Kubernetes 的监测(Watch)机制来发现集群中新创建且尚未被调度到节点上的 Pod。调度的主要任务是将Pod分配到集群中的合适的节点上。调度器根据Pod的需求(如CPU、内存、存储等)和节点的资源状况(如可用CPU、内存、节点标签等)来决定Pod部署在哪个节点上。
调度已经在前面文章讲解过了,就不详细展开了。下面就详细介绍一下具体的调度策略
2. 调度策略详解
2.1 nodeSelector
nodeSelector
是 Kubernetes 中最简单、最直观的调度机制,它基于节点标签进行 Pod 调度。节点标签是 Kubernetes 用于标识节点属性的键值对,调度器会根据这些标签来决定将 Pod 调度到哪个节点。
工作原理:
每个 Kubernetes 节点可以具有多个标签(labels)。标签是 key: value 的形式,通常表示节点的特征,如硬件类型、地区、环境等。
Pod 的 nodeSelector 字段指定一组标签,调度器会筛选出具有这些标签的节点,然后将 Pod 调度到这些节点。
使用场景:
环境隔离:比如,你有一些节点专门用于生产环境,其他节点用于测试环境,使用 nodeSelector 来确保 Pod 只在生产节点上运行。 硬件要求:例如,某些应用需要 SSD 磁盘或特定的 CPU 类型,你可以使用 nodeSelector 来确保 Pod 调度到符合这些硬件要求的节点。
实战操作:
1、给节点打上标签
# 给k8s-node2节点打上ssd标签
kubectl label node k8s-node2 disktype=ssd
# 查看标签
kubectl get node k8s-node1 --show-labels
2、编写yaml文件
【温馨提示】生产环境不要直接运行pod,这里只是为了演示效果
vim nodeSelector.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
nodeSelector:
disktype: ssd
containers:
- name: nginx
image: docker.m.daocloud.io/nginx:1.25
运行pod
kubectl apply -f nodeSelector.yaml
3、查看pod调度情况
[root@k8s-master data]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 60s 10.224.169.149 k8s-node2 <none> <none>
已经根据节点标签将pod调度到node2节点。
nodeSelector的优缺点:
优点:简单高效。 缺点:灵活性差,无法处理复杂的调度需求。
2.2 亲和性与反亲和性
亲和性(Affinity
)和反亲和性(Anti-Affinity
)是比 nodeSelector
更强大、更灵活的调度策略。它们允许用户根据更多的条件进行节点选择,比如节点的标签、Pod 的存在与否等。
亲和性(Affinity)用于指定 Pod 需要尽量调度到某些节点上,基于节点标签、Pod 与节点的关系等。
反亲和性(Anti-Affinity)用于指定 Pod 不应该调度到某些节点,或者应该尽量避免和其他 Pod 调度到同一个节点上。
Kubernetes 中有两种亲和性:
节点亲和性(Node Affinity) Pod 亲和性与反亲和性(Pod Affinity & Anti-Affinity)。
节点亲和性
节点亲和性是 nodeSelector
的增强版,允许基于更复杂的规则选择节点。
实战操作
1、编写yaml文件
vim nodeAffinity.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx2
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: disktype
operator: In
values:
- ssd
containers:
- name: nginx2
image: docker.m.daocloud.io/nginx:1.25
确保 Pod 只会调度到具有 disktype=ssd标签的节点。
2、运行pod
kubectl apply -f nodeAffinity.yaml
3、查看pod调度情况
[root@k8s-master data]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 27m 10.224.169.149 k8s-node2 <none> <none>
nginx2 1/1 Running 0 3m16s 10.224.169.154 k8s-node2 <none> <none>
已经将pod调度到预期的node上
字段解释:
nodeAffinity
是定义节点选择规则的字段。通过requiredDuringSchedulingIgnoredDuringExecution(
必须满足调度时条件)和preferredDuringSchedulingIgnoredDuringExecution
(调度时首选,但不强制)来定义硬性和软性约束。matchExpressions
支持使用 In、NotIn、Exists 等操作符,这些操作符使得你可以做更复杂的条件匹配。
operator 字段操作符说明:
操作符 | 说明 |
---|---|
In | 指定值必须在给定的列表中 |
NotIn | 指定值不在给定的列表中 |
Exists | 指定键存在,不关心值 |
DoesNotExist | 指定键不存在 |
Pod 亲和性
Pod 亲和性允许你指定 Pod 希望与其他 Pod 一起调度的偏好,而反亲和性则表示 Pod 希望避免与某些 Pod 一起调度。
1、给前面创建的pod打上app=nginx1的标签
# 给使用nodeSelector创建的nginx打上标签
kubectl label pod nginx app=nginx1
2、编写yaml文件
vim podAffinity.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx3
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution: # 满足条件才调度
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- nginx1
topologyKey: "kubernetes.io/hostname"
containers:
- name: nginx3
image: docker.m.daocloud.io/nginx:1.25
3、运行pod
kubectl apply -f podAffinity.yaml
4、查看调度情况
[root@k8s-master data]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 4h32m 10.224.169.149 k8s-node2 <none> <none>
nginx2 1/1 Running 0 4h8m 10.224.169.154 k8s-node2 <none> <none>
nginx3 1/1 Running 0 14s 10.224.169.153 k8s-node2 <none> <none>
可见pod已经调度到node2上,现在3个都运行在node2上,接下来测试一下反亲和性
Pod 反亲和性
要求 Pod 不与特定的 Pod 调度到同一节点上,通常用于防止某些类型的 Pod 集中到同一节点上,避免单点故障。
1、编写yaml文件
vim podAntiAffinity.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx4
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions: # 不要和具有app=nginx1标签的Pod在同一节点
- key: app
operator: In
values:
- nginx1
topologyKey: "kubernetes.io/hostname"
containers:
- name: app
image: docker.m.daocloud.io/nginx:1.25
2、运行pod
kubectl apply -f podAntiAffinity.yaml
3、查看调度情况
[root@k8s-master data]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 4h37m 10.224.169.149 k8s-node2 <none> <none>
nginx2 1/1 Running 0 4h13m 10.224.169.154 k8s-node2 <none> <none>
nginx3 1/1 Running 0 5m26s 10.224.169.153 k8s-node2 <none> <none>
nginx4 1/1 Running 0 10s 10.224.36.69 k8s-node1 <none> <none>
可以看到新建的nginx4避免与标签为 app=nginx1的 Pod 调度到同一节点上,所以被调度到node1上,验证了pod的反亲和性
亲和性和反亲和性优缺点:
优点:
灵活性强:支持多条件、复杂的调度规则,可以基于节点标签、Pod 标签、拓扑结构等进行细粒度控制。 支持跨节点调度:可以控制 Pod 在集群中的分布,增强应用的可用性和容错性。
缺点:
配置复杂:调度规则更加复杂,理解和配置起来需要更多的时间。 可能影响调度效率:当使用大量的亲和性和反亲和性规则时,调度器需要更多时间来评估每个节点的匹配情况。
2.3 nodeName
nodeName 是 Kubernetes 中一种最简单且强制性的调度方式,它允许你直接指定一个节点来运行 Pod,而无需依赖调度器的调度决策。nodeName 适用于一些特殊场景,比如需要将 Pod 调度到特定的节点(例如硬件节点,或特定的节点上有特定设备,如 GPU)。
实战操作:
1、编写yaml
vim nodeName.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx5
spec:
nodeName: k8s-node1
containers:
- name: nginx5
image: docker.m.daocloud.io/nginx:1.25
2、运行pod
kubectl apply -f nodeName.yaml
3、查看调度情况
[root@k8s-master data]# kubectl get pod nginx5 -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx5 1/1 Running 0 47s 10.224.36.70 k8s-node1 <none> <none>
可以看到Pod nginx5 会被直接调度到 node1节点上。
nodeName优缺点:
优点:简单、直观,适用于需要特定硬件或设备的场景。 缺点:失去调度器的灵活性,不适用于动态环境。
2.4 Pod 拓扑分布约束
Pod 拓扑分布约束用于保证 Pod 在多个节点、区域或故障域中的均衡分布。通过设置 topologySpreadConstraints,可以确保 Pod 在集群中分布更加均衡,避免 Pod 集中到某个节点或某个区域,从而增强集群的高可用性和容灾能力。
apiVersion: v1
kind: Pod
metadata:
name: nginx6
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: nginx
containers:
- name: nginx6
image: docker.m.daocloud.io/nginx:1.25
[root@k8s-master data]# kubectl get pod nginx6 -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx6 1/1 Running 0 37s 10.224.36.71 k8s-node1 <none> <none>
使用deployment来演示可能效果更明显
topologySpreadConstraints 保证 nginx Pod 在不同的主机上均匀分布。maxSkew 控制 Pod 在各个节点之间的最大不平衡度,而 topologyKey 是表示分布维度的关键字段,kubernetes.io/hostname 表示按主机名进行分布。
优缺点:
优点:保证 Pod 在多个节点、区域的均衡分布,提高高可用性。 缺点:配置较复杂,可能对调度性能产生影响。
2.5 污点与容忍性
污点(Taints
)和容忍性(Tolerations
)是 Kubernetes 的一对调度机制,用于控制 Pod 是否能调度到带有某些特定条件的节点。节点可以加上污点,只有设置了相应容忍性的 Pod 才能调度到这些节点。
污点(Taints) 是对节点的标记,表示该节点不希望接受不符合条件的 Pod。容忍(Tolerations) 是 Pod 上的标记,表示 Pod 能够“容忍”某些污点。
实战操作:
1、给节点加污点:
# 在节点1上添加污点
kubectl taint nodes k8s-node1 key=value:NoSchedule
【温馨提示】设置污点后,新建的pod如果没有设置容忍,都无法调度到该节点,已存在的pod不受影响。
2、Pod 上设置容忍性:
vim tolerations.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx7
spec:
tolerations: # 配置容忍规则,需要完全匹配才可以调度到有污点的节点
- key: "key"
operator: "Equal"
value: "value"
effect: "NoSchedule"
containers:
- name: nginx7
image: docker.m.daocloud.io/nginx:1.25
3、查看调度情况
[root@k8s-master data]# kubectl get pod nginx7 -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx7 1/1 Running 0 32s 10.224.36.72 k8s-node1 <none> <none>
nginx7被调度到设置了污点的节点,说明tolerations生效了,容忍了污点。
4、去除污点(taint)
kubectl taint node k8s-node1 key-
优缺点:
优点:提供了灵活的节点隔离和调度控制,适用于隔离特定类型的工作负载或维护节点。 缺点:配置复杂,滥用污点可能导致资源浪费或 Pod 调度错误。
总结
Kubernetes 提供了多种调度策略,可以根据不同的需求灵活选择:
nodeSelector:简单的节点标签匹配调度。 亲和性与反亲和性:更灵活的调度控制,基于节点和 Pod 之间的关系进行调度。 nodeName:直接指定节点进行调度。 Pod 拓扑分布约束:确保 Pod 在集群中的均衡分布,增强高可用性。 污点与容忍性:通过污点和容忍性控制哪些 Pod 可以调度到哪些节点。
通过合理组合这些调度策略,你可以优化 Pod 的资源分配、提升集群的可靠性和性能。
有缘相遇,不妨点个关注!
END
往期推荐
1 | |
2 | |
3 |
4 | |
5 | |
6 |