1、问题现象及分析

环境说明

环境说明:

  • centos7.3

  • Kubernetes1.14

  • docker 1.18.9

异常信息:kubectl get pod发现服务被驱逐,然后在调度到其它节点过程中出现问题,之所以出现问题是因为编排文件中添加了污点,已经标注该Pod不能被调度到其它节点。但是为什么会出现Pod被驱逐,这倒是个问题?查看/var/log/messages中日志,发现大量镜像无法被拉取的错误,如下所示:

镜像被删除问题

Nov  7 06:20:49 k8work2 kubelet: E1107 06:20:49.829886   13241 remote_image.go:113] PullImage "k8s.gcr.io/kube-proxy:v1.14.2" from image service failed: rpc error: code = Unknown desc = Error response from daemon: Get https://k8s.gcr.io/v2/: dial tcp 74.125.204.82:443: connect: connection timed out
Nov  7 06:20:49 k8work2 kubelet: E1107 06:20:49.866132   13241 pod_workers.go:190] Error syncing pod 4fedf7b3-207e-11eb-90a3-2c534a095a16 ("kube-proxy-pqvvb_kube-system(4fedf7b3-207e-11eb-90a3-2c534a095a16)"), skipping: failed to "StartContainer" for "kube-proxy" with ErrImagePull: "rpc error: code = Unknown desc = Error response from daemon: Get https://k8s.gcr.io/v2/: dial tcp 74.125.204.82:443: connect: connection timed out"

这段日志的意思是因为镜像无法拉取,所以启动出现启动失败问题,除此之外还有coredns、Controller等,也出现此类镜像无法拉取问题。

出现这个问题,很容易理解,内网集群,在集群安装过程中,镜像是通过复制过来的,但是执行docker images|grep k8s发现k8s的镜像全不在了,难道有人为删除,要不然镜像为什么会无缘无故消失呢?

于是又开始查看日志,又发现日志中存在此类信息,确认不是人为删除,而是kubelet回收掉了,具体日志如下所示:

Nov  7 05:44:51 k8work2 kubelet: I1107 05:44:51.041315   13241 image_gc_manager.go:317] attempting to delete unused images
Nov  7 05:44:51 k8work2 kubelet: I1107 05:44:51.083785   13241 image_gc_manager.go:371] [imageGCManager]: Removing image "sha256:6858809bf669cc5da7cb6af83d0fae838284d12e1be0182f92f6bd96559873e3" to free 1231725 bytes

为什么要把k8s自身运行需要的镜像回收掉呢?这里先不过多解释,具体原因,且看下文。

找不到manifests问题

Nov  7 06:20:47 k8work2 kubelet: E1107 06:20:47.943073   13241 file_linux.go:61] Unable to read config path "/etc/kubernetes/manifests": path does not exist, ignoring

其实看了下网上这个问题,也挺多的,因为是计算节点,不包含manifests,但是日志中一直在提示这个错误,这种噪音日志看着就难受,我是直接在/etc/kubernetes/创建了manifests文件夹,问题直接解决。此错误跟本文中的Pod驱逐应该没什么关系,看了看其它计算接单存在同样问题。

孤儿Volume问题

Nov  7 09:32:03 k8work2 kubelet: E1107 09:32:03.431224   13241 kubelet_volumes.go:154] Orphaned pod "f6a977f4-2098-11eb-90a3-2c534a095a16" found, but volume paths are still present on disk : There were a total of 1 errors similar to this. Turn up verbosity to see them.

进入到/var/lib/kubelet/pods/,通过id号,进入kubelet的目录,可以发现里面还存在容器的数据,etc-hosts文件中还保留着pod名称等信息。

从错误信息可以推测,这台计算节点存在一个孤儿Pod,并且该Pod挂载了数据卷(volume),阻碍了Kubelet对孤儿Pod正常的回收清理。所以一直在提示上面错误信息,我在确认该Pod确认该Pod确实已经不在运行,并且没有数据丢失的风险,直接执行了rm -rf f6a977f4-2098-11eb-90a3-2c534a095a16,删除过后,不在刷此类错误。

驱逐问题

Nov  7 07:21:19 k8work2 kubelet: E1107 07:21:19.021705   13241 eviction_manager.go:576] eviction manager: pod es-0_log(aa41dd4c-2085-11eb-90a3-2c534a095a16) failed to evict timeout waiting to kill pod
Nov  7 07:21:22 k8work2 kubelet: I1107 07:21:22.883681   13241 image_gc_manager.go:300] [imageGCManager]: Disk usage on image filesystem is at 86% which is over the high threshold (85%). Trying to free 21849563955 bytes down to the low threshold (80%).
Nov  7 07:21:22 k8work2 kubelet: E1107 07:21:22.890923   13241 kubelet.go:1278] Image garbage collection failed multiple times in a row: failed to garbage collect required amount of images. Wanted to free 21849563955 bytes, but freed 0 bytes

日志大概提示意思是磁盘压力过大,已经超过阈值,于是df -h查看了下磁盘,果然这台机器服务产生了大量日志,导致磁盘占用过高,但是磁盘虽然占用过高,为什么要回收镜像呢?在官网查询了下,大概是这样介绍的:

垃圾回收是kubelet的一个有用功能,它将清理未使用的镜像和容器。kubelet将每分钟对容器执行一次垃圾回收,每五分钟对镜像执行一次垃圾回收。

镜像垃圾回收策略只考虑两个因素:HighThresholdPercentLowThresholdPercent。磁盘使用率超过上限阈值(HighThresholdPercent)将触发垃圾回收。垃圾回收将删除最近最少使用的镜像,直到磁盘使用率满足下限阈值(LowThresholdPercent)。

容器垃圾回收策略考虑三个用户定义变量。MinAge是容器可以被执行垃圾回收的最小生命周期。MaxPerPodContainer是每个pod内允许存在的死亡容器的最大数量。MaxContainers是全部死亡容器的最大数量。可以分别独立地通过将MinAge设置为0,以及将MaxPerPodContainerMaxContainers设置为小于0来禁用这些变量。kubelet 将处理无法辨识的、已删除的以及超出前面提到的参数所设置范围的容器。最老的容器通常会先被移除。

对于k8s用户来说上述kubelet参数都是可以调整的,具体调整方式请参考:https://kubernetes.io/zh/docs/concepts/cluster-administration/kubelet-garbage-collection/这里不在过多赘述。

说到这里大概已经找到原因,之所以出现Pod被驱逐,原因是因为磁盘压力超过阈值,在k8s看来,这个计算节点已经不正常,所以开启垃圾回收机制,按照默认回收策略首先删除了自身的镜像信息,然后导致内网镜像拉取失败问题,然后开始重新调度Pod,但是因为该Pod中添加了污点,不能被调度到其它节点,最后导致启动失败。

2、问题解决

解决过程

于是找到占用磁盘数据所在文件夹,是一个以pod PVC命名的文件,确认里面的数据可以删除之后,我直接把PVC文件夹以及内容,全部都给删掉了,再次启动Pod,一直处于init状态,event事件提示无法找到PVC,仔细看了下该Pod所在编排文件内容,发现该Pod是有状态应用,以sts进行编排,我们知道sts以特定顺序启动,并且拥有稳定网络身份标识、写入固定的存储,现在我把存储名称都给干掉了,所以导致无法启动,大家引以为戒。

但是之所以会出现上面有状态Pod无法启动的问题,究其原因是因为复用了过去的PVC,我只要把PVC、PV删除了,重新创建,一切万事大吉,于是我开始使用kubectl delete pvc pvc_name -n log,有趣的一幕又发生了,PVC一直卡在Terminating无法删除。

网上查了查解决方式,大概两种:

  • 直接到etcd中删除

  • 使用kubectl patch

因为本地没有安装etcdctl,所以直接使用了kubectl patch解决问题,解决命令如下所示:

kubectl delete pvc pvc_name -n log
kubectl patch pvc pvc_name  -p '{"metadata":{"finalizers":null}}' -n log

之所以需要强制执行,因为原生k8s不允许执行删除后的回滚动作。这大概就是k8s最终一致性原则的体现。

再次重新启动Pod,启动成功,已经重新创建PVC和PV。

3、总结

通过本文可以看出两点:

  • 当k8s集群出现问题时一定要仔细查看日志,先看k8s本身事件信息,如果不能找到线索,紧接着查看内核日志,出现问题之后,正常情况下一定能够找到问题日志。

  • 不要盲目删除数据,一定搞明白东西的原理之后再做删除操作,否则会带来不必要的麻烦。

当然要彻底解决此类问题,还是需要监控巡逻系统,出现磁盘告警之后能够立马通知到系统管理员或者自动做出进一步数据处理,不至于服务挂掉。如有问题,请关注公众号,加我微信,一起讨论!

推荐

压测nginx出现no live upstreams while connecting to upstream的问题分析

并非每个容器内部都能包含一个操作系统

原创不易,随手关注或者”在看“,诚挚感谢!

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐