将POD运行在指定的节点上,方式有:
nodeSelector
亲和性和反亲和性:nodeAffinity、podAffinity
nodeName
nodeSelector是PodSpec的一个字段,是包含键值对的映射。为了使Pod在节点上运行,节点的标签必须包含PodSpec中nodeSelector中的键值对。节点的标签可以与nodeSelector中的键值对一样,也可以多于nodeSelector描述的键值对。
nodeSelector提供了以重非常简单的方法将Pod约束到特定标签的节点上。而亲和性/反亲和性极大地扩展了可以表达约束的类型。包括:
1.语言更具表现力(不仅仅是“对完全匹配规则的AND”)
2.可以使规则是“软需求”/“偏好”,而不是影星要求。因此,如果调度器无法满足该要求,仍然调度该Pod
3.可以使用节点上存在的Pod的标签来进行约束,而不是使用节点本身的标签进行约束。来约束哪些Pod可以或者不可以被放置在同一节点。
节点亲和性/反亲和性
节点亲和性概念类似于nodeSelector,它可以根据节点上的标签来约束Pod可以调度到哪些节点。
目前有两种类型的节点亲和性:
requireDuringSchedulingIgnoreDuringExecution(在调度过程中要求存在,在执行过程中忽略),可是视作为“硬需求”。
preferredDuringSchedulingIgnoreDuringExecution(在调度过程中作为首选,在执行过程中忽略),可是视作为“软需求”。
requireDuringScheduling。调度期间,将Pod调度到一个节点上时,必须满足规则。
preferredDuringScheduling。调度期间,将定义的规则视为首选(偏好),如果不满足也不影响调度。
IgnoreDuringExecution。如果Pod在执行期间(Pod已经在节点上运行),而此时节点的标签发生变更,变得不满足Pod的亲和性规则。那么节点继续在节点上运行。
节点亲和性通过 PodSpec 的 affinity
字段下的 nodeAffinity
字段进行指定
Example
apiVersion: v1
kind: Pod
metadata:
name: with-node-affinity
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/e2e-az-name
operator: In
values:
- e2e-az1
- e2e-az2
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: another-node-label-key
operator: In
values:
- another-node-label-value
containers:
- name: with-node-affinity
image: k8s.gcr.io/pause:2.0
此节点亲和性规则表示,Pod只能放置在具有标签键kubernetes.io/e2e-az-name,值为e2e-az1或e2e-az2的节点上。另外在满足这些标准的节点中,具有标签为another-node-label-key,标签值为another-node-label-value的节点应该优先使用。
操作符
新的节点亲和性语法支持以下操作符:In、NotIn、Exists、DoesNotExist、Gt、Lt。
可以使用NotIn、DoesNotExist来实现节点反亲和性。或者使用节点污点将Pod从特定节点中驱逐。
匹配逻辑
1.如果同时制定了nodeSelector和nodeAffinity。则两者都必须满足,才能将Pod调度到候选节点上。
2.如果指定了多个与nodeAffinity关联的nodeSelectorTerms。则如果其中一个nodeSelectorTerms满足的话,Pod将可以调度到节点上。
3.如果指定了多个与nodeSelectorTerms关联的matchExpressions,则只有当所有matchExpressions满足的话,Pod才可以调度到节点上。
4.如果修改或删除了Pod所在节点的标签,在运行中的Pod不会被删除。这与上文提到的,节点亲和性类型中的"IgnoreDuringExecution"解释一致。
preferredDuringSchedulingIgnoredDuringExecution 中的 weight 字段值的 范围是 1-100。 对于每个符合所有调度要求(资源请求、RequiredDuringScheduling 亲和性表达式等) 的节点,调度器将遍历该字段的元素来计算总和,并且如果节点匹配对应的 MatchExpressions,则添加“权重”到总和。 然后将这个评分与该节点的其他优先级函数的评分进行组合。 总分最高的节点是最优选的。
Pod亲和性/反亲和性
Pod 间亲和性与反亲和性使你可以 基于已经在节点上运行的 Pod 的标签 来约束 Pod 可以调度到的节点,而不是基于节点上的标签。与节点不同,因为 Pod 是命名空间限定的(因此 Pod 上的标签也是命名空间限定的), 因此作用于 Pod 标签的标签选择算符必须指定选择算符应用在哪个命名空间。
注意1:Pod 间亲和性与反亲和性需要大量的处理,这可能会显著减慢大规模集群中的调度。 不建议在超过数百个节点的集群中使用。
注意2:Pod 反亲和性需要对节点进行一致的标记,即集群中的每个节点必须具有适当的标签能够匹配 topologyKey。如果某些或所有节点缺少指定的 topologyKey 标签,可能会导致意外行为。
与节点亲和性一样,当前有两种类型的 Pod 亲和性与反亲和性,即 requiredDuringSchedulingIgnoredDuringExecution
和 preferredDuringSchedulingIgnoredDuringExecution
,分别表示“硬性”与“软性”要求。
Pod 间亲和性通过 PodSpec 中 affinity 字段下的 podAffinity 字段进行指定。 而 Pod 间反亲和性通过 PodSpec 中 affinity 字段下的 podAntiAffinity 字段进行指定。
Example
apiVersion: v1
kind: Pod
metadata:
name: with-pod-affinity
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S1
topologyKey: topology.kubernetes.io/zone
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S2
topologyKey: topology.kubernetes.io/zone
containers:
- name: with-pod-affinity
image: k8s.gcr.io/pause:2.0
在这个 Pod 的亲和性配置定义了一条 Pod 亲和性规则和一条 Pod 反亲和性规则。
Pod 亲和性规则表示,仅当节点和至少一个已运行且有键为“security”且值为“S1”的标签 的 Pod 处于同一区域时,才可以将该 Pod 调度到节点上。 (更确切的说,如果节点 N 具有带有键 topology.kubernetes.io/zone
和某个值 V 的标签, 则 Pod 有资格在节点 N 上运行,以便集群中至少有一个节点具有键 topology.kubernetes.io/zone
和值为 V 的节点正在运行具有键“security”和值 “S1”的标签的 pod。)
Pod 反亲和性规则表示,如果节点处于 Pod 所在的同一可用区且具有键“security”和值“S2”的标签, 则该 pod 不应将其调度到该节点上。 (如果 topologyKey
为 topology.kubernetes.io/zone
,则意味着当节点和具有键 “security”和值“S2”的标签的 Pod 处于相同的区域,Pod 不能被调度到该节点上。)
操作符
Pod 亲和性与反亲和性的合法操作符有 In
,NotIn
,Exists
,DoesNotExist
。
topologyKey 限制
原则上,topologyKey 可以是任何合法的标签键。 然而,出于性能和安全原因,topologyKey 受到一些限制:
1.对于 Pod 亲和性而言,在 requiredDuringSchedulingIgnoredDuringExecution 和 preferredDuringSchedulingIgnoredDuringExecution 中,topologyKey 不允许为空。
2.对于 Pod 反亲和性而言,requiredDuringSchedulingIgnoredDuringExecution 和 preferredDuringSchedulingIgnoredDuringExecution 中,topologyKey 也都不可以为空。
3.对于 requiredDuringSchedulingIgnoredDuringExecution 要求的 Pod 反亲和性, 引入准入控制器 LimitPodHardAntiAffinityTopology ,确保 topologyKey 只能是 kubernetes.io/hostname。如果你希望 topologyKey 也可用于其他自定义的拓扑逻辑,你可以更改或禁用准入控制器。
4.除上述情况外,topologyKey 可以是任何合法的标签键。
除了 labelSelector 和 topologyKey,你也可以指定表示命名空间的 namespaces 队列,labelSelector 也应该匹配它 (这个与 labelSelector 和 topologyKey 的定义位于相同的级别)。 如果忽略或者为空,则namespace默认为出现亲和/反反亲和定义的pod的名称空间。
所有与 requiredDuringSchedulingIgnoredDuringExecution 亲和性与反亲和性 关联的 matchExpressions 必须满足,才能将 pod 调度到节点上。
nodeName 是节点选择约束的最简单方法,但是由于其自身限制,通常不使用它。 nodeName 是 PodSpec 的一个字段。 如果它不为空,调度器将忽略 Pod,并且给定节点上运行的 kubelet 进程尝试执行该 Pod。 因此,如果 nodeName 在 PodSpec 中指定了,则它优先于上面的节点选择方法。
使用 nodeName 来选择节点的一些限制:
如果指定的节点不存在;
如果指定的节点没有资源来容纳 Pod,Pod 将会调度失败并且其原因将显示为, 比如 OutOfmemory 或 OutOfcpu;
云环境中的节点名称并非总是可预测或稳定的。
Example
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
nodeName: kube-01
节点亲和性 是 Pod 的一种属性,它使 Pod 被吸引到一类特定的节点 (这可能出于一种偏好,也可能是硬性要求)。 污点(Taint)则相反——它使节点能够排斥一类特定的 Pod。
容忍度(Toleration)是应用于 Pod 上的,允许(但并不要求)Pod 调度到带有与之匹配的污点的节点上。
污点和容忍度(Toleration)相互配合,可以用来避免 Pod 被分配到不合适的节点上。 每个节点上都可以应用一个或多个污点,这表示对于那些不能容忍这些污点的 Pod,是不会被该节点接受的。
可以使用kubectl taint给节点新增或者删除污点:
节点新增污点:给节点 node1
增加一个污点,它的键名是 key1
,键值是 value1
,效果是 NoSchedule
。 这表示只有拥有和这个污点相匹配的容忍度的 Pod 才能够被分配到 node1
这个节点。
kubectl taint nodes node1 key1=value1:NoSchedule
节点删除污点:
kubectl taint nodes node1 key1=value1:NoSchedule-
污点类型:
NoSchedule:新的 Pod 不调度到该 Node 上,不影响正在运行的
PodPreferNoSchedule:soft 版的 NoSchedule,尽量不调度到该 Node 上
NoExecute:新的 Pod 不调度到该 Node 上,并且删除(evict)已在运行的 Pod。Pod 可以增加一个时间(tolerationSeconds)
可以在PodSpec中定义Pod的容忍度。
Example
以下两个例子都可以容忍上述新增的节点污点,使pod调度到node1上。
##key1值等于value1,且效果是不调度
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoSchedule"
##存在标签key1,且效果是不调度
tolerations:
- key: "key1"
operator: "Exists"
effect: "NoSchedule"
operator 的默认值是 Equal。
一个容忍度和一个污点相“匹配”是指它们有一样的键名和效果,并且:
如果 operator 是 Exists (此时容忍度不能指定 value),或者
如果 operator 是 Equal ,则它们的 value 应该相等
说明两种特殊情况:
1.key为空,operator为Exists。表示key、value可以是任意值;
2.如果effect为空,表示效果可以是NoSchedule、PodPreferNoSchedule、NoExecute中任意一个。
您可以给一个节点添加多个污点,也可以给一个 Pod 添加多个容忍度设置。 Kubernetes 处理多个污点和容忍度的过程就像一个过滤器:从一个节点的所有污点开始遍历, 过滤掉那些 Pod 中存在与之相匹配的容忍度的污点。余下未被过滤的污点的 effect 值决定了 Pod 是否会被分配到该节点,特别是以下情况:
通过污点和容忍度,可以灵活地让 Pod 避开 某些节点或者将 Pod 从某些节点驱逐。下面是几个使用例子:
基于污点的驱逐
前文提到过污点的 effect 值 NoExecute会影响已经在节点上运行的 Pod
当某种条件为真时,节点控制器会自动给节点添加一个污点。当前内置的污点包括:
使用这个功能特性,结合 tolerationSeconds
,Pod 就可以指定当节点出现一个 或全部上述问题时还将在这个节点上运行多长的时间。
Example
tolerations:
- key: "node.kubernetes.io/unreachable"
operator: "Exists"
effect: "NoExecute"
tolerationSeconds: 6000
说明:
Kubernetes 会自动给 Pod 添加一个 key 为 node.kubernetes.io/not-ready 的容忍度 并配置 tolerationSeconds=300,除非用户提供的 Pod 配置中已经已存在了 key 为 node.kubernetes.io/not-ready 的容忍度。
Kubernetes 会给 Pod 添加一个 key 为 node.kubernetes.io/unreachable 的容忍度 并配置 tolerationSeconds=300,除非用户提供的 Pod 配置中已经已存在了 key 为 node.kubernetes.io/unreachable 的容忍度。
DaemonSet 中的 Pod 被创建时, 针对以下污点自动添加的 NoExecute 的容忍度将不会指定 tolerationSeconds,以防 DaemonSet 崩溃:
Pod 可以有 优先级。 优先级表示一个 Pod 相对于其他 Pod 的重要性。 如果一个 Pod 无法被调度,调度程序会尝试抢占(驱逐)较低优先级的 Pod, 以使悬决 Pod 可以被调度。
如何使用优先级和抢占:
1.新增一个或多个 PriorityClass。
2.创建 Pod,并将其 priorityClassName 设置为新增的 PriorityClass。
Kubernetes 已经提供了 2 个 PriorityClass: system-cluster-critical 和 system-node-critical。 这些是常见的类,用于确保始终优先调度关键组件。
PriorityClass 是一个无名称空间对象,它定义了从优先级类名称到优先级整数值的映射。 名称在 PriorityClass 对象元数据的 name 字段中指定。 值在必填的 value 字段中指定。值越大,优先级越高。 PriorityClass 对象的名称必须是有效的 DNS 子域名, 并且它不能以 system- 为前缀。
PriorityClass 对象可以设置任何小于或等于 10 亿的 32 位整数值。 较大的数字是为通常不应被抢占或驱逐的关键的系统 Pod 所保留的。 集群管理员应该为这类映射分别创建独立的 PriorityClass 对象。
PriorityClass 还有两个可选字段:globalDefault
和 description
。
globalDefault
字段表示这个 PriorityClass 的值应该用于没有 priorityClassName
的 Pod。 系统中只能存在一个 globalDefault
设置为 true 的 PriorityClass。 如果不存在设置了 globalDefault
的 PriorityClass, 则没有 priorityClassName
的 Pod 的优先级为零。
description
字段是一个任意字符串。 它用来告诉集群用户何时应该使用此 PriorityClass。
Example
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: high-priority
value: 1000000
globalDefault: false
description: "此优先级类应仅用于 XYZ 服务 Pod。"
关于 PodPriority 和现有集群的注意事项
PodSpec定义PriorityClass
优先级准入控制器使用 priorityClassName
字段并填充优先级的整数值。
Example
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
priorityClassName: high-priority
配置了 PreemptionPolicy: Never 的 Pod 将被放置在调度队列中较低优先级 Pod 之前, 但它们不能抢占其他 Pod。等待调度的非抢占式 Pod 将留在调度队列中,直到有足够的可用资源, 它才可以被调度。非抢占式 Pod,像其他 Pod 一样,受调度程序回退的影响。 这意味着如果调度程序尝试这些 Pod 并且无法调度它们,它们将以更低的频率被重试, 从而允许其他优先级较低的 Pod 排在它们之前。
非抢占式 Pod 仍可能被其他高优先级 Pod 抢占。
PreemptionPolicy 默认为 PreemptLowerPriority, 这将允许该 PriorityClass 的 Pod 抢占较低优先级的 Pod(现有默认行为也是如此)。 如果 PreemptionPolicy 设置为 Never,则该 PriorityClass 中的 Pod 将是非抢占式的。
场景:科学计算。数据科学工作负载是一个示例用例。用户可以提交他们希望优先于其他工作负载的作业, 但不希望因为抢占运行中的 Pod 而导致现有工作被丢弃。 设置为 PreemptionPolicy: Never
的高优先级作业将在其他排队的 Pod 之前被调度, 只要足够的集群资源“自然地”变得可用。
Example
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: high-priority-nonpreempting
value: 1000000
preemptionPolicy: Never
globalDefault: false
description: "This priority class will not cause other pods to be preempted."
Pod优先级对调度顺序的影响
当启用 Pod 优先级时,调度程序会按优先级对悬决 Pod 进行排序, 并且每个悬决的 Pod 会被放置在调度队列中其他优先级较低的悬决 Pod 之前。 因此,如果满足调度要求,较高优先级的 Pod 可能会比具有较低优先级的 Pod 更早调度。 如果无法调度此类 Pod,调度程序将继续并尝试调度其他较低优先级的 Pod。
Pod P被创建后会进入队列等待调度。如无法找到满足需求的调度节点,则触发抢占逻辑。抢占逻辑试图找到一个节点,删除节点上一个或多个优先级较低的Pod,这些Pod被驱逐释放资源后,Pod P则被调度到节点上。
当Pod P抢占N节点上一个或多个Pod时,Pod P状态的nominatedNodeName字段会被设置程节点N的名称。
但是如果在节点N释放出足够的资源给Pod P之前,有其他节点有足够的节点调度Pod P的话,那么Pod P会被调度到其他节点上。但是Pod P的nominatedNodeName字段不会发生变化。
此外,如果调度程序抢占节点 N 上的 Pod,但随后比 Pod P 更高优先级的 Pod 到达, 则调度程序可能会将节点 N 分配给新的更高优先级的 Pod。 在这种情况下,调度程序会清除 Pod P 的 nominatedNodeName。 通过这样做,调度程序使 Pod P 有资格抢占另一个节点上的 Pod。
被抢占牺牲者体面终止
当 Pod 被抢占时,牺牲者会得到他们的 体面终止期。 它们可以在体面终止期内完成工作并退出。如果它们不这样做就会被杀死。 这个体面终止期在调度程序抢占 Pod 的时间点和待处理的 Pod (P) 可以在节点 (N) 上调度的时间点之间划分出了一个时间跨度。 同时,调度器会继续调度其他待处理的 Pod。当牺牲者退出或被终止时, 调度程序会尝试在待处理队列中调度 Pod。 因此,调度器抢占牺牲者的时间点与 Pod P 被调度的时间点之间通常存在时间间隔。 为了最小化这个差距,可以将低优先级 Pod 的体面终止时间设置为零或一个小数字。
支持 PodDisruptionBudget,但不保证
PodDisruptionBudget (PDB) 允许多副本应用程序的所有者限制因自愿性质的干扰而同时终止的 Pod 数量。 Kubernetes 在抢占 Pod 时支持 PDB,但对 PDB 的支持是基于尽力而为原则的。 调度器会尝试寻找不会因被抢占而违反 PDB 的牺牲者,但如果没有找到这样的牺牲者, 抢占仍然会发生,并且即使违反了 PDB 约束也会删除优先级较低的 Pod。
与低优先级 Pod 之间的 Pod 间亲和性
如果悬决 Pod 与节点上的一个或多个较低优先级 Pod 具有 Pod 间亲和性, 则在没有这些较低优先级 Pod 的情况下,无法满足 Pod 间亲和性规则。 在这种情况下,调度程序不会抢占节点上的任何 Pod。 相反,它寻找另一个节点。调度程序可能会找到合适的节点, 也可能不会。无法保证悬决 Pod 可以被调度。
不能跨界点抢占
假设正在考虑在一个节点 N 上执行抢占,以便可以在 N 上调度待处理的 Pod P。(反亲和性) 只有当另一个节点上的 Pod Q被抢占时,P 才可能在 N 上变得可行。
调度器不会进行跨界点抢占。因此,此情况下不会进行调度。节点N视为不满足调度需求。
Pod的抢占会造成一些低优先级的Pod被不必要的抢占,所以个人觉得慎用。
在配置多个调度方案时, 你可以将某个方案与节点亲和性关联起来,如果某个调度方案仅适用于某组 特殊的节点时,这样做是很有用的。 要实现这点,可以在调度器配置中为 NodeAffinity 插件 添加 addedAffinity 参数。 例如:
apiVersion: kubescheduler.config.k8s.io/v1beta1
kind: KubeSchedulerConfiguration
profiles:
- schedulerName: default-scheduler
- schedulerName: foo-scheduler
pluginConfig:
- name: NodeAffinity
args:
addedAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: scheduler-profile
operator: In
values:
- foo
这里的 addedAffinity 除遵从 Pod 规约中设置的节点亲和性。通过 podSpec.schedulerName 来选择使用哪一个调度器(默认使用内置的调度器)。
注意:DaemonSet 控制器为 DaemonSet 创建 Pods, 但该控制器不理会调度方案。因此,建议你保留一个调度方案,例如 default-scheduler,不要在其中设置 addedAffinity。 这样,DaemonSet 的 Pod 模板将会使用此调度器名称。 否则,DaemonSet 控制器所创建的某些 Pods 可能持续处于不可调度状态。
https://kubernetes.feisky.xyz/concepts/components/schedulerhttps://kubernetes.feisky.xyz/concepts/components/scheduler
将 Pod 分配给节点 | Kuberneteshttps://kubernetes.io/zh/docs/concepts/scheduling-eviction/assign-pod-node/
污点和容忍度 | KubernetesPods that *attracts* them to a set of nodes (either as a preference or a hard requirement). _Taints_ are the opposite -- they allow a node to repel a set of pods. -- 节点亲和性 是 Pod 的一种属性,它使 Pod 被吸引到一类特定的节点 (这可能出于一种偏好,也可能是硬性要求)。 污点(Taint)则相反——它使节点能够排斥一类特定的 Pod。容忍度(Toleration)是应用于 Pod 上的,允许(但并不要求)Pod 调度到带有与之匹配的污点的节点上。污点和容忍度(Toleration)相互配合,可以用来避免 Pod 被分配到不合适的节点上。 每个节点上都可以应用一个或多个污点,这表示对于那些不能容忍这些污点的 Pod,是不会被该节点接受的。概念 您可以使用命令 kubectl taint 给节点增加一个污点。比如,kubectl taint nodes node1 key1=value1:NoSchedule 给节点 node1 增加一个污点,它的键名是 key1,键值是 value1,效果是 NoSchedule。 这表示只有拥有和这个污点相匹配的容忍度的 Pod 才能够被分配到 node1 这个节点。https://kubernetes.io/zh/docs/concepts/scheduling-eviction/taint-and-toleration/Pod 优先级和抢占 | Kuberneteshttps://kubernetes.io/zh/docs/concepts/scheduling-eviction/pod-priority-preemption/#%E8%A2%AB%E6%8A%A2%E5%8D%A0%E7%89%BA%E7%89%B2%E8%80%85%E7%9A%84%E4%BD%93%E9%9D%A2%E7%BB%88%E6%AD%A2