转载说明:如果您喜欢这篇文章并打算转载它,请记得附上原文链接。码字不易,请文明转载,谢谢。
前言
在使用k8s过程中,时常会遇到pod出现各种异常。本文则总结了在工作过程中所遇到的pod异常情况的原因及网络上查询到的可能导致异常的因素,包含:
- pod长时间处于pending状态
- pod长时间处于unknown状态
- pod处于Waiting或ContainerCreating状态
- Pod处于ImagePullBackOff状态
- Pod 处于CrashLoopBackOff状态
- Pod一直处于Terminating状态
状态及排查
1. pending状态
当pod长时间处于pending状态,可以通过kubectl describe命令查看具体原因,一般原因有这些:
1.1 静态pod
由于静态Pod无法通过API Server直接管理,所以在Master上尝试删除这个Pod时,会使其变成Pending状态,且不会被删除。
1.2 调度异常
可能是找不到满足条件的Node节点调度,具体一般有以下几种情况:
- 当前集群没有worker节点可用,可能全是打了污点的master节点;
- 可能是因为pod设置了调度条件,而当前node不满足该条件。例如nginx调度的主机需要有“nginx=true”的标签,而所有node都没有这个标签,就无法调度;
- pod指定调度到具体某个有污点的node,但未设置容忍污点,无法调度到指定node;
- 自定义调度器异常,k8s支持多调度器,一般情况下,每个新pod都会由默认的调度器进行调度。但是如果在pod中提供了自定义的调度器名称,那么默认的调度器会忽略该pod,转由指定的调度器完成pod调度。如果自定义的调度器还未在系统中部署,则默认的调度器会忽略这个pod,这个pod将会永远处于pending状态;
- 开启了资源配额管理,但当前调度的目标节点上资源不足;
- 集群资源不足(CPU/GPU/内存等),如果是资源不足,可以通过增加资源和配置pod调度优先级保证重要的pod能“抢占式调度”;
- nodeSelector和affinity(亲和性)配置:无法找到满足affinity配置或nodeSelector配置的node节点;
1.3 kube-scheduler 未正常运行
附:如果是PVC处于pending状态——如果在系统中没有满足PVC要求的PV,PVC则会无限期处于Pending状态,直到等到系统管理员创建了一个符合其要求的PV
2. unknown状态
当pod启动时,长时间处于unknow状态,可能的原因是node可能出现了异常,比如CPU高,负载高等情况,需要排查node节点。
3. Waiting&ContainerCreating状态
如果pod长时间处于Waiting或ContainerCreating状态,一般情况原因常见于这些:
3.1 拉取镜像失败
例如:
- 镜像地址/名称/tag等错误;
- 镜像没有推送到仓库;
- 镜像仓库需要身份验证(例如harbor中该库设置为私有,没有配置或错误配置imagePullSecret);
- 镜像太大,拉取镜像超时;
- push到仓库的镜像文件被损坏;
- 服务器无法访问镜像仓库地址;
3.2 node节点磁盘空间不足
3.3 挂载 Volume 失败
3.4 依赖问题
比如configmap、pvc、secret等挂载失败
3.5 节点内存碎片化严重
如果节点上内存碎片化严重,缺少大页内存,会导致即使总的剩余内存较多,但还是会申请内存失败。解决,可先进行drop_cache操作:
echo 3 > /proc/sys/vm/drop_caches
必要时候进行内存整理,开销会比较大,会造成业务卡住一段时间(慎用):
echo 1 > /proc/sys/vm/compact_memory
3.6 limit设置太小或者单位不对
如果 limit 设置过小以至于不足以成功运行Sandbox也会造成这种状态。常见的是因为memory limit单位设置不对造成的limit过小,比如误将memory的limit单位像request一样设置为小m,这个单位在memory不适用,会被 k8s 识别成byte, 应该用Mi或M。
3.7 CNI 网络错误
如果发生 CNI 网络错误通常需要检查下网络插件的配置和运行状态,如果没有正确配置或正常运行通常表现为:无法配置 Pod 网络或者无法分配 Pod IP。
3.8 controller-manager 运行异常
3.9 升级 docker 没删干净旧版本,新旧版本不兼容导致
3.10 目标主机上已存在同名容器
4. ImagePullBackOff状态
有时候会遇到Pod长时间处于ImagePullBackOff状态,就是起不来。这个问题一般就是镜像拉取失败的问题,具体一般有(类似3.1):
4.1 镜像名称/tag/地址等错误
4.2 镜像没有推送到仓库,image不存在
4.3 镜像仓库需要身份验证
例如harbor中该库设置为私有,没有配置或错误配置imagePullSecret
4.4 镜像太大,拉取镜像超时
4.5 push到仓库的镜像文件被损坏
4.6 服务器无法访问镜像仓库地址
- 到公网拉取image,服务器无法访问公网;
- http类型registry,地址未加入到insecure-registry。例如harbor如果用http,就需要将仓库地址加入insecure-registry;
- https 自签发类型 resitry,没有给节点添加 ca 证书;
- 无法解析仓库地址。例如私有仓库域名,未经过内网解析的域名,未加入节点hosts,就无法访问;
5. CrashLoopBackOff状态
如果pod处于CrashLoopBackOff状态,说明之前是启动了,只是又异常退出了。只要Pod的restartPolicy不是Never就可能被重启拉起,且Pod的RestartCounts通常大于0。
5.1 根据容器退出的状态码排查原因
执行kubectl describe pod 命令,查看“Exit Code:”字段,排查异常pod退出时的状态码,如果状态码不是0,就是异常退出。 常见异常状态码: 137:表示程序被 SIGKILL 中断信号杀死。异常原因可能为: ==1)通常是由于Pod中容器内存达到了其资源限制(resources.limits)。例如,内存溢出(OOM)。由于资源限制是通过 Linux 的 cgroup 实现的,当某个容器内存达到资源限制, cgroup 就会将其强制停止(类似于 kill -9),此时通过 describe pod 可以看到 Reason 是 OOMKilled。 2)宿主机本身资源不够用(OOM),则内核会选择停止一些进程来释放内存。无论是 cgroup 限制,还是因为节点机器本身资源不够导致的进程停止,都可以从系统日志中找到记录。方法如下:Ubuntu 系统日志存储在目录/var/log/syslog,CentOS 系统日志存储在目录/var/log/messages 中,两者系统日志均可通过journalctl -k命令进行查看。 3)livenessProbe(存活检查)失败,使得 kubelet 停止 Pod。 4)被恶意木马进程停止。==
1和255:通常表示一般错误,具体原因需要通过容器日志进一步定位。例如,可能是设置异常退出使用 exit(1) 或 exit(-1)导致的,而-1将会根据规则转换成255。
5.2 可能的常见原因:
1)到公网拉取image,服务器无法访问公网 2)容器主动退出。如果是容器进程主动退出,退出状态码一般在 0-128 之间,除了可能是业务程序 BUG,还有其它许多可能原因。可以通过 kubectl logs -p 查看容器退出前的标准输出,如果有采集业务日志,也可以排查下业务日志。 3)系统发生OOM。如果发生系统 OOM,可以看到 Pod 中容器退出状态码是 137,表示被 SIGKILL 信号杀死,同时内核会报错: Out of memory: Kill process...。 ==大概率是节点上部署了其它非 K8S 管理的进程消耗了比较多的内存,或者 kubelet 的 --kube-reserved 和 --system-reserved 配的比较小,没有预留足够的空间给其它非容器进程,节点上所有 Pod 的实际内存占用总量不会超过 /sys/fs/cgroup/memory/kubepods 这里 cgroup 的限制,这个限制等于 capacity - "kube-reserved" - "system-reserved",如果预留空间设置合理,节点上其它非容器进程(kubelet, dockerd, kube-proxy, sshd 等) 内存占用没有超过 kubelet 配置的预留空间是不会发生系统 OOM 的,可以根据实际需求做合理的调整。== 4)检查是否发生 cgroup OOM。如果是 cgrou OOM 杀掉的进程,从Pod事件的下“Reason”字段可以看到是OOMKilled,说明容器实际占用的内存超过limit了。同时内核日志会报: Memory cgroup out of memory。解决方法:可以根据需求调整一下limit配置。
5.3 节点内存碎片化,解决方法同3.5
5.4 node节点主机发生了重启
Pod 所在node节点主机重启会导致容器重启,状态码一般为 255。
5.5 挂载了 configmap subpath
K8S对configmap subpath的支持有个问题,如果容器挂载 configmap 指定了subpath,且后来修改了configmap中的内容,当容器重启时会失败,subPath的使用方法一共有两种: 1)同一个pod中多容器挂载同一个卷时提供隔离 2)将configMap和secret作为文件挂载到容器中而不覆盖挂载目录下的文件
5.6 configmap配置文件缺失
config配置文件未挂载,报错“open conf file faild”
6. Terminating状态
删除pod时,有时候会遇到pod一直处于Terminating状态,迟迟无法结束删除。先简单看pod被删除的过程: 1)APIServer收到删除Pod的请求,该Pod被标记删除,处于Terminating状态; 2)节点上的kubelet监听到了这个状态的变更,开始销毁pod; 3)kubelet调用运行时接口,清理相关容器; 4)所有容器销毁成功,同时APIServer; 5)APIServer感知到Pod成功销毁,检查metadata是否还有finalizers,如果有就等待其它控制器清理完,如果没有就直接从etc中删除pod记录;
那么,当pod一直处于Terminating状态时,可能的原因大概有这些:
6.1 node节点异常
node节点异常具体表现的原因可能是:
- node节点被关机;
- node节点内核异常,可通过journalctl -k命令查看日志;
- 节点负载高;
- 节点网络异常;
- node主机不明原因异常。之前遇到过华x云主机出现异常,连控制台都无法登录主机,此时该主机上的pod删除时一直处于Terminating状态。过后联系该云厂商相关工程师帮忙排查,没有找到原因,最后在控制台强制重启了。
- 节点磁盘不足。当docker的数据目录所在磁盘被写满时,docker将无法正常运行,甚至无法进行删除和创建操作
6.2 检查是否存在 “i” 文件属性
如果容器镜像本身或者容器启动后写入的文件存在 “i” 文件属性,此文件将无法被修改或删除。“i” 文件属性描述可通过 man chattr 进行查看。
解决方法: 彻底解决方法:不在容器镜像中或启动后的容器设置 “i” 文件属性,彻底杜绝此问题发生。
临时恢复方法: 临时方法一: 针对 kubelet 日志报错提示的文件路径,执行 chattr -i 命令。示例如下:
chattr -i /var/lib/docker/overlay2/{containerID}/diff/usr/bin/bash
临时方法二: 等待 kubelet 自动重试,Pod 即可自动删除。
6.3 检查是否存在 Finalizers
K8S 资源的 metadata 中如果存在 finalizers,通常说明该资源是由某个程序创建的,finalizers 中也会添加一个专属于该程序的标识。例如,Rancher 创建的一些资源就会写入 finalizers 标识(namespace删除不掉,也可能是这个原因)。
若想要删除该程序所创建的资源时,则需要由创建该资源的程序进行删除前的清理,且只有清理完成并将标识从该资源的 finalizers 中移除,才可以彻底删除资源。
解决方法 使用 kubectl edit 命令手动编辑资源定义,删除 finalizers,删除资源便不会再受阻。
6.3 docker17版本的bug
可以通过kubectl delete pod ${pod name} -n ${namespace} --force --grace-period=0强制删除,但在执行 docker ps 命令后,仍然看得到该容器,建议将docker升级到18版本,不建议强制删除。
参考链接:
- 《Kubernetes权威指南》
- https://cloud.tencent.com/document/product/457/43130
- 其余多篇相关优秀文档