Kubernetes 亲和性、反亲和性、污点、容忍、维护驱逐
kubernetes nodeSelector节点选择、nodeAffinity节点亲和性、podAffinity pod亲和性,podAntiAffinity pod反亲和性、污点taints与容忍tolerations、挂维护 cordon 、 驱逐 drain
目录
1、亲和性
在默认情况下,Pod 通过默认调度策略来选择跑在哪个节点上,默认调度器算法考虑的因素有资源,负载等等,但有些场景比如
- 我就想把pod部署在某个node节点上,或者就想跟某个pod部署在同一node节点上;
- 我就不想把pod部署在某个node节点上,或者不想跟某个pod部署在同一node节点上;
这就需要用到 Kubernetes 里面的一个概念:亲和性和反亲和性
当然亲和性和反亲和性根据上面的情况有分成:
- 节点亲和性(
nodeAffinity
) - Pod 亲和性(
podAffinity
)
1.1 nodeSelector 节点选择
节点选择是如何实现的,是通过给node增加label标签,然后
需要用到nodeSelector标签选择来实现。
我们可以通过下面的命令查看我们的 node 的 label:
$ kubectl get nodes --show-labels
NAME STATUS ROLES AGE VERSION LABELS
master Ready master 247d *** ***
node02 Ready <none> 247d *** ***
node03 Ready <none> 227d *** ***
现在我们先给节点node02增加一个tag=unintelligible
的标签,命令如下:
$ kubectl label nodes node02 tag=unintelligible
node "node02" labeled
我们可以通过上面的--show-labels
参数可以查看上述标签是否生效
当 node 打上了标签后,Pod 只需要在spec里
添加nodeSelector
字段,标签选择节点刚打上的 label 即可。
apiVersion: v1
kind: Pod
metadata:
labels:
app: busybox
name: busybox
spec:
containers:
- command:
- sleep
- "3600"
image: busybox
imagePullPolicy: Always
name: busybox
nodeSelector:
tag: unintelligible
通过kubectl create -f创建pod后,可以通过kubectl get pod -o wide查看验证一下
$ kubectl get pods -o wide -l app=busybox
NAME READY STATUS RESTARTS AGE IP NODE
busybox 1/1 Running 164 1m 10.244.4.10 node02
也可以通过describe查看详细事件
需要注意的是nodeSelector是
强制性的,必须在目标节点启动,如果目标节点不满足条件,比如资源不足啥的,那 Pod 就会 Pending 等待,直到可以创建
通过上面的例子我们可以感受到nodeSelector
的方式比较直观,但是还够灵活,控制粒度偏大,接下来我们再和大家了解下更加灵活的方式:节点亲和性(nodeAffinity
)。
1.2 亲和性和反亲和性调度
通过上面的例子我们可以感受到nodeSelector
的方式比较直观,但是还够灵活,控制粒度偏大,那么就有了更加灵活的方式:节点亲和性(nodeAffinity
) Pod 亲和性(podAffinity
)。
亲和性调度可以分成软策略和硬策略两种方式:
软策略:
满足条件最好,不满足也无所谓,如果你没有满足调度要求的节点的话,pod 就会忽略这条规则,继续完成调度过程硬策略:
必须满足 比较强硬,如果没有满足条件的节点的话,就不断重试直到满足条件为止。
对于亲和性和反亲和性都有这两种规则可以设置:
软策略:preferredDuringSchedulingIgnoredDuringExecution
硬策略:requiredDuringSchedulingIgnoredDuringExecution
别问我咋念,也别问我咋写,我也记不住。。。
1.2.1 nodeAffinity 节点亲和性
节点亲和性就是控制 pod 要部署在哪些主机上,不能部署在哪些主机上。
比如现在我们用一个 Deployment 来管理3个 pod 副本,现在我们来控制下这些 pod 的调度,如下
apiVersion: apps/v1
kind: Deployment
metadata:
name: affinity
labels:
app: affinity
spec:
replicas: 3
selector:
matchLabels:
app: affinity
role: test
template:
metadata:
labels:
app: affinity
role: test
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
name: http
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution: # 硬
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: NotIn
values:
- node03
preferredDuringSchedulingIgnoredDuringExecution: # 软
- weight: 1
preference:
matchExpressions:
- key: tag
operator: In
values:
- unintelligible
上面这个 pod 强硬要求不能运行在 node03 这个节点上,剩下的node都可以,但是如果有节点满足tag=unintelligible
的话就优先调度到这个节点上。
$ kubectl create -f node-affinity.yaml
deployment.apps "affinity" created
$ kubectl get pods -l app=affinity -o wide
NAME READY STATUS RESTARTS AGE IP NODE
affinity-7b4c946854-5gfln 1/1 Running 0 47s 10.244.4.121 node02
affinity-7b4c946854-l8b47 1/1 Running 0 47s 10.244.4.122 node02
affinity-7b4c946854-r86p5 1/1 Running 0 47s 10.244.4.124 node02
从结果可以看出 pod 都被部署到了 node02
另外匹配的方法有如下几种
- In:label 值在列表中
- NotIn:label 值不在列表中
- Gt:label 值大于指定值
- Lt:label 值小于指定值
- Exists: label已经存在
- DoesNotExist:label不存在
nodeSelectorTerms
下面可以有多个matchExpressions
,满足任何一个条件就可以了
matchExpressions
下也可以有多个选项,必须同时满足这个matchExpressions
下的所有条件
1.2.2 podAffinity pod亲和性
pod 亲和性主要是想把pod和某个依赖的pod放在一起,而 pod 反亲和性主要想把 pod和某个pod分开。
pod亲和性举例:比如集群是跨机房的,两个pod之间依赖度比较高,部署到了两个机房,互相访问就比较耗费网络资源,延迟比较大,这时候就需要pod亲和性
pod反亲和性举例:比如某个pod平时占用IO比较大,另一个pod占用IO也比较大,那就别往一起凑合了,分开点吧
同样,我们来测试下 pod 的亲和性:
apiVersion: apps/v1
kind: Deployment
metadata:
name: affinity
labels:
app: affinity
spec:
replicas: 3
selector:
matchLabels:
app: affinity
role: test
template:
metadata:
labels:
app: affinity
role: test
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
name: http
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution: # 硬策略
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- busybox
topologyKey: kubernetes.io/hostname
pod 需要调度到没有app=busybox这个
label的pod所在的node上,就是说这个node上的pod不能有app=busybox
。
topologyKey:个人理解为满足这个条件的标签集合,标签是 kubernetes.io/hostname
当然也可以是其他标签,我们用当前这个例子来解释一下,比如busybox运行在node2上,node2的标签应该是kubernetes.io/hostname=node2,那后pod亲和性调度就要调度到kubernetes.io/hostname=node2的上面,pod反亲和性调度就要调度到kubernetes.io/hostname不等于node2
在举个例子,比如一个k8s集群,北京有100个节点,每个节点都有个标签zone=beijing,上海有100个节点,每个节点都有个标签为zone=shanghai
如果设置了pod亲和性,app In busybox ,且busybox的pod所在的机器标签zone=beijing,那么新的pod也一定会调度到zone=beijing上
如果设置pod反亲和性,app In busybox ,且busybox的pod所在的机器标签zone=beijing,那么新的pod一定会调度到zone=shanghai上,一个都不会落到beijing
我们查看有标签app=busybox
的 pod 列表:
$ kubectl get pods -o wide -l app=busybox
NAME READY STATUS RESTARTS AGE IP NODE
test-busybox 1/1 Running 164 1h 10.244.4.205 node02
我们看到这个 pod 运行在了 node02 的节点上面,所以按照上面的亲和性来说,上面我们部署的3个 pod 副本也应该运行在 node02 节点上:
$ kubectl get pods -o wide -l app=affinity
NAME READY STATUS RESTARTS AGE IP NODE
affinity-564f9d7db9-lzzvq 1/1 Running 0 3m 10.244.4.216 node02
affinity-564f9d7db9-p79cq 1/1 Running 0 3m 10.244.4.217 node02
affinity-564f9d7db9-spfzs 1/1 Running 0 3m 10.244.4.218 node02
如果我们把 busybox 和 affinity 这个 Deployment 都删除,然后重新创建 affinity 这个资源,将不能正常调度,一直处于pending状态
1.2.3 podAntiAffinity pod反亲和性
pod 反亲和性是反着来的,比如一个节点上运行了某个 pod,那么我们的 pod 则希望被调度到其他节点上去,同样我们把上面的 podAffinity 直接改成 podAntiAffinity
apiVersion: apps/v1
kind: Deployment
metadata:
name: affinity
labels:
app: affinity
spec:
replicas: 3
selector:
matchLabels:
app: affinity
role: test
template:
metadata:
labels:
app: affinity
role: test
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
name: http
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution: # 硬策略
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- busybox
topologyKey: kubernetes.io/hostname
2、污点与容忍
nodeAffinity
无论是硬策略还是软策略方式,都是调度 pod 到预期节点上,而Taints
恰好与之相反,如果一个节点标记为 Taints ,除非 pod 也被标识为可以容忍污点节点,否则该 Taints 节点不会被调度 pod。
污点:taints
容忍:tolerations
2.1 污点策略
污点策略有以下选项:
- NoSchedule:表示 pod 不会被调度到标记为 taints 的节点
PreferNoSchedule
:NoSchedule 的软策略版本,表示尽量不调度到污点节点上去NoExecute
:该选项意味着一旦 Taint 生效,如该节点内正在运行的 pod 没有对应 Tolerate 设置,会直接被逐出
污点 taint 标记节点的命令如下:
$ kubectl taint nodes node02 test=node02:NoSchedule
node "node02" tainted
2.2 容忍
上面的命名将 node02 节点标记为了污点,影响策略是 NoSchedule,只会影响新的 pod 调度,如果仍然希望某个 pod 调度到 taint 节点上,则必须在 Spec 中做出Toleration
定义,才能调度到该节点
比如现在我们想要将一个 pod 调度到 node02节点
apiVersion: apps/v1
kind: Deployment
metadata:
name: taint
labels:
app: taint
spec:
replicas: 3
revisionHistoryLimit: 10
selector:
matchLabels:
app: taint
template:
metadata:
labels:
app: taint
spec:
containers:
- name: nginx
image: nginx
ports:
- name: http
containerPort: 80
tolerations:
- key: "test"
operator: "Equal"
value: "value1"
effect: "NoSchedule"
由于 node02节点被标记为了污点节点,所以我们这里要想 pod 能够调度到 node02 节点去,就需要增加容忍的声明,以下两种方法均可:
tolerations:
- key: "test"
operator: "Equal"
value: "node02"
effect: "NoSchedule"
tolerations:
- key: "test"
operator: "Exists"
effect: "NoSchedule"
一个容忍度和一个污点相“匹配”是指它们有一样的键名和效果,并且:
- 如果
operator
是Exists
(此时容忍度不能指定value
)- 如果
operator
是Equal
,则它们的value
应该相等- 如果
operator
不指定,则默认为Equal另外:
- 空的 key 如果再配合 Exists 就能匹配所有的 key 与 value,也是是能容忍所有 node 的所有 Taints
- 空的 effect 匹配所有的 effect
然后创建上面的资源,查看结果会发现有的pod会落在node02上
设置NoExcute 的驱逐时间
通常情况下,如果给一个节点添加了一个 effect 值为NoExecute
的污点, 则任何不能忍受这个污点的 Pod 都会马上被驱逐, 任何可以忍受这个污点的 Pod 都不会被驱逐。
但是,如果 Pod 存在一个 effect 值为NoExecute
的容忍度指定了可选属性tolerationSeconds
的值,则表示 Pod 还能继续在节点上运行的时间。
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoExecute"
tolerationSeconds: 3600
这表示如果这个 Pod 正在运行,这时候被打上了NoExecute
的污点, 那么 Pod 还将继续运行 3600 秒,然后被驱逐。 如果在3600秒内污点被删除了,Pod就不会被驱逐。
2.3 取消污点
取消节点的污点标记,只需要在刚刚创建污点命令的结尾添加个“-”即可:
$ kubectl taint nodes node02 test=node02:NoSchedule-
node "node02" untainted
3、驱逐维护
3.1 挂维护,禁止调度
# kubectl cordon node02
设置node02不可调度,通过get node查看各节点状态,发现node02为SchedulingDisabled,此时新的pod不会调度到该节点上,但是现有的pod还是正常运行。
3.2 驱逐
# kubectl drain node02 --delete-local-data --ignore-daemonsets --force
参数说明:
--delete-emptydir-data 即使有使用emptyDir的pod也删除
--ignore-daemonsets 删除daemonsets管理的pod
--force 强制删除
我们可以通过不断kubectl get pod -o wide 观察到pod在驱逐迁移的过程
3.3 解除维护,解除禁止调度
# kubectl uncordon node02
解除后新的pod可以调度到node2节点,但是之前驱逐走的不会主动回来,需要重启pod后通过系统调度选择才有可能回到此节点
更多推荐
所有评论(0)