K8S Node NotReady故障

报障:

    今日上午,值班同学发现airflow无法使用。查看时其部署的Node节点NotReady了。

分析:

    马上查看K8S集群节点的状态,发现这个节点已经是NotReady状态了。第一反应就是ping下节点看是否宕机了?ping正常,于是登录到该节点查看kubelet状态。发现kubelet报runtime不可用,查看containerd的状态,一直在不断的重启,而且启动不成功。为了尽快恢复业务,决定先将containerd的数据目录清空后重新拉起。于是删除containerd数据目录下的文件夹:

# ls -lrth /xpu-k8s-data/containerd/total 0drwx------ 2 root root 6 Apr 28 10:54 io.containerd.snapshotter.v1.btrfsdrwx------ 3 root root 31 Apr 28 10:54 io.containerd.snapshotter.v1.aufsdrwx------ 3 root root 31 Apr 28 10:54 io.containerd.snapshotter.v1.nativedrwx--x--x 2 root root 29 Apr 28 10:54 io.containerd.metadata.v1.boltdrwx--x--x 2 root root 6 Apr 28 10:54 io.containerd.runtime.v1.linuxdrwxr-xr-x 4 root root 45 Apr 28 10:54 io.containerd.content.v1.contentdrwx------ 3 root root 54 Apr 28 10:54 io.containerd.snapshotter.v1.overlayfsdrwx--x--x 3 root root 28 Apr 28 10:54 io.containerd.runtime.v2.taskdrwxr-xr-x 4 root root 53 Apr 28 10:55 io.containerd.grpc.v1.cridrwx------ 2 root root 6 Apr 28 14:49 tmpmounts# rm -rf /xpu-k8s-data/containerd/*

    发现执行的时间很长,超过几分钟。通过du命令统计该目录的大小也很久,我判断是这个目录下的小文件太多了。于是我ctrl+c掉rm和du命令。直接将该目录改个名字后重新创建一个目录。然后重新拉起containerd和kubelet进程后节点Ready了。pod也正常了。

    然后接着再来删除之前的数据目录,执行删除后经过30min-60min才删除完成。通过rm -rfv 参数可以看到打印出来删除的文件信息,是pod中存在大量的python,go的.cache和.git的小文件。查看containerd的报错:

K8S Node NotReady故障_第1张图片

K8S Node NotReady故障_第2张图片

  猜测是因为小文件数量过多,导致节点containerd停止后重启失败,不断重启导致节点NotReady的。

    恢复一段时间后,airflow跑任务时,拉起个别的pod没有问题,但是当同时拉起几十个pod跑任务的时候有很多的pod无法启动。报如下错误:

    该节点的pod网段IP地址已经用完,无法分配IP了。集群设计的时候给每个Node的子网掩码是25。可以部署125个pod。但是现在节点上的pod才十几个。猜测是之前节点故障的时候,直接删除了元数据后拉起containerd导致原来的pod在系统上的网络信息没有被删除。导致IP被占用了。我们的集群采用了flannel组件,并且开启了--kube-subnet-mgr参数。所以IP分配信息不会记录到etcd,而是直接记录到Node节点上的。

# ps -ef | grep flannelroot 6450 45116 0 14:59 pts/4 00:00:00 grep --color=auto flannelroot 53065 52700 0 13:02 ? 00:00:43 /opt/bin/flanneld --ip-masq --kube-subnet-mgr

   Pod在Node上的网络信息主要有两点:

       1)容器在Node侧的vethxxx接口;

       2)容器在Node侧的IP地址;

    所以,只要我们删除旧Pod的容器残留在Node上的以上网络信息应该就可以恢复了。

解决:

    1)找到容器在Node侧的vethxxx接口并删除

        我们知道,veth对一侧在容器里面,一侧在Node侧。通过命令ip addr可以查看到。这里我们需要找到不存在容器遗留在Node的vethxxx接口。

K8S Node NotReady故障_第3张图片

    如上图所示,每个vethxxx接口都有一个Node侧的index ID(第一列数字),而vethxxx@if后面的数字3是该veth接口对应在容器侧的eth0网卡在容器内的index ID。容器和Node的veth接口对应关系如下:

K8S Node NotReady故障_第4张图片

    通过这样的方式找到其映射关系的。有了这个信息之后,我就可以去到该Node上所有的容器中拿到eth0对应的在Node的index ID,从而在Node侧过滤找出已经无效的vethxxx接口并删掉。操作如下:

for pid in $(for i in $(crictl ps | awk '{print $1}'); do crictl inspect $i | grep -i pid|grep , | awk '{print $2}' | sed 's/,//';done);do nsenter -t $pid --net ;done

    由于Node上现有的容器不多,十几个所以通过这种方式逐一登录拿到对应的index ID。如果容器较多,需要结合expect工具做自动识别处理。这里没有做。拿到所有有效的veth的index ID后报错到veth_yes文件中,把Node侧所有的veth信息报错到veth_all文件中:

ip add | grep veth > veth_all

    然后根据有效的index ID把其信息从veth_all中删除,得到所有要删除的vethxxx接口信息并删除:

// 删除有效的veth信息#!/bin/bashfor i in $(cat veth_yes)do grep $i veth_all; sed -i "/$i/d" veth_alldone

// 删除所有无效的vethxxx接口信息#!/bin/bashfor i in $(cat veth_all | awk '{print $2}' | awk -F@ '{print $1}')do  ip link delete $idone

    删除后,Node侧只剩下当前running容器的vethxxx接口了。 

       

    2)找到容器在Node侧记录的IP地址并删除

    veth接口处理完,那么IP地址没有释放该如何处理呢?

   查阅资料后得知,IPAM插件已分配的IP地址保存在/var/lib/cni/networks/cbr0文件中。如下:

K8S Node NotReady故障_第5张图片

    于是,将其中未真正在使用的IP地址文件删除即可。删除后如下:

    此时,所有无法分配IP的Pod都可以正常获取IP从creating状态变为Running状态了。

    至此,问题修复!

你可能感兴趣的:(kubernetes,java,docker)