K8s集群中的Pod调度约束:污点、容忍与驱逐

时间:2024-06-02 20:38:00

前言

在 Kubernetes 集群中,Pod 的调度约束是确保工作负载按照特定规则在集群中正确部署的关键因素。其中,污点(Taints)、约束(Affinity)、容忍(Toleration)和驱逐等概念扮演着重要角色。本文将介绍这些概念,以及它们在K8s集群中的作用和影响。

目录

一、污点(Taint)

1. 概述

2. taint effect 支持的选项

3. 污点相关操作

3.1 设置污点

3.2 去除污点

二、容忍(Tolerations)

1. 概述

2. 设置 Pod 容忍

3. 其它注意事项

三、驱逐(cordon 和 drain)

1. 维护操作

1.1 cordon(隔离)

1.2 Drain(排空)

2. phase 的可能状态有 

四、故障排除步骤 


一、污点(Taint)

1. 概述

节点亲和性让 Pod 倾向或强制部署在特定节点上,而 Taint 则是节点用来拒绝不兼容 Pod 的机制。每个节点可设多个 Taint,拒绝不容忍这些 Taint 的 Pod 接入。通过 kubectl taint 命令可以给 Node 添加污点限制,Node 被设置上污点之后就和 Pod 之间存在了一种相斥的关系,可以让 Node 拒绝 Pod 的调度执行,甚至将 Node 已经存在的 Pod 驱逐出去。

污点的组成格式为:key=value:effect。每个污点有一个 key 和 value 作为污点的标签,其中 value 可以为空,effect 描述污点的作用。

2. taint effect 支持的选项

NoSchedule:表示 k8s 不会将 Pod 调度到具有该污点的 Node 上;

PreferNoSchedule:表示 k8s 将尽量避免将 Pod 调度到具有该污点的 Node 上;

NoExecute:表示 k8s 将不会将 Pod 调度到具有该污点的 Node 上,同时会将 Node 上已经存在的 Pod 驱逐出去。

master 就是因为有 NoSchedule 污点,k8s 才不会将 Pod 调度到 master 节点上:

[root@master01 ~]# kubectl describe node master | grep -i taints
Taints:             node-role.kubernetes.io/master:NoSchedule

3. 污点相关操作

3.1 设置污点

格式:
kubectl taint node node名字 key1=value1:NoSchedule 

① 创建副本 yaml 文件

[root@master01 data]#vim pod01.yaml  
apiVersion: apps/v1  
kind: Deployment  
metadata:
  name: pod-nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      pod: nginx
  template:
    metadata:
      labels:
        pod: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14
        ports:
        - containerPort: 80

② 给 node01 设置污点

[root@master01 data]# kubectl taint node node01 w=d:NoSchedule
node/node01 tainted

③ 启动 pod,并查看 pod 详情信息

[root@master01 data]# kubectl apply -f pod1.yaml 
deployment.apps/pod-nginx created
[root@master01 data]# kubectl get pod -o wide
NAME                        READY   STATUS    RESTARTS   AGE   IP            NODE     NOMINATED NODE   READINESS GATES
pod-nginx-76c6c5b65-296dl   1/1     Running   0          69s   10.244.2.18   node02   <none>           <none>
pod-nginx-76c6c5b65-jhqt2   1/1     Running   0          69s   10.244.2.20   node02   <none>           <none>
pod-nginx-76c6c5b65-kqbc2   1/1     Running   0          69s   10.244.2.19   node02   <none>           <none>

由于 node01 节点设置了污点,所以 k8s 不会将 Pod 调度到该节点上。

④ 节点说明,查找 Taints 字段

格式:
kubectl describe node node名字

[root@master01 data]# kubectl describe node node01 | grep -i taints
Taints:             w=d:NoSchedule

3.2 去除污点

格式:
kubectl taint node node名字 key1:NoSchedule-

① 去除 node01 节点污点

[root@master01 data]# kubectl taint node node01 w:NoSchedule-
node/node01 untainted
[root@master01 data]# kubectl describe node node01 | grep -i taint
Taints:             <none>

② 查看 pod 详情信息

[root@master01 data]# kubectl get pod -o wide
NAME                        READY   STATUS    RESTARTS   AGE   IP            NODE     NOMINATED NODE   READINESS GATES
pod-nginx-76c6c5b65-296dl   1/1     Running   0          10m   10.244.2.18   node02   <none>           <none>
pod-nginx-76c6c5b65-jhqt2   1/1     Running   0          10m   10.244.2.20   node02   <none>           <none>
pod-nginx-76c6c5b65-kqbc2   1/1     Running   0          10m   10.244.2.19   node02   <none>           <none>

③ 设置 node02 节点污点,添加 NoExecute 选项

[root@master01 data]# kubectl taint node node02 fql=b:NoExecute
node/node02 tainted

④ 持续查看 pod 详情信息

[root@master01 data]# kubectl get pod -o wide -w
NAME                        READY   STATUS              RESTARTS   AGE   IP            NODE     NOMINATED NODE   READINESS GATES
pod-nginx-76c6c5b65-296dl   0/1     Terminating         0          13m   10.244.2.18   node02   <none>           <none>
pod-nginx-76c6c5b65-2h46w   0/1     ContainerCreating   0          3s    <none>        node01   <none>           <none>
pod-nginx-76c6c5b65-2ldxl   0/1     ContainerCreating   0          3s    <none>        node01   <none>           <none>
pod-nginx-76c6c5b65-jhqt2   0/1     Terminating         0          13m   <none>        node02   <none>           <none>
pod-nginx-76c6c5b65-kqbc2   0/1     Terminating         0          13m   <none>        node02   <none>           <none>
pod-nginx-76c6c5b65-mrfnl   0/1     ContainerCreating   0          3s    <none>        node01   <none>           <none>
pod-nginx-76c6c5b65-kqbc2   0/1     Terminating         0          13m   <none>        node02   <none>           <none>
pod-nginx-76c6c5b65-kqbc2   0/1     Terminating         0          13m   <none>        node02   <none>           <none>
pod-nginx-76c6c5b65-jhqt2   0/1     Terminating         0          13m   <none>        node02   <none>           <none>
pod-nginx-76c6c5b65-jhqt2   0/1     Terminating         0          13m   <none>        node02   <none>           <none>
pod-nginx-76c6c5b65-296dl   0/1     Terminating         0          13m   10.244.2.18   node02   <none>           <none>
pod-nginx-76c6c5b65-296dl   0/1     Terminating         0          13m   10.244.2.18   node02   <none>           <none>
pod-nginx-76c6c5b65-mrfnl   1/1     Running             0          32s   10.244.1.51   node01   <none>           <none>
pod-nginx-76c6c5b65-2h46w   1/1     Running             0          33s   10.244.1.52   node01   <none>           <none>
pod-nginx-76c6c5b65-2ldxl   1/1     Running             0          84s   10.244.1.54   node01   <none>           <none>

当给 node02 设置污点 NoExecute 选项时,k8s 将不会将 Pod 调度到具有该污点的 Node 上,同时会将 Node 上已经存在的 Pod 驱逐出去。

另一方面,pod 是 Deployment 或者 StatefulSet 资源类型,为了维持副本数量则会在别的 Node 上再创建新的 pod;

此时 node01 没有污点,所以自然 pod 会转移到 node01 节点上。

二、容忍(Tolerations)

1. 概述

用于标记 pod 可以在哪些 node 节点调度运行,如果一个节点上拥有 pod 容忍度中指定 taint(污点),那么该节点上就可以调度 pod。

设置了污点的 Node 将根据 taint 的 effect:NoSchedule、PreferNoSchedule、NoExecute 和 Pod 之间产生互斥的关系,Pod 将在一定程度上不会被调度到 Node 上。但我们可以在 Pod 上设置容忍(Tolerations),意思是设置了容忍的 Pod 将可以容忍污点的存在,可以被调度到存在污点的 Node 上。

2. 设置 Pod 容忍

① 将所有 node 节点设置污点

[root@master01 data]# kubectl taint node node01 fql=a:NoExecute
node/node01 tainted
[root@master01 data]# kubectl taint node node02 fql=b:NoExecute
node/node02 tainted

[root@master01 data]# kubectl describe node node01 | grep -i taints
Taints:             fql=a:NoExecute
[root@master01 data]# kubectl describe node node02 | grep -i taints
Taints:             fql=b:NoExecute

② 尝试创建 pod

[root@master01 data]# kubectl apply -f pod1.yaml 
deployment.apps/pod-nginx created
[root@master01 data]# kubectl get pod
NAME                        READY   STATUS    RESTARTS   AGE
pod-nginx-76c6c5b65-pkjn9   0/1     Pending   0          4s

由此可见,无法创建 pod,尝试创建 pod 容忍。

③ 修改 yaml 文件,设置容忍

[root@master01 data]# vim pod1.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: myapp01
  labels:
    app: myapp01
spec:
  containers:
  - name: nginx01
    image: soscscs/nginx01:v1
  tolerations:                # 容忍策略
  - key: "fql"                # 污点的键
    operator: "Equal"         # 污点的操作符,指定了要匹配污点键的精确匹配,Exists表示所有
    value: "a"                # 污点的值
    effect: "NoExecute"       # 污点的效果
    tolerationSeconds: 30     # 容忍污点的时间为30秒

# 其中的 key、vaule、effect 都要与 Node 上设置的 taint 保持一致
# operator 的值为 Exists 将会忽略 value 值,即存在即可
# tolerationSeconds 用于描述当 Pod 需要被驱逐时可以在 Node 上继续保留运行的时间

④ 更新 pod,持续观察 pod 详情信息

[root@master01 data]# kubectl apply -f pod1.yaml 
pod/myapp01 created

[root@master01 data]# kubectl get pod -w
NAME      READY   STATUS    RESTARTS   AGE
myapp01   0/1     Pending   0          0s
myapp01   0/1     Pending   0          0s
myapp01   0/1     ContainerCreating   0          1s
myapp01   1/1     Running             0          2s
myapp01   1/1     Terminating         0          31s
myapp01   0/1     Terminating         0          32s

由此可见,当到30秒的时候,pod 将会被驱逐。

3. 其它注意事项

① 当不指定 key 值时,表示容忍所有的污点 key

  tolerations:
  - operator: "Exists"

这条规则意味着 Pod 可以被调度到任何带有污点的节点上,无论污点的键(key)是什么。这里的操作符 "Exists" 表示只要目标节点上有任何污点存在,不论其具体值或效果(effect),Pod 都会容忍并被允许调度到那个节点上。 

② 当不指定 effect 值时,表示容忍所有的污点作用

  tolerations:
  - key: "key"
    operator: "Exists"

③ 有多个 Master 存在时,防止资源浪费,可以如下设置

kubectl taint node Master-Name node-role.kubernetes.io/master=:PreferNoSchedule

④ 如果某个 Node 更新升级系统组件,为了防止业务长时间中断,可以先在该 Node 设置 NoExecute 污点,把该 Node 上的 Pod 都驱逐出去

kubectl taint node node01 check=mycheck:NoExecute

⑤ 此时如果别的 Node 资源不够用,可临时给 Master 设置 PreferNoSchedule 污点,让 Pod 可在 Master 上临时创建

kubectl taint node master node-role.kubernetes.io/master=:PreferNoSchedule

⑥ 待所有 Node 的更新操作都完成后,再去除污点

kubectl taint node node01 check=mycheck:NoExecute-

三、驱逐(cordon 和 drain)

1. 维护操作

cordon 和 drain 是两个重要的命令,主要用于节点维护和资源调度管理,它们在节点升级、硬件维护或故障处理等场景。

1.1 cordon(隔离)

将 Node 标记为不可调度的状态,这样就不会让新创建的 Pod 在此 Node 上运行。

示例:

① 创建 pod

[root@master01 data]# vim pod2.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: nginx01
        image: nginx:1.14

[root@master01 data]# kubectl get pod -o wide
NAME                                READY   STATUS    RESTARTS   AGE   IP            NODE     NOMINATED NODE   READINESS GATES
nginx-deployment-596c8f4864-khz5z   1/1     Running   0          13s   10.244.2.23   node02   <none>           <none>

② 将 node02 标记为不可调度

[root@master01 data]# kubectl cordon node02
[root@master01 data]# kubectl describe node node02 | grep -i taints
Taints:             node.kubernetes.io/unschedulable:NoSchedule

③ 再次创建新的 pod 查看调度情况

[root@master01 data]# kubectl apply -f pod2.yaml  # 需要修改 yaml 控制器名 
[root@master01 data]# kubectl apply -f pod2.yaml
[root@master01 data]# kubectl get pod -o wide
NAME                                  READY   STATUS    RESTARTS   AGE     IP             NODE     NOMINATED NODE   READINESS GATES
nginx-deployment-596c8f4864-khz5z     1/1     Running   0          3m38s   10.244.2.23    node02   <none>           <none>
nginx01-deployment-596c8f4864-v9k2x   1/1     Running   0          22s     10.244.1.186   node01   <none>           <none>
nginx02-deployment-596c8f4864-pk9r2   1/1     Running   0          4s      10.244.1.187   node01   <none>           <none>

由于 node02 标记为不可调度,所有新建的 pod 将会被调度到 node01。同时 node02 原来的 pod 不受影响。

1.2 Drain(排空)

kubectl drain 可以让 Node 节点开始释放所有 pod,并且不接收新的 pod 进程。drain 本意排水,意思是将出问题的 Node 下的 Pod 转移到其它 Node 下运行。

格式:

kubectl drain <NODE_NAME> --ignore-daemonsets --delete-local-data --force

# --ignore-daemonsets:无视 DaemonSet 管理下的 Pod。
# --delete-local-data:如果有 mount local volume 的 pod,会强制杀掉该 pod。
# --force:强制释放不是控制器管理的 Pod,例如 kube-proxy。

示例:

① kubectl uncordon 将 node02 标记为可调度的状态

[root@master01 data]# kubectl uncordon node02
node/node02 uncordoned
[root@master01 data]# kubectl describe node node02 | grep -i taints
Taints:             <none>

② 驱逐 node01 节点 上的 pod

[root@master01 data]# kubectl drain node01 --ignore-daemonsets --delete-local-data --force
Flag --delete-local-data has been deprecated, This option is deprecated and will be deleted. Use --delete-emptydir-data.
node/node01 cordoned
WARNING: ignoring DaemonSet-managed Pods: kube-flannel/kube-flannel-ds-lhlv2, kube-system/kube-proxy-w7vl7
evicting pod default/nginx02-deployment-596c8f4864-pk9r2
evicting pod default/nginx01-deployment-596c8f4864-v9k2x
pod/nginx02-deployment-596c8f4864-pk9r2 evicted
pod/nginx01-deployment-596c8f4864-v9k2x evicted
node/node01 evicted

③ 持续查看 pod 调度情况

[root@master01 data]# kubectl get pod -w
NAME                                  READY   STATUS    RESTARTS   AGE
nginx-deployment-596c8f4864-khz5z     1/1     Running   0          11m
nginx01-deployment-596c8f4864-v9k2x   1/1     Running   0          8m21s
nginx02-deployment-596c8f4864-pk9r2   1/1     Running   0          8m3s
nginx01-deployment-596c8f4864-v9k2x   1/1     Terminating   0          10m
nginx02-deployment-596c8f4864-pk9r2   1/1     Terminating   0          10m
nginx01-deployment-596c8f4864-jwx7m   0/1     Pending       0          0s
nginx02-deployment-596c8f4864-rqcg9   0/1     Pending       0          0s
nginx01-deployment-596c8f4864-jwx7m   0/1     Pending       0          0s
nginx02-deployment-596c8f4864-rqcg9   0/1     Pending       0          0s
nginx02-deployment-596c8f4864-rqcg9   0/1     ContainerCreating   0          0s
nginx01-deployment-596c8f4864-jwx7m   0/1     ContainerCreating   0          0s
nginx01-deployment-596c8f4864-v9k2x   0/1     Terminating         0          10m
nginx02-deployment-596c8f4864-pk9r2   0/1     Terminating         0          10m
nginx01-deployment-596c8f4864-v9k2x   0/1     Terminating         0          10m
nginx01-deployment-596c8f4864-v9k2x   0/1     Terminating         0          10m
nginx02-deployment-596c8f4864-pk9r2   0/1     Terminating         0          10m
nginx02-deployment-596c8f4864-pk9r2   0/1     Terminating         0          10m
nginx01-deployment-596c8f4864-jwx7m   1/1     Running             0          2s
nginx02-deployment-596c8f4864-rqcg9   1/1     Running             0          2s

[root@master01 data]# kubectl get pod -o wide
NAME                                  READY   STATUS    RESTARTS   AGE     IP            NODE     NOMINATED NODE   READINESS GATES
nginx-deployment-596c8f4864-khz5z     1/1     Running   0          16m     10.244.2.23   node02   <none>           <none>
nginx01-deployment-596c8f4864-jwx7m   1/1     Running   0          2m16s   10.244.2.24   node02   <none>           <none>
nginx02-deployment-596c8f4864-rqcg9   1/1     Running   0          2m16s   10.244.2.25   node02   <none>           <none>

可以看到已经 pod 已经被调度到 node02。执行 drain 命令,会自动做了两件事情:

  • 设定此 node 为不可调度状态(cordon)
  • evict(驱逐)了 Pod

2. phase 的可能状态有 

Pending:表示APIServer创建了Pod资源对象并已经存入了etcd中,但是它并未被调度完成(比如还没有调度到某台node上),或者仍然处于从仓库下载镜像的过程中;

Running:Pod已经被调度到某节点之上,并且Pod中所有容器都已经被kubelet创建。至少有一个容器正在运行,或者正处于启动或者重启状态(也就是说Running状态下的Pod不一定能被正常访问);

Succeeded:有些pod不是长久运行的,比如job、cronjob,一段时间后Pod中的所有容器都被成功终止,并且不会再重启。需要反馈任务执行的结果;

Failed:Pod中的所有容器都已终止了,并且至少有一个容器是因为失败终止。也就是说,容器以非0状态退出或者被系统终止,比如 command 写的有问题;

Unknown:表示无法读取 Pod 状态,通常是 kube-controller-manager 无法与 Pod 通信。

四、故障排除步骤 

查看Pod事件:
kubectl describe TYPE NAME_PREFIX  

查看Pod日志(Failed状态下):
kubectl logs <POD_NAME> [-c Container_NAME]

进入Pod(状态为running,但是服务没有提供):
kubectl exec –it <POD_NAME> bash

查看集群信息:
kubectl get nodes

发现集群状态正常:
kubectl cluster-info

查看kubelet日志发现:
journalctl -xefu kubelet