文章大纲
将 Pod 约束在特定的节点上可以使用 nodeSelect
对节点进行标签选择。
但是调度器通常会合理的调度 Pod 到合适的节点,所以除了使用 nodeSelect
进行强制约束外,还可以使用亲和性和反亲和性以及污点和容忍度进行指派 Pod。
nodeSelect
nodeSelect
依赖于节点标签,所以首先需要对节点添加上标签。
查看集群节点上所有的标签:
[vagrant@master01 ~]$ kubectl get nodes --show-labels
NAME STATUS ROLES AGE VERSION LABELS
master01.example.com Ready control-plane 41d v1.29.1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=master01.example.com,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node.kubernetes.io/exclude-from-external-load-balancers=,role=ceph
worker01.example.com Ready edge 40d v1.29.1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=worker01.example.com,kubernetes.io/os=linux,node-role.kubernetes.io/edge=,role=ceph
worker02.example.com Ready <none> 40d v1.29.1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=worker02.example.com,kubernetes.io/os=linux,role=ceph
为特定的节点添加标签:
[vagrant@master01 ~]$ kubectl label nodes worker01.example.com env=qa
node/worker01.example.com labeled
[vagrant@master01 ~]$ kubectl get nodes -l env=qa
NAME STATUS ROLES AGE VERSION
worker01.example.com Ready edge 40d v1.29.1
移除 label:
[vagrant@master01 ~]$ kubectl label node worker01.example.com env-
node/worker01.example.com unlabeled
[vagrant@master01 ~]$ kubectl get nodes -l env=qa
No resources found
将 Pod 调度到指定的标签的节点上:
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
nodeSelector:
env: qa
验证 Pod 已经调度到了 worker01
:
[vagrant@master01 ~]$ kubectl get pods/nginx -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 59s 10.244.95.132 worker01.example.com <none> <none>
[vagrant@master01 ~]$ kubectl get nodes -l env=qa
NAME STATUS ROLES AGE VERSION
worker01.example.com Ready edge 40d v1.29.1
亲和性和反亲和性
亲和性和反亲和性分为两种:节点亲和性 Pod 之间的亲和性和反亲和性。
节点亲和性
nodeSelector
只能选择拥有所指定标签的节点,可以标明规则是偏好或者软需求,在调度器无法找到匹配的节点时,仍然能够调度该 Pod(nodeSelector 就不行)。
节点亲和性有两种:
requiredDuringSchedulingIgnoredDuringExecution
:调度器只有满足规则时才能执行调度。preferredDuringSchedulingIgnoredDuringExecution
:调度器首先寻找满足规则的节点,如果找不到,调度器仍然会执行调度。
IgnoredDuringExecution
代表 Pod 调度到节点后,节点的标签发生了变化,Pod 依旧运行在该节点上。
首先为节点添加标签:
[vagrant@master01 ~]$ kubectl label node worker02.example.com node-role.kubernetes.io/worker=
node/worker02.example.com labeled
[vagrant@master01 ~]$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
master01.example.com Ready control-plane 41d v1.29.1
worker01.example.com Ready edge 40d v1.29.1
worker02.example.com Ready worker 40d v1.29.1
部署 Pod,并使用亲和性约束:
apiVersion: v1
kind: Pod
metadata:
name: with-node-affinity
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node-role.kubernetes.io/worker
operator: Exists
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: env
operator: In
values:
- prod
containers:
- name: with-node-affinity
image: registry.k8s.io/pause:2.0
验证:
[vagrant@master01 ~]$ kubectl get pods -o wide | grep with-node-affinity
with-node-affinity 1/1 Running 0 32s 10.244.45.238 worker02.example.com <none> <none>
虽然 worker02 上没有 env=prod
标签,但还是调度在该节点上了:
[vagrant@master01 ~]$ kubectl get nodes -l node-role.kubernetes.io/worker
NAME STATUS ROLES AGE VERSION
worker02.example.com Ready worker 40d v1.29.1
[vagrant@master01 ~]$ kubectl get nodes -l env=prod
No resources found
operator
除了使用 Exists
来验证是否存在的逻辑之外,还可以使用 In
、NotIn
、DoesNotExist
、Gt
和 Lt
之一作为逻辑操作符。
如果使用非 Exist
,需要提供 values
字段来提供逻辑判断的值,例如:
preference:
matchExpressions:
- key: another-node-label-key
operator: In
values:
- another-node-label-value
节点亲和性可以通过 weight
来定义权重,这个适用于多个规则进行筛选时,满足规则且权重高的节点将被调度。
Pod 间亲和性和反亲和性
调度器在调度 Pod 时,除了有节点的亲和性,还有 Pod 间亲和性和反亲和性。
节点亲和性是基于节点的标签,Pod 间的亲和性和反亲和性是基于当前运行在节点上的 Pod 的标签。
Pod 的亲和性和反亲和性也有两种类型:
requiredDuringSchedulingIgnoredDuringExecution
preferredDuringSchedulingIgnoredDuringExecution
在 Pod 亲和性中可以使用 requiredDuringSchedulingIgnoredDuringExecution
来约束两个服务的 Pod 应该放到同一位置,可能是因为它们通信非常频繁。
在 Pod 反亲和性中可以使用 preferredDuringSchedulingIgnoredDuringExecution
来阻止同一个服务的 Pod 放置在同一个位置,避免出现单点故障。
Pod 间亲和性和反亲和性的规则格式为:如果 X 上已经运行了一个或多个满足规则 Y 的 Pod,则这个 Pod 应该运行在 X 上(反亲和性的话就是不应该)。
X 可以是节点、机架、云提供商可用区等类似的拓扑愈,Y 是 Kubernetes 尝试满足的规则。
Pod 亲和性使用 .affinity.podAffinity
字段,Pod 反亲和性使用 .affinity.podAntiAffinity
字段。
参考示例:
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: registry.k8s.io/pause:2.0
上述例子中,调度器首先会找到有 zone
标签的节点,因为没有指定 zone
的值,所以只判断是否存在该标签,如果该节点上运行了一个带有 security=S1
的标签的 Pod,则会调度到该节点,如果该节点上运行了一个带有 security=s2
的标签,则不会调度到该节点。
测试一下,首先需要在 node 上打上标签:
[vagrant@master01 ~]$ kubectl label node worker01.example.com zone=cn-1
node/worker01.example.com labeled
[vagrant@master01 ~]$ kubectl label node worker02.example.com zone=cn-2
node/worker02.example.com labeled
[vagrant@master01 ~]$ kubectl get nodes -l zone
NAME STATUS ROLES AGE VERSION
worker01.example.com Ready edge 41d v1.29.1
worker02.example.com Ready worker 41d v1.29.1
接下来运行一个带有 security=S1
标签的 Pod:
apiVersion: v1
kind: Pod
metadata:
name: nginxsecuritys1
labels:
security: S1
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
检查调度到的节点:
[vagrant@master01 ~]$ kubectl get pod -o wide | grep nginxsecuritys1
nginxsecuritys1 1/1 Running 0 22s 10.244.45.215 worker02.example.com <none> <none>
使用 Pod 亲和性将第二个 Pod 调度到跟 nginxsecuritys1
相同的节点上:
apiVersion: v1
kind: Pod
metadata:
name: nginxsecuritys2
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S1
topologyKey: zone
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
验证:
[vagrant@master01 ~]$ kubectl get pods -o wide | grep security
nginxsecuritys1 1/1 Running 0 37m 10.244.45.215 worker02.example.com <none> <none>
nginxsecuritys2 1/1 Running 0 10m 10.244.45.219 worker02.example.com <none> <none>
定义 Pod 反亲和性规则:
apiVersion: v1
kind: Pod
metadata:
name: nginxsecuritys3
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S1
topologyKey: zone
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
验证:
[vagrant@master01 ~]$ kubectl get pods -o wide | grep security
nginxsecuritys1 1/1 Running 0 37m 10.244.45.215 worker02.example.com <none> <none>
nginxsecuritys2 1/1 Running 0 10m 10.244.45.219 worker02.example.com <none> <none>
nginxsecuritys3 1/1 Running 0 3m41s 10.244.95.133 worker01.example.com <none> <none>
nodeName
使用 nodeName
优先级将高于 nodeSelector
和亲和性规则。
示例:
apiVersion: v1
kind: Pod
metadata:
name: nginxnodenameonworker1
spec:
nodeName: worker01.example.com
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
验证:
[vagrant@master01 ~]$ kubectl get pod/nginxnodenameonworker1 -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginxnodenameonworker1 1/1 Running 0 12s 10.244.95.156 worker01.example.com <none> <none>
污点和容忍度
节点亲和性使 Pod 被吸引到一类特定的节点。污点(Taint) 相反,污点应用于节点上使节点排斥一类特定的 Pod 。
容忍度(Toleration)是应用于 Pod 上的。容忍度允许调度器调度带有对应污点的 Pod。容忍度允许调度但不保证调度。
污点和容忍度的相互配合可以避免 Pod 被分配到不合适的节点上。
每个节点上可以应用多个污点,不能容忍这些污点的 Pod 是不会调度到该节点上。
使用 kubectl taint
给节点添加一个污点:
kubectl taint nodes worker01 key1=value1:NoSchedule
删除污点:
kubectl taint nodes worker01 key1=value1:NoSchedule-
上述的 NoSchedule
对应的是效果,表示只拥有和这个污点想匹配的容忍度的 Pod 才能后被调度到该节点。除此之外 effect 有以下类型:
NoExecute
:会影响已在节点上运行的 Pod,如果 Pod 不能容忍这类污点,会马上驱逐。NoSchedule
:需要具有匹配的容忍度 Pod 才会被调度,当前正在节点上运行的 Pod 不会被驱逐。PreferNoSchedule
:软性的NoSchedule
,尝试避免将不能容忍的 Pod 调度到节点上,但不保证完全避免。 在 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"
Pod 容忍度也有 operator
参数:
- Equal:如果键、值和效果参数匹配,则容忍度和污点匹配,这是默认的行为
- Exists:如果键和效果参数匹配,则容忍度和污点匹配,必须将值参数留空