Kubernetes(k8s)资源调度
nodeSelector(节点选择器)
nodeSelector
: 用于将Pod调度到匹配Label的Node上,如果没有匹配的标签会调度失败。
作用:
- 约束Pod到特定的节点运行
- 完全匹配节点标签
nodeSelector
是节点选择约束的最简单推荐形式。nodeSelector
是 PodSpec
的一个字段。 它包含键值对的映射。为了使 pod 可以在某个节点上运行,该节点的标签中 必须包含这里的每个键值对(它也可以具有其他标签)。 最常见的用法的是一对键值对。
添加标签到节点
执行 kubectl get nodes
命令获取集群的节点名称。 选择一个你要增加标签的节点,然后执行 kubectl label nodes <node-name> <label-key>=<label-value>
命令将标签添加到你所选择的节点上。 例如,如果你的节点名称为 ‘kubernetes-foo-node-1.c.a-robinson.internal’ 并且想要的标签是 disktype=ssd
,则可以执行 kubectl label nodes kubernetes-foo-node-1.c.a-robinson.internal disktype=ssd
命令。
你可以通过重新运行 kubectl get nodes --show-labels
, 查看节点当前具有了所指定的标签来验证它是否有效。 你也可以使用 kubectl describe node "nodename"
命令查看指定节点的标签完整列表。
步骤三:添加 nodeSelector 字段到 Pod 配置中
添加一个 nodeSelector 部分
//添加一个 nodeSelector 部分
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
nodeSelector:
disktype: ssd
当你之后运行 kubectl apply -f https://k8s.io/examples/pods/pod-nginx.yaml
命令, Pod 将会调度到将标签添加到的节点上。 你可以通过运行 kubectl get pods -o wide
并查看分配给 pod 的 node来验证其是否有效
实例:
//节点添加标签
kubectl label nodes node2.example.com disktype=ssd
//写入disktype=ssd
[root@master manifest]# vim test.yaml
---
apiVersion: v1
kind: Pod
metadata:
name: test
spec:
containers:
- name: b1
image: busybox
command: ["/bin/sh","-c","sleep 9000"]
env:
- name: xx
valueFrom:
fieldRef:
fieldPath: status.podIPs
nodeSelector:
disktype: ssd
[root@master manifest]# kubectl create -f test.yaml
pod/test created
[root@master manifest]# kubectl get pods
NAME READY STATUS RESTARTS AGE
test 1/1 Running 0 8s
//标签选择到指定主机上面,不会变
[root@master manifest]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test 1/1 Running 0 58s 10.140.10.69 node2.example.com <none> <none>
nodeAffinity(节点亲和性)
节点亲和性概念上类似于 nodeSelector
,它使你可以根据节点上的标签来约束 Pod 可以调度到哪些节点。
目前有两种类型的节点亲和性,分别为 requiredDuringSchedulingIgnoredDuringExecution
和preferredDuringSchedulingIgnoredDuringExecution
。 你可以视它们为“硬需求”和“软需求”,意思是,前者指定了将 Pod 调度到一个节点上 必须满足的规则(就像 nodeSelector
但使用更具表现力的语法), 后者指定调度器将尝试执行但不能保证的偏好。 名称的“IgnoredDuringExecution”
部分意味着,类似于 nodeSelector
的工作原理, 如果节点的标签在运行时发生变更,从而不再满足 Pod 上的亲和性规则,那么 Pod 将仍然继续在该节点上运行。 将来我们计划提供 requiredDuringSchedulingRequiredDuringExecution
, 它将与 requiredDuringSchedulingIgnoredDuringExecution
完全相同, 只是它会将 Pod 从不再满足 Pod 的节点亲和性要求的节点上驱逐。
因此,requiredDuringSchedulingIgnoredDuringExecution
的示例将是 “仅将 Pod 运行在具有 Intel CPU 的节点上”,而preferredDuringSchedulingIgnoredDuringExecution
的示例为 “尝试将这组 Pod 运行在 XYZ 故障区域,如果这不可能的话,则允许一些 Pod 在其他地方运行”。
节点亲和性通过 PodSpec 的 affinity
字段下的 nodeAffinity
字段进行指定。
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 操作符的使用。新的节点亲和性语法支持下面的操作符: In
,NotIn
,Exists
,DoesNotExist
,Gt
,Lt
。 你可以使用 NotIn
和 DoesNotExist
来实现节点反亲和性行为,或者使用 节点污点 将 Pod 从特定节点中驱逐。
如果你同时指定了 nodeSelector
和 nodeAffinity
,两者必须都要满足, 才能将 Pod 调度到候选节点上。
如果你指定了多个与 nodeAffinity
类型关联的 nodeSelectorTerms
,则如果其中一个nodeSelectorTerms
满足的话,pod将可以调度到节点上。
如果你指定了多个与 nodeSelectorTerms
关联的 matchExpressions
,则 只有当所有 matchExpressions
满足的话,Pod 才会可以调度到节点上。
如果你修改或删除了 pod 所调度到的节点的标签,Pod 不会被删除。 换句话说,亲和性选择只在 Pod 调度期间有效。
preferredDuringSchedulingIgnoredDuringExecution
中的 weight 字段值的 范围是 1-100。 对于每个符合所有调度要求(资源请求、RequiredDuringScheduling
亲和性表达式等) 的节点,调度器将遍历该字段的元素来计算总和,并且如果节点匹配对应的 MatchExpressions
,则添加“权重”到总和。 然后将这个评调度方案中设置节点亲和性
taint and tolrations(污点和容忍度)
节点亲和性 是 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
这个节点。
若要移除上述命令所添加的污点,你可以执行:
kubectl taint nodes node1 key1=value1:NoSchedule-
您可以在 PodSpec 中定义 Pod 的容忍度。 下面两个容忍度均与上面例子中使用 kubectl taint
命令创建的污点相匹配, 因此如果一个 Pod 拥有其中的任何一个容忍度都能够被分配到 node1
:
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoSchedule"
tolerations:
- key: "key1"
operator: "Exists"
effect: "NoSchedule"
这里是一个使用了容忍度的 Pod:
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
tolerations:
- key: "example-key"
operator: "Exists"
effect: "NoSchedule"
operator
的默认值是 Equal
。
一个容忍度和一个污点相“匹配”是指它们有一样的键名和效果,并且:
如果 operator
是 Exists
(此时容忍度不能指定 value
),或者
如果 operator
是 Equal
,则它们的 value
应该相等
说明存在两种特殊情况
如果一个容忍度的 key
为空且 operator 为 Exists
, 表示这个容忍度与任意的 key 、value 和 effect 都匹配,即这个容忍度能容忍任意 taint。
如果 effect 为
空,则可以与所有键名 key1
的效果相匹配。
上述例子中 effect 使用的值为 NoSchedule
,您也可以使用另外一个值 PreferNoSchedule
。 这是“优化”或“软”版本的 NoSchedule
—— 系统会 尽量 避免将 Pod 调度到存在其不能容忍污点的节点上, 但这不是强制的。effect 的值还可以设置为 NoExecute
,下文会详细描述这个值。
您可以给一个节点添加多个污点,也可以给一个 Pod 添加多个容忍度设置。 Kubernetes
处理多个污点和容忍度的过程就像一个过滤器:从一个节点的所有污点开始遍历, 过滤掉那些 Pod 中存在与之相匹配的容忍度的污点。余下未被过滤的污点的 effect 值决定了 Pod 是否会被分配到该节点,特别是以下情况:
如果未被过滤的污点中存在至少一个 effect 值为 NoSchedule
的污点, 则 Kubernetes
不会将 Pod 分配到该节点。
如果未被过滤的污点中不存在 effect 值为 NoSchedule
的污点, 但是存在 effect 值为 PreferNoSchedule
的污点, 则 Kubernetes
会 尝试不将 Pod 分配到该节点。
如果未被过滤的污点中存在至少一个 effect 值为 NoExecute
的污点, 则 Kubernetes
不会将 Pod 分配到该节点(如果 Pod 还未在节点上运行), 或者将 Pod 从该节点驱逐(如果 Pod 已经在节点上运行)。
例如,假设您给一个节点添加了如下污点
kubectl taint nodes node1 key1=value1:NoSchedule
kubectl taint nodes node1 key1=value1:NoExecute
kubectl taint nodes node1 key2=value2:NoSchedule
假定有一个 Pod,它有两个容忍度:
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoSchedule"
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoExecute"
在这种情况下,上述 Pod 不会被分配到上述节点,因为其没有容忍度和第三个污点相匹配。 但是如果在给节点添加上述污点之前,该 Pod 已经在上述节点运行, 那么它还可以继续运行在该节点上,因为第三个污点是三个污点中唯一不能被这个 Pod 容忍的。
通常情况下,如果给一个节点添加了一个 effect 值为 NoExecute
的污点, 则任何不能忍受这个污点的 Pod 都会马上被驱逐, 任何可以忍受这个污点的 Pod 都不会被驱逐。 但是,如果 Pod 存在一个 effect 值为 NoExecute
的容忍度指定了可选属性 tolerationSeconds
的值,则表示在给节点添加了上述污点之后, Pod 还能继续在节点上运行的时间。例如,
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoExecute"
tolerationSeconds: 3600
effect 值为 NoExecute
的容忍度指定了可选属性 tolerationSeconds
的值,则表示在给节点添加了上述污点之后, Pod 还能继续在节点上运行的时间。例如,
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoExecute"
tolerationSeconds: 3600
这表示如果这个 Pod 正在运行,同时一个匹配的污点被添加到其所在的节点, 那么 Pod 还将继续在节点上运行 3600 秒,然后被驱逐。 如果在此之前上述污点被删除了,则 Pod 不会被驱逐。