官方文档: 将 Pod 分配给节点
将 Pod 分配给节点
有几种方法可以实现约束一个 Pod 只能在特定的节点上运行
。推荐的方法都是使用 selector
来进行选择。通常这样的约束不是必须的,因为调度器会自动进行合理的分配。比如,将 Pod 分散到节点上,而不是将 Pod 放置在可用资源不足的节点上等等。但是在某些情况下,你可能需要进一步控制 Pod 分配的节点,例如:确保 Pod 最终分配在连接了 SSD 的机器上,或者将来自两个不同的服务且有大量通信的 Pods 被分配在同一个可用区。
nodeSelector
nodeSelector 是节点选择约束的最简单的推荐形式。nodeSelector 是 PodSpec 的一个字段。它包含键值对的映射。为了使 pod 可以在某个节点上运行,该节点的标签中必须包含这里的每个键值对。最常见的用法是一对键值对。
以下是一个使用 nodeSelector 的例子:
给节点添加标签
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40# 执行 kubectl get nodes 命令获取集群的节点名称。
% kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-master-01 Ready master 23d v1.19.7
k8s-master-02 Ready master 23d v1.19.7
k8s-master-03 Ready master 23d v1.19.7
k8s-node-01 Ready node 23d v1.19.7
k8s-node-02 Ready node 23d v1.19.7
# 选择一个你要增加标签的节点,然后执行 kubectl label nodes <node-name> <label-key>=<label-value> 命令 添加标签到指定节点
% kubectl label nodes k8s-node-01 disktype=ssd
node/k8s-node-01 labeled
# 通过 kubectl get nodes --show-labels,查看节点是否具有所指定的标签
% kubectl get nodes --show-labels
NAME STATUS ROLES AGE VERSION LABELS
k8s-master-01 Ready master 23d v1.19.7 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-master-01,kubernetes.io/os=linux,node-role.kubernetes.io/master=,node.kubernetes.io/master=
k8s-master-02 Ready master 23d v1.19.7 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-master-02,kubernetes.io/os=linux,node-role.kubernetes.io/master=,node.kubernetes.io/master=
k8s-master-03 Ready master 23d v1.19.7 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-master-03,kubernetes.io/os=linux,node-role.kubernetes.io/master=,node.kubernetes.io/node=
k8s-node-01 Ready node 23d v1.19.7 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,disktype=ssd,ingress=true,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node-01,kubernetes.io/os=linux,node-role.kubernetes.io/node=,node.kubernetes.io/node=
k8s-node-02 Ready node 23d v1.19.7 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node-02,kubernetes.io/os=linux,node-role.kubernetes.io/node=,node.kubernetes.io/node=
# 也可以使用 kubectl describe node <node-name> 命令查看指定节点的标签完整列表
% kubectl describe nodes k8s-node-01
Name: k8s-node-01
Roles: node
Labels: beta.kubernetes.io/arch=amd64
beta.kubernetes.io/os=linux
disktype=ssd
ingress=true
kubernetes.io/arch=amd64
kubernetes.io/hostname=k8s-node-01
kubernetes.io/os=linux
node-role.kubernetes.io/node=
node.kubernetes.io/node=
Annotations: node.alpha.kubernetes.io/ttl: 0
projectcalico.org/IPv4Address: 192.168.200.21/24
projectcalico.org/IPv4IPIPTunnelAddr: 172.31.44.0
volumes.kubernetes.io/controller-managed-attach-detach: true
... 省略若干行 ...添加 nodeSelector 字段到 Pod 配置中,选择一个 Pod 配置文件,并在其中添加一个 nodeSelector 部分。如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
nodeSelector:
disktype: ssd执行以下命令,该 Pod 将会被调度到具有
disktype=ssd
这个标签的节点上。1
2% kubectl apply -f pod-nginx.yaml
pod/nginx created执行以下命令查看 Pod 被分配给了哪个节点进行验证
1
2
3% kubectl get pod -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 66s 172.31.44.9 k8s-node-01 <none> <none>
内置的节点标签
除了我们手动添加的标签外,节点还预先制定了一组准标签。参见 常见标签,注解以及污点
- kubernetes.io/hostname: Kubelet 用主机名生成此标签。需要注意的是主机名可修改,这是把“实际的”主机名通过参数 –hostname-override 传给 kubelet 实现的。
- failure-domain.beta.kubernetes.io/zone: 从 v1.17 开始,此标签被弃用,取而代之的是 topology.kubernetes.io/zone.
- failure-domain.beta.kubernetes.io/region: 从 v1.17 开始,此标签被弃用,取而代之的是 topology.kubernetes.io/region.
- topology.kubernetes.io/zone
- topology.kubernetes.io/region
- beta.kubernetes.io/instance-type: 从 v1.17 起,此标签被弃用,取而代之的是 node.kubernetes.io/instance-type.
- node.kubernetes.io/instance-type: Kubelet 用 cloudprovider 定义的实例类型生成此标签。 所以只有用到 cloudprovider 的场合,才会设置此标签。
- kubernetes.io/os: Kubelet 用 Go 定义的 runtime.GOOS 生成该标签的键值。在混合使用异构操作系统场景下(例如:混合使用 Linux 和 Windows 节点),此键值可以带来极大便利。
- kubernetes.io/arch: Kubelet 用 Go 定义的 runtime.GOARCH 生成该标签的键值。在混合使用 arm 和 x86 节点的场景中,此键值可以带来极大便利。
节点隔离/限制
向 Node 对象添加标签可以将 Pod 定位到特定的节点或者节点组。这可以用来确保指定的 Pod 只能运行在具有一定隔离性,安全性或者监管属性的节点上。当为此目的使用标签时,强烈建议选择节点上的 kubelet 进程无法修改的标签键。这可以防止受感染的节点使用其 kubelet 凭据在自己的 Node 对象上设置这些标签,并影响调度器将工作负载调度到受感染的节点。
NodeRestriction 准入插件可以防止 kubelet 使用 node-restriction.kubernetes.io/
前缀设置或修改标签。
要使用该标签前缀进行节点隔离,需要满足以下条件:
- 检查是否在使用 Kubernetes v1.11+,以便
NodeRestriction
功能可用。 - 确保你在使用节点授权并且已经启用 NodeRestriction 准入插件。
- 将
node-restriction.kubernetes.io/
前缀下的标签添加到 Node 对象,然后在节点选择器中使用这些标签。例如,example.com.node-restriction.kubernetes.io/fips=true
或example.com.node-restriction.kubernetes.io/pci-dss=true
。
亲和性和反亲和性
nodeSelector 提供了一种非常简单的方法来将 Pod 约束到具有特定标签的节点上。亲和性/反亲和性的功能极大地扩展了你可以表达约束的类型。关键的增强点包括:
- 语言更具表现力(不仅仅是 “对完全匹配规则的 AND”)
- 规则是 “软需求”/“偏好”,而不是硬性要求,因此,如果调度器无法满足该要求,仍然调度该 Pod
- 可以使用节点上(或其他拓扑域中)的 Pod 的标签来约束,而不是使用节点本身的标签,来允许哪些 pod 可以或者不可以被放置在一起。
亲和性功能包含两种类型的亲和性,即 节点亲和性
和 Pod 间亲和性/反亲和性
。 节点亲和性就像现有的 nodeSelector
(但具有上面列出的前两个好处),而 Pod 间亲和性/反亲和性约束使用的是 Pod 标签而不是节点标签;
节点亲和性 (nodeAffinity)
节点亲和性概念上类似于 nodeSelector
,它使你可以根据节点上的标签来约束 Pod 可以调度到哪些节点。
目前有两种类型的节点亲和性,分别为 requiredDuringSchedulingIgnoredDuringExecution
和 preferredDuringSchedulingIgnoredDuringExecution
。可以视它们为 “硬需求” 和 “软需求”,意思是,前者指定了要将 Pod 调度到一个节点上必须满足的规则,后者指定了调度器将尝试执行但不能保证的偏好。名称的IgnoredDuringExecution
部分意味着,如果节点的标签在运行时发生变更,从而不再满足 Pod 上的亲和性规则,那么 Pod 将仍然继续在该节点上运行。(注:将来可能会提供提供 requiredDuringSchedulingRequiredDuringExecution
,它将与 requiredDuringSchedulingIgnoredDuringExecution
完全相同,只是它会将 Pod 从不再满足 Pod 的节点亲和性要求的节点上驱逐)。
节点亲和性通过 PodSpec 的 affinity
字段下的 nodeAffinity
字段进行指定。
下面是一个使用节点亲和性的 Pod 的示例:
1 | apiVersion: v1 |
此示例表示:
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 从特定节点中驱逐。
如果你同时指定了nodeSelector
和nodeAffinity
,两者必须都要满足, 才能将 Pod 调度到候选节点上。如果你指定了多个与
nodeAffinity
类型关联的nodeSelectorTerms
,则如果其中一个nodeSelectorTerms
满足的话,pod 将可以调度到节点上。如果你指定了多个与
nodeSelectorTerms
关联的matchExpressions
,则只有当所有matchExpressions
满足的话,Pod 才会可以调度到节点上。注:如果你修改或删除了 pod 所调度到的节点的标签,Pod 不会被删除。换句话说,亲和性选择只在 Pod 调度期间有效。
preferredDuringSchedulingIgnoredDuringExecution
中的weight
字段值的范围是1-100
。数值越高的节点优先级越高。
Pod 间亲和性与反亲和性
Pod 间亲和性与反亲和性使你可以基于已经在节点上运行的 Pod 的标签来约束 Pod 是否可以调度到该节点,而不是基于节点上的标签。
规则的格式为 “如果 X 节点上已经运行了一个或多个满足规则 Y 的 Pod, 则这个 Pod 应该(或者在反亲和性的情况下不应该)运行在 X 节点”。
Y 表示一个具有可选的关联命名空间列表的 LabelSelector;与节点不同,因为 Pod 是命名空间限定的(因此 Pod 上的标签也是命名空间限定的),因此作用于 Pod 标签的LabelSelector 必须指定 selector 应该作用在哪个命名空间。从概念上讲,X 是一个拓扑域,如节点、机架、云供应商可用区、云供应商地理区域等。你可以使用 topologyKey 来表示它,topologyKey 是节点标签的键,以便系统用来表示这样的拓扑域。
注意:Pod 间亲和性与反亲和性需要大量的处理,这可能会显著减慢大规模集群中的调度。 官方不建议在超过数百个节点的集群中使用它们。Pod 反亲和性需要对节点进行一致的标记,即集群中的每个节点必须具有适当的标签能够匹配 topologyKey。如果某些或所有节点缺少指定的 topologyKey 标签,可能会导致意外行为。
- Pod 间亲和性使用 podAffinity 字段指定;
- Pod 间反亲和性使用 podAntiAffinity 字段指定;
与节点亲和性一样,当前有两种类型的 Pod 亲和性与反亲和性,即 requiredDuringSchedulingIgnoredDuringExecution
和 preferredDuringSchedulingIgnoredDuringExecution
,分别表示“硬性”与“软性”要求。亲和性的一个示例是 “将服务 A 和服务 B 的 Pod 放置在同一区域,因为它们之间进行大量交流”,而反亲和性的示例将是 “将此服务的 pod 跨区域分布”(硬性要求是说不通的,因为你可能拥有的 Pod 数多于区域数)。
Pod 亲和性 (podAffinity) 的示例
1 | apiVersion: v1 |
在这个 Pod 的亲和性配置中定义了一条 Pod 亲和性规则和一条 Pod 反亲和性规则。
在此示例中,podAffinity
配置为 requiredDuringSchedulingIgnoredDuringExecution
, 然而 podAntiAffinity
配置为 preferredDuringSchedulingIgnoredDuringExecution
。
Pod 亲和性与反亲和性的合法操作符有: In,NotIn,Exists,DoesNotExist。
原则上,topologyKey
可以是任何合法的标签键。 然而,出于性能和安全原因,topologyKey
受到一些限制:
- 对于 Pod 亲和性而言,在
requiredDuringSchedulingIgnoredDuringExecution
和preferredDuringSchedulingIgnoredDuringExecution
中,topologyKey
不允许为空。 - 对于 Pod 反亲和性而言,
requiredDuringSchedulingIgnoredDuringExecution
和preferredDuringSchedulingIgnoredDuringExecution
中,topologyKey
都不可以为空。 - 对于
requiredDuringSchedulingIgnoredDuringExecution
要求的 Pod 反亲和性, 准入控制器LimitPodHardAntiAffinityTopology
被引入以确保topologyKey
只能是kubernetes.io/hostname
。如果你希望topologyKey
也可用于其他定制topology
,你可以更改准入控制器或者禁用之。 - 除上述情况外,
topologyKey
可以是任何合法的标签键。
亲和性与反亲和性总结
亲和性 Affinity 包括
NodeAffinity: 节点亲和性
- requiredDuringSchedulingIgnoredDuringExecution: 硬亲和力,既支持必须部署到指定节点上,也支持不部署到指定节点(取决于
operator
的取值); - preferredDuringSchedulingIgnoredDuringExecution: 软亲和力,尽量部署在满足条件的节点上,或者是尽量不要部署在被匹配的节点。
- requiredDuringSchedulingIgnoredDuringExecution: 硬亲和力,既支持必须部署到指定节点上,也支持不部署到指定节点(取决于
PodAffinity: Pod 亲和力
- requiredDuringSchedulingIgnoredDuringExecution: 硬亲和力,必须将不同的两个 Pod 部署在一块;
- preferredDuringSchedulingIgnoredDuringExecution: 软亲和力,尽量将不同的两个 Pod 部署在一块;
PodAntiAffinity: Pod 反亲和力
- requiredDuringSchedulingIgnoredDuringExecution: 硬亲和力,不允许将两个 Pod 部署在一块;
- preferredDuringSchedulingIgnoredDuringExecution: 软亲和力,尽量将两个 Pod 分开部署;
示例
以下是一个相对完整的配置了节点亲和力的 Deployment 资源清单文件 :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: demo-nginx
name: demo-nginx
spec:
progressDeadlineSeconds: 600
replicas: 2
revisionHistoryLimit: 10
selector:
matchLabels:
app: demo-nginx
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 1
type: RollingUpdate
template:
metadata:
labels:
app: demo-nginx
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
initContainers:
- command:
- sh
- -c
- echo "InitContainer" >> /tmp/nfs/init
image: nginx
imagePullPolicy: IfNotPresent
name: init1
resources: {}
volumeMounts:
- mountPath: /tmp/nfs
name: nfs-test
containers:
- command:
- sh
- -c
- sleep 36000000000
image: nginx
imagePullPolicy: IfNotPresent
name: nginx2
resources: {}
volumeMounts:
- mountPath: /mnt
name: cache-volume
- mountPath: /tmp/nfs
name: nfs-test
- command:
- sh
- -c
- sleep 36000000000
image: nginx
imagePullPolicy: IfNotPresent
name: nginx
ports:
- containerPort: 80
name: web
protocol: TCP
resources:
limits:
cpu: 100m
memory: 270Mi
requests:
cpu: 100m
memory: 70Mi
volumeMounts:
- mountPath: /etc/nginx/nginx.conf
name: config-volume
subPath: etc/nginx/nginx.conf
- mountPath: /mnt/
name: config-volume-non-subpath
- mountPath: /tmp/1
name: test-hostpath
- mountPath: /tmp/2
name: cache-volume
tolerations:
- effect: NoSchedule
key: master-test
operator: Equal
value: test
- effect: NoExecute
key: master-test
operator: Equal
tolerationSeconds: 60
value: test
volumes:
- hostPath:
path: /etc/hosts
type: File
name: test-hostpath
- configMap:
defaultMode: 420
items:
- key: nginx.conf
path: etc/nginx/nginx.conf
name: nginx-conf
name: config-volume
- configMap:
defaultMode: 420
name: nginx-conf
name: config-volume-non-subpath
- emptyDir:
medium: Memory
name: cache-volume
- name: nfs-test
nfs:
path: /data/k8s-data/testDir
server: 192.168.200.26
dnsPolicy: ClusterFirst
restartPolicy: Always
terminationGracePeriodSeconds: 30以下是一个相对完整的配置了 Pod 亲和力的 Deployment 资源清单文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: demo-nginx
name: demo-nginx
namespace: default
spec:
progressDeadlineSeconds: 600
replicas: 2
revisionHistoryLimit: 10
selector:
matchLabels:
app: demo-nginx
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 1
type: RollingUpdate
template:
metadata:
labels:
app: demo-nginx
spec:
# 把demo-nginx和kube-systemnamespace下的符合label为k8s-app= calico-kube-controllers的Pod部署在同一个节点(拓扑域)上
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: k8s-app
operator: In
values:
- calico-kube-controllers
# 如果写了 namespaces 的字段,但是留空,他是匹配所有 namespace 下的指定 label 的Pod,
# 如果写了 namespaces 并且指定了值,就是匹配指定 namespace 下的指定 label 的 Pod。
# 如果没有写 namespace,则匹配当前的 namespace:
namespaces:
- kube-system
topologyKey: kubernetes.io/hostname
containers:
- command:
- sh
- -c
- sleep 36000000000
image: nginx
imagePullPolicy: IfNotPresent
name: nginx2
resources: {}
volumeMounts:
- mountPath: /mnt
name: cache-volume
- mountPath: /tmp/nfs
name: nfs-test
- command:
- sh
- -c
- sleep 36000000000
image: nginx
imagePullPolicy: IfNotPresent
name: nginx
ports:
- containerPort: 80
name: web
protocol: TCP
resources:
limits:
cpu: 100m
memory: 270Mi
requests:
cpu: 100m
memory: 70Mi
volumeMounts:
- mountPath: /etc/nginx/nginx.conf
name: config-volume
subPath: etc/nginx/nginx.conf
- mountPath: /mnt/
name: config-volume-non-subpath
- mountPath: /tmp/1
name: test-hostpath
- mountPath: /tmp/2
name: cache-volume
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
shareProcessNamespace: true
terminationGracePeriodSeconds: 30
tolerations:
- effect: NoSchedule
key: master-test
operator: Equal
value: test
- effect: NoExecute
key: master-test
operator: Equal
tolerationSeconds: 60
value: test
volumes:
- hostPath:
path: /etc/hosts
type: File
name: test-hostpath
- configMap:
defaultMode: 420
items:
- key: nginx.conf
path: etc/nginx/nginx.conf
name: nginx-conf
name: config-volume
- configMap:
defaultMode: 420
name: nginx-conf
name: config-volume-non-subpath
- emptyDir:
medium: Memory
name: cache-volume
- name: nfs-test
nfs:
path: /data/k8s-data/testDir
server: 192.168.200.26
污点和容忍度
节点亲和性是 Pod 的一种属性,它使 Pod 被吸引到一类特定的节点。这可能出于一种偏好,也可能是硬性要求。Taint
(污点)则相反,它使节点能够排斥一类特定的 Pod。Tolerations
(容忍度)是应用于 Pod 上的,它允许(但并不要求)Pod 调度到带有与之匹配的污点的节点上。
污点和容忍度相互配合,可以用来避免 Pod 被分配到不合适的节点上。每个节点上都可以应用一个或多个污点,这表示对于那些不能容忍这些污点的 Pod,是不会被该节点接受的。
添加和删除污点
可以使用
kubectl taint
给节点增加一个污点。比如:1
kubectl taint nodes k8s-node-01 key1=value1:NoSchedule
给节点 k8s-node-01 增加一个污点,它的键名是 key1,键值是 value1,effect 是 NoSchedule。这表示只有拥有和这个污点相匹配的容忍度的 Pod 才能够被分配到 k8s-node-01 这个节点。
若要移除上述命令所添加的污点,可以执行
1
kubectl taint nodes k8s-node-01 key1=value1:NoSchedule-
如果要在 Pod 中定义 Pod 的容忍度,可以在 .spec 下添加 tolerations 相关内容,下面两个容忍度均与上面使用 kubectl taint 命令创建的污点相匹配,因此如果一个 Pod 拥有其中的任何一个容忍度都能够被分配到 k8s-node-01
实例11
2
3
4
5tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoSchedule"实例2
1
2
3
4tolerations:
- key: "key1"
operator: "Exists"
effect: "NoSchedule"这里一个使用了容忍度的 Pod,内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15apiVersion: 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。
一个容忍度和一个污点相“匹配”是指它们有一样的键名和 effect,并且:
- 如果 operator 是 Exists,此时容忍度不能指定 value;
- 如果 operator 是 Equal,则此时它们的 value 应该相等。
注意:
如果一个容忍度的 key 为空且 operator 为 Exists, 表示这个容忍度与任意的 key 、value 和 effect 都匹配,即这个容忍度能容忍任意 taint。
如果 effect 为空,则可以与所有键名 key1 的效果相匹配。
上述例子中的 effect 使用的值为 NoSchedule,effect 的取值可以如下:
- NoExecute: 禁止调度到该节点,如果不符合这个污点的 Pod 会被驱逐;
- NoSchedule: 禁止调度到该节点,已经在该节点上的 Pod 不受影响;
- PreferNoSchedule: 尽量避免将 Pod 调度到指定节点,如果没有更合适的节点,可以允许被部署到该节点。
内置的污点
当某种条件为真时,节点控制器会自动给节点添加一个污点。当前内置的污点包括:
node.kubernetes.io/not-ready
: 节点未准备好。这相当于节点状态 Ready 的值为 “False”。node.kubernetes.io/unreachable
: 节点控制器访问不到节点. 这相当于节点状态 Ready 的值为 “Unknown”。node.kubernetes.io/memory-pressure
: 节点存在内存压力。node.kubernetes.io/disk-pressure
: 节点存在磁盘压力。node.kubernetes.io/pid-pressure
: 节点的 PID 压力。node.kubernetes.io/network-unavailable
: 节点网络不可用。node.kubernetes.io/unschedulable
: 节点不可调度。node.cloudprovider.kubernetes.io/uninitialized
: 如果 kubelet 启动时指定了一个 “外部” 云平台驱动,它将给当前节点添加一个污点将其标志为不可用。在cloud-controller-manager
的一个控制器初始化这个节点后,kubelet 将删除这个污点。
在节点被驱逐时,节点控制器或者 kubelet 会添加带有 NoExecute
效应的相关污点。如果异常状态恢复正常,kubelet 或节点控制器能够移除相关的污点。
为了保证由于节点问题引起的 Pod 驱逐速率限制行为正常,系统实际上会以限定速率的方式添加污点。在像主控节点与工作节点间通信中断等场景下,这样做可以避免 Pod 被大量驱逐。
使用这个功能特性,结合 tolerationSeconds
,Pod 就可以指定当节点出现一个或全部上述问题时还将在这个节点上运行多长的时间。
比如,一个使用了很多本地状态的应用程序在网络断开时,仍然希望停留在当前节点上运行一段较长的时间, 愿意等待网络恢复以避免被驱逐。在这种情况下,Pod 的容忍度可能是下面这样的:
1 | tolerations: |
说明:
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
的容忍度。
这种自动添加的容忍度意味着在其中一种问题被检测到时 Pod 默认能够继续停留在当前节点运行 5 分钟。
DaemonSet
中的 Pod 被创建时, 针对以下污点自动添加的 NoExecute
的容忍度将不会指定 tolerationSeconds
:
node.kubernetes.io/unreachable
node.kubernetes.io/not-ready
这保证了出现上述问题时 DaemonSet
中的 Pod 永远不会被驱逐。
基于节点状态添加污点
Node 生命周期控制器会自动创建与 Node 条件相对应的带有 NoSchedule
效应的污点。 同样,调度器不检查节点条件,而是检查节点污点。这确保了节点条件不会影响调度到节点上的内容。用户可以通过添加适当的 Pod 容忍度来选择忽略某些 Node 的问题(表示为 Node 的调度条件)。
DaemonSet 控制器自动为所有守护进程添加如下 NoSchedule 容忍度以防 DaemonSet 崩溃:
- node.kubernetes.io/memory-pressure
- node.kubernetes.io/disk-pressure
- node.kubernetes.io/pid-pressure (1.14 或更高版本)
- node.kubernetes.io/unschedulable (1.10 或更高版本)
- node.kubernetes.io/network-unavailable (只适合主机网络配置)