报障:
今日上午,值班同学发现airflow无法使用。查看时其部署的Node节点NotReady了。
分析:
马上查看K8S集群节点的状态,发现这个节点已经是NotReady状态了。第一反应就是ping下节点看是否宕机了?ping正常,于是登录到该节点查看kubelet状态。发现kubelet报runtime不可用,查看containerd的状态,一直在不断的重启,而且启动不成功。为了尽快恢复业务,决定先将containerd的数据目录清空后重新拉起。于是删除containerd数据目录下的文件夹:
# ls -lrth /xpu-k8s-data/containerd/
total 0
drwx------ 2 root root 6 Apr 28 10:54 io.containerd.snapshotter.v1.btrfs
drwx------ 3 root root 31 Apr 28 10:54 io.containerd.snapshotter.v1.aufs
drwx------ 3 root root 31 Apr 28 10:54 io.containerd.snapshotter.v1.native
drwx--x--x 2 root root 29 Apr 28 10:54 io.containerd.metadata.v1.bolt
drwx--x--x 2 root root 6 Apr 28 10:54 io.containerd.runtime.v1.linux
drwxr-xr-x 4 root root 45 Apr 28 10:54 io.containerd.content.v1.content
drwx------ 3 root root 54 Apr 28 10:54 io.containerd.snapshotter.v1.overlayfs
drwx--x--x 3 root root 28 Apr 28 10:54 io.containerd.runtime.v2.task
drwxr-xr-x 4 root root 53 Apr 28 10:55 io.containerd.grpc.v1.cri
drwx------ 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的报错:
猜测是因为小文件数量过多,导致节点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 flannel
root 6450 45116 0 14:59 pts/4 00:00:00 grep --color=auto flannel
root 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接口。
如上图所示,每个vethxxx接口都有一个Node侧的index ID(第一列数字),而vethxxx@if后面的数字3是该veth接口对应在容器侧的eth0网卡在容器内的index ID。容器和Node的veth接口对应关系如下:
通过这样的方式找到其映射关系的。有了这个信息之后,我就可以去到该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/bash
for i in $(cat veth_yes)
do
grep $i veth_all; sed -i "/$i/d" veth_all
done
// 删除所有无效的vethxxx接口信息
#!/bin/bash
for i in $(cat veth_all | awk '{print $2}' | awk -F@ '{print $1}')
do
ip link delete $i
done
删除后,Node侧只剩下当前running容器的vethxxx接口了。
2)找到容器在Node侧记录的IP地址并删除
veth接口处理完,那么IP地址没有释放该如何处理呢?
查阅资料后得知,IPAM插件已分配的IP地址保存在/var/lib/cni/networks/cbr0文件中。如下:
于是,将其中未真正在使用的IP地址文件删除即可。删除后如下:
此时,所有无法分配IP的Pod都可以正常获取IP从creating状态变为Running状态了。
至此,问题修复!略略略略略~~~