k8s Pod调度失败(NoExecute)排查及分析

2023-03-18 11:39:09 浏览数 (2)

问题背景

k8s集群,总共5个节点,信息如下所示:

集群

污点

k8s版本

系统版本

master

1.14

Centos 7.1

work1

1.14

Centos 7.1

work2

1.14

Centos 7.1

work3

1.14

Centos 7.1

work4

1.14

Centos 7.1

其中work1 Pod有特殊要求,需要访问外网,在work1节点添加了NoExecute污点,其它不能容忍该污点的Pod不能被调度到该节点。

正常情况下该Pod正常启动和使用,偶尔一次此机器出现断电故障,但是机器重启之后,发现该Pod无法启动,kubectl describe pod发现如下错误:

代码语言:javascript复制
1 node(s) had taints that the pod didn't tolerate 2 node(s) didn't match node selector

大概意思是说,其中一个节点包含该污点,但是该Pod无法容忍。

既然包含,为什么无法容忍呢?

原因查找过程

1、 首先确认污点是否存在,执行如下命令:

代码语言:javascript复制
[root@work2 log]# kubectl describe node/work2 |grep Taint
Taints:             caf=log:NoExecute
                    node.kubernetes.io/unreachable:NoExecute
                    node.kubernetes.io/unreachable:NoSchedule
                    

确认污点确实是存在的。

2、查看整个node的污点,执行命令如下所示:

代码语言:javascript复制
kubectl describe node work2
......
CreationTimestamp:  Thu, 10 Sep 2020 12:05:45  0800
Taints:             caf=log:NoExecute
                    node.kubernetes.io/unreachable:NoExecute
                    node.kubernetes.io/unreachable:NoSchedule
.....

看到这里,问题原因大概找到了,原因是因为机器在关机同时,k8s自动为这个节点添加了不可被调度污点 node.kubernetes.io/unreachable:NoExecute,所以也就导致我的业务Pod不可被调度。

解决过程

一开始个人想法只要把k8s添加的污点给删除了,也就可以被调度了,于是我开始手动删除污点,具体执行命令如下所示:

kubectl taint node k8snode2 node.kubernetes.io/unreachable-

然后再次查看该节点污点是否正常,奇怪的事情发生了,执行kubectl describe node work2查看污点,NoSchedule删除了。但是NoExecute无法删除。

NoSchedule:如果一个pod没有声明容忍这个Taint,则系统不会把该Pod调度到有这个Taintnode

NoExecute:定义pod的驱逐行为,以应对节点故障。

因为无法被删除,所以Pod依然无法被调度到该节点,我又想了,有没有办法删除所有的污点,这样就连带着把这个不可用污点也给删除了,答案找到了,通过如下命令:kubectl patch node k8s-node1 -p '{"spec":{"Taints":[]}}' 事实证明我又异想天开了,所有污点都删除了,但依然无法删除NoExecute污点。

怎么办,乖乖看官方文档去,这么成熟的k8s怎么会有这种低级问题?

NoExecute上面提到的污点会影响节点上已经运行的Pod,如下所示:

  • 立即将不能忍受的污点逐出
  • 容忍污点但未定义tolerationSecondsPod将永远绑定
  • 可以忍受指定污点的Pod在指定的时间内保持绑定。当某些条件为真时,节点控制器会自动为节点添加污点。内置以下污点:

node.kubernetes.io/not-ready:节点尚未准备好。这对应于NodeConditionReadyFalse

node.kubernetes.io/unreachable:无法从节点控制器访问节点。这对应于NodeConditionReadyUnknown

node.kubernetes.io/out-of-disk:节点磁盘不足。

node.kubernetes.io/memory-pressure:节点有内存压力。

node.kubernetes.io/disk-pressure:节点有磁盘压力。

node.kubernetes.io/network-unavailable:节点的网络不可用。

node.kubernetes.io/unschedulable:节点不可调度。

node.cloudprovider.kubernetes.io/uninitialized:当kubelet从外部云服务提供程序启动时,在节点上设置此污点以将其标记为不可用。来自cloud-controller-manager的控制器初始化此节点后,kubelet删除此污点。

如果要逐出节点,则节点控制器或kubelet会添加相关的污点NoExecute。如果故障情况恢复正常,则kubelet或节点控制器可以删除相关的污点。具体文档地址,如下所示:https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/

大概意思是说,之所以出现此污点,是k8s内部认为该节点尚不能工作,所以添加了此污点,防止Pod调度到此节点,看了半天,原来节点底层出现故障了,首先查看下kubelet状态,状态不正常,如下所示:

代码语言:javascript复制
[root@work2 yaml]# systemctl status kubelet                 
● kubelet.service - kubelet: The Kubernetes Node Agent
   Loaded: loaded (/usr/lib/systemd/system/kubelet.service; disabled; vendor preset: disabled)
  Drop-In: /usr/lib/systemd/system/kubelet.service.d
           └─10-kubeadm.conf
   Active: inactive (dead)
     Docs: https://kubernetes.io/docs/

原来如此,k8s圈内有句话说的比较恰当,如果说APIServer是整个集群的大脑,那么kubelet就是每个节点的小脑,它主要用于跟APIServer交互,让APIServer获取节点的状态信息,现在kubelet已经挂了,很自然无法进行Pod的调度了。直接执行:systemctl start kubelet再次查看状态,已经正常,如下所示:

代码语言:javascript复制
[root@work2 yaml]# systemctl status kubelet
● kubelet.service - kubelet: The Kubernetes Node Agent
   Loaded: loaded (/usr/lib/systemd/system/kubelet.service; disabled; vendor preset: disabled)
  Drop-In: /usr/lib/systemd/system/kubelet.service.d
           └─10-kubeadm.conf
   Active: active (running) since Tue 2020-11-17 14:46:35 CST; 1 day 1h ago
     Docs: https://kubernetes.io/docs/
 Main PID: 15763 (kubelet)
   Memory: 65.9M
   CGroup: /system.slice/kubelet.service
           └─15763 /usr/bin/kubelet --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernete...

再次执行kubectl describe nodes work2 NoExecute污点已经消失;紧接着Pod已经可以调度到该节点。

总结分析

如上所说的就是一次关机重启kubelet没有启动,导致服务的异常,那么问题原因真正原因又是什么呢?通过查看/var/log/messages内核日志,发现机器启动的时候,并没有启动kubelet,也就是说kubelet没有加到开机启动项里面,于是执行如下命令:systemctl enable kubelet设置开机自启动。

常见的kubelet无法启动大多是因为没有关闭交换内存导致,所以可以执行swapoff -a并且执行vi /etc/fstab将文件中的/dev/mapper/centos-swap swap swap defaults 0 0这一行注释掉,输入free -mswap那一行输出为0,则说明已经关闭。

Pod不能正确被调度的原因大多是资源不足造成的,可能是CPU、内存、也可能是超过单个节点容纳Pod最大数量,碰到此类异常,根据异常信息具体分析即可!好了,一篇水文就先说到这里!

推荐

Ingress-nginx灰度发布功能详解

如何使用 Ingress-nginx 进行前后端分离?

深入探究 K8S ConfigMap 和 Secret

Kubernetes入门培训(内含PPT)


0 人点赞