侧边栏壁纸
博主头像
汪洋

即使慢,驰而不息,纵会落后,纵会失败,但一定可以达到他所向的目标。 - 鲁迅

  • 累计撰写 190 篇文章
  • 累计创建 74 个标签
  • 累计收到 108 条评论

故障处理 - K8S Node NotReady 故障,Containerd 不断重启

汪洋
2023-05-05 / 0 评论 / 6 点赞 / 845 阅读 / 2,834 字

报障

今日上午,值班同学发现 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 的报错:
image.png

image.png

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

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

image.png

该节点的 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 上的网络信息主要有两点:

  • 容器在 Node 侧的 vethxxx 接口;
  • 容器在 Node 侧的 IP 地址;

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

解决

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

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

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

通过这样的方式找到其映射关系的。有了这个信息之后,我就可以去到该 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 文件中。如下:

image.png

于是,将其中未真正在使用的IP地址文件删除即可。此时,所有无法分配 IP 的 Pod 都可以正常获取 IP 从 creating 状态变为 Running 状态了。

0

评论区