在K8s中,Pod作为工作负载的运行载体,是最为外围的一个资源对象。Pod具备简单的生命周期,在其生命周期的每一个阶段,可能产生多种不同的异常情况。K8s作为一个简单零碎,异样诊断往往要求弱小的常识和教训储备。联合实战经验以及EDAS用户实在场景的演绎,咱们总结了K8s Pod的13种常见异样场景,给出各个场景的常见谬误状态,剖析其起因和排查思路。
本文篇幅超过7千字,通读全文大略须要20分钟。文章内容源自大量实在场景的积淀和剖析,倡议珍藏,以供查阅。
Pod生命周期
在整个生命周期中,Pod会呈现5种阶段(Phase)。
- Pending:Pod被K8s创立进去后,起始于Pending阶段。在Pending阶段,Pod将通过调度,被调配至指标节点开始拉取镜像、加载依赖项、创立容器。
- Running:当Pod所有容器都已被创立,且至多一个容器曾经在运行中,Pod将进入Running阶段。
- Succeeded:当Pod中的所有容器都执行实现后终止,并且不会再重启,Pod将进入Succeeded阶段。
- Failed:若Pod中的所有容器都已终止,并且至多有一个容器是因为失败终止,也就是说容器以非0状态异样退出或被零碎终止,Pod将进入Failed阶段。
- Unkonwn:因为某些起因无奈获得 Pod 状态,这种状况Pod将被置为Unkonwn状态。
一般来说,对于Job类型的负载,Pod在胜利执行完工作之后将会以Succeeded状态为终态。而对于Deployment等负载,个别冀望Pod可能继续提供服务,直到Pod因删除隐没,或者因异样退出/被零碎终止而进入Failed阶段。
Pod的5个阶段是 Pod 在其生命周期中所处地位的简略宏观概述,并不是对容器或 Pod 状态的综合汇总。Pod有一些细分状态( PodConditions ),例如Ready/NotReady、Initialized、 PodScheduled/Unschedulable等。这些细分状态形容造成Pod所处阶段的具体成因是什么。比方,Pod 以后阶段是Pending,对应的细分状态是 Unschedulable,这就意味着Pod调度呈现了问题。
容器也有其生命周期状态(State):Waiting、Running和 Terminated。并且也有其对应的状态起因(Reason),例如ContainerCreating、Error、OOMKilled、CrashLoopBackOff、Completed等。而对于产生过重启或终止的容器,上一个状态(LastState)字段不仅蕴含状态起因,还蕴含上一次退出的状态码(Exit Code)。例如容器上一次退出状态码是137,状态起因是OOMKilled,阐明容器是因为OOM被零碎强行终止。在异样诊断过程中,容器的退出状态是至关重要的信息。
除了必要的集群和利用监控,个别还须要通过kubectl命令收集异样状态信息。
// 获取Pod以后对象形容文件kubectl get pod <podName> -n <namespace> -o yaml // 获取Pod信息和事件(Events)kubectl describe pod <podName> -n <namespace>// 获取Pod容器日志kubectl logs <podName> <containerName> -n <namespace>// 在容器中执行命令kubectl exec <podName> -n <namespace> -c <containerName> -- <CMD> <ARGS>
Pod异样场景
Pod在其生命周期的许多工夫点可能产生不同的异样,依照Pod容器是否运行为标志点,咱们将异样场景大抵分为两类:
- 在Pod进行调度并创立容器过程中产生异样,此时Pod将卡在Pending阶段。
- Pod容器运行中产生异样,此时Pod依照具体场景处在不同阶段。
下文将对这具体的13种场景进行形容和剖析。
调度失败
常见谬误状态:UnschedulablePod
被创立后进入调度阶段,K8s调度器根据Pod申明的资源申请量和调度规定,为Pod筛选一个适宜运行的节点。当集群节点均不满足Pod调度需要时,Pod将会处于Pending状态。造成调度失败的典型起因如下:
- 节点资源有余
K8s将节点资源(CPU、内存、磁盘等)进行数值量化,定义出节点资源容量(Capacity)和节点资源可调配额(Allocatable)。资源容量是指 Kubelet 获取的计算节点以后的资源信息,而资源可调配额是Pod可用的资源。Pod容器有两种资源额度概念:申请值Request和限度值Limit,容器至多能获取申请值大小、至少能获取限度值的资源量。Pod 的资源申请量是Pod中所有容器的资源申请之和,Pod的资源限度量是Pod中所有容器的资源限度之和。K8s默认调度器依照较小的申请值作为调度根据,保障可调度节点的资源可调配额肯定不小于Pod资源申请值。当集群没有一个节点满足Pod的资源申请量,则Pod将卡在Pending状态。
Pod因为无奈满足资源需要而被Pending,可能是因为集群资源有余,须要进行扩容,也有可能是集群碎片导致。以一个典型场景为例,用户集群有10几个4c8g的节点,整个集群资源使用率在60%左右,每个节点都有碎片,但因为碎片太小导致扩不进去一个2c4g的Pod。一般来说,小节点集群会更容易产生资源碎片,而碎片资源无奈供Pod调度应用。如果想最大限度地缩小资源节约,应用更大的节点可能会带来更好的后果。
- 超过Namespace资源配额
K8s用户能够通过资源配额(Resource Quota)对Namespace进行资源使用量限度,包含两个维度:
- 限定某个对象类型(如Pod)可创建对象的总数。
- 限定某个对象类型可耗费的资源总数。
如果在创立或更新Pod时申请的资源超过了资源配额,则Pod将调度失败。此时须要查看Namespace资源配额状态,做出适当调整。
- 不满足 NodeSelector节点选择器
Pod通过NodeSelector节点选择器指定调度到带有特定Label的节点,若不存在满足 NodeSelector的可用节点,Pod将无奈被调度,须要对NodeSelector或节点Label进行正当调整。
- 不满足亲和性
节点亲和性(Affinity)和反亲和性(Anti-Affinity)用于束缚Pod调度到哪些节点,而亲和性又细分为软亲和(Preferred)和硬亲和(Required)。对于软亲和规定,K8s调度器会尝试寻找满足对应规定的节点,如果找不到匹配的节点,调度器依然会调度该 Pod。而当硬亲和规定不被满足时,Pod将无奈被调度,须要查看Pod调度规定和指标节点状态,对调度规定或节点进行正当调整。
- 节点存在污点
K8s提供污点(Taints)和容忍(Tolerations)机制,用于防止 Pod 被调配到不适合的节点上。如果节点上存在污点,而 Pod 没有设置相应的容忍,Pod 将不会调度到该 节点。此时须要确认节点是否有携带污点的必要,如果不必要的话能够移除污点;若Pod能够调配到带有污点的节点,则能够给Pod减少污点容忍。
- 没有可用节点节点
可能会因为资源有余、网络不通、Kubelet未就绪等起因导致不可用(NotReady)。当集群中没有可调度的节点,也会导致Pod卡在Pending状态。此时须要查看节点状态,排查不可用节点问题并修复,或进行集群扩容。
镜像拉取失败
常见谬误状态:ImagePullBackOff
Pod通过调度后调配到指标节点,节点须要拉取Pod所需的镜像为创立容器做筹备。拉取镜像阶段可能存在以下几种起因导致失败:
- 镜像名字拼写错误或配置了谬误的镜像
呈现镜像拉取失败后首先要确认镜像地址是否配置谬误。
- 公有仓库的免密配置谬误
集群须要进行免密配置能力拉取公有镜像。自建镜像仓库时须要在集群创立免密凭证Secret,在Pod指定ImagePullSecrets,或者将Secret嵌入ServicAccount,让Pod应用对应的ServiceAccount。而对于acr等镜像服务云产品个别会提供免密插件,须要在集群中正确装置免密插件能力拉取仓库内的镜像。免密插件的异样包含:集群免密插件未装置、免密插件Pod异样、免密插件配置谬误,须要查看相干信息进行进一步排查。
- 网络不通
网络不通的常见场景有三个:
- 集群通过公网拜访镜像仓库,而镜像仓库未配置公网的拜访策略。对于自建仓库,可能是端口未凋谢,或是镜像服务未监听公网IP;对于acr等镜像服务云产品,须要确认开启公网的拜访入口,配置白名单等拜访控制策略。
- 集群位于专有网络,须要为镜像服务配置专有网络的访问控制,能力建设集群节点与镜像服务之间的连贯。
- 拉取海内镜像例如http://gcr.io仓库镜像,需配置镜像减速服务。
- 镜像拉取超时
常见于带宽有余或镜像体积太大,导致拉取超时。能够尝试在节点上手动拉取镜像,察看传输速率和传输工夫,必要时能够对集群带宽进行升配,或者适当调整 Kubelet 的 --image-pull-progress-deadline 和 --runtime-request-timeout 选项。
- 同时拉取多个镜像,触发并行度管制
常见于用户弹性扩容出一个节点,大量待调度Pod被同时调度下来,导致一个节点同时有大量Pod启动,同时从镜像仓库拉取多个镜像。而受限于集群带宽、镜像仓库服务稳定性、容器运行时镜像拉取并行度管制等因素,镜像拉取并不反对大量并行。这种状况能够手动打断一些镜像的拉取,依照优先级让镜像分批拉取。
依赖项谬误
常见谬误状态:Error
在 Pod 启动之前,Kubelet将尝试查看与其余 K8s 元素的所有依赖关系。次要存在的依赖项有三种:PersistentVolume、ConfigMap和Secret。当这些依赖项不存在或者无奈读取时,Pod容器将无奈失常创立,Pod会处于Pending状态直到满足依赖性。当这些依赖项能被正确读取,但呈现配置谬误时,也会呈现无奈创立容器的状况。比方将一个只读的长久化存储卷PersistentVolume以可读写的模式挂载到容器,或者将存储卷挂载到/proc等非法门路,也会导致容器创立失败。
容器创立失败
常见谬误状态:Error
Pod容器创立过程中呈现了谬误。常见起因包含:
- 违反集群的安全策略,比方违反了 PodSecurityPolicy 等。
- 容器无权操作集群内的资源,比方开启 RBAC 后,须要为 ServiceAccount 配置角色绑定。
- 短少启动命令,Pod形容文件和镜像Dockerfile中均未指定启动命令。
- 启动命令配置谬误。Pod配置文件能够通过command字段定义命令行,通过args字段给命令行定义参数。启动命令配置谬误的状况十分多见,要分外留神命令及参数的格局。正确的填写形式可参考:
初始化失败
常见谬误状态:CrashLoopBackOffK8s
提供Init Container个性,用于在启动利用容器之前启动一个或多个初始化容器,实现应用程序所需的预置条件。Init container与利用容器实质上是一样的,但它们是仅运行一次就完结的工作,并且必须在执行实现后,零碎能力继续执行下一个容器。如果 Pod 的Init Container执行失败,将会block业务容器的启动。通过查看Pod状态和事件定位到Init Container故障后,须要查看Init Container日志进一步排查故障点。
回调失败
常见谬误状态:FailedPostStartHook或FailedPreStopHook事件
K8s提供PostStart和PreStop两种容器生命周期回调,别离在容器中的过程启动前或者容器中的过程终止之前运行。PostStart 在容器创立之后立刻执行,但因为是异步执行,无奈保障和容器启动命令的执行程序相关联。PreStop 在容器终止之前被同步阻塞调用,罕用于在容器完结前优雅地开释资源。如果PostStart或者PreStop 回调程序执行失败,容器将被终止,依照重启策略决定是否重启。当呈现回调失败,会呈现FailedPostStartHook或FailedPreStopHook事件,进一步联合容器打出的日志进行故障排查。
就绪探针失败
常见谬误状态:容器曾经全副启动,然而Pod处于NotReady状态,服务流量无奈从Service达到Pod
K8s应用Readiness Probe(就绪探针)来确定容器是否曾经就绪能够承受流量。只有当Pod 中的容器都处于就绪状态时,K8s 才认定该Pod 处于就绪状态,才会将服务流量转发到该容器。个别就绪探针失败分为几种状况:
- 容器内利用起因: 健康检查所配置规定对应的端口或者脚本,无奈胜利探测,如容器内利用没失常启动等。
- 探针配置不当:写错查看端口导致探测失败;检测距离和失败阈值设置不合理,例如每次查看距离1s,一次不通过即失败;启动提早设置太短,例如利用失常启动须要15s,而设置容器启动10s后启用探针。
- 零碎层问题:节点负载高,导致容器过程hang住。
- CPU资源有余:CPU资源限度值过低,导致容器过程响应慢。
须要特地阐明的是,对于微服务利用,服务的注册和发现由注册核心治理,流量不会通过Service,间接从上游Pod流到上游Pod。然而注册核心并没有如K8s就绪探针的查看机制,对于启动较慢的JAVA利用来说,服务注册胜利后所需资源依然可能在初始化中,导致呈现上线后流量有损的状况。对于这一类场景,EDAS提供提早注册和服务预热等解决方案,解决K8s微服务利用上线有损的问题。
存活探针失败
常见谬误状态:CrashLoopBackOff
K8s应用Liveness Probe(存活探针)来确定容器是否正在运行。如果存活态探测失败,则容器会被杀死,随之依照重启策略决定是否重启。存活探针失败的起因与就绪探针相似,然而存活探针失败后容器会被kill隐没,所以排障过程要辣手得多。一个典型的用户场景是,用户在压测期间通过HPA弹性扩容出多个新Pod,然而新Pod一启动就被大流量阻塞,无奈响应存活探针,导致Pod被kill。kill后又重启,重启完又挂掉,始终在Running和CrashLoopBackOff状态中振荡。微服务场景下能够应用提早注册和服务预热等伎俩,防止刹时流量打挂容器。如果是程序自身问题导致运行阻塞,倡议先将Liveness探针移除,通过Pod启动后的监控和过程堆栈信息,找出流量涌入后过程阻塞的根因。
容器退出
常见谬误状态:CrashLoopBackOff
容器退出分为两种场景:
- 启动后立刻退出,可能起因是:
- 启动命令的门路未蕴含在环境变量PATH中。
- 启动命令援用了不存在的文件或目录。
- 启动命令执行失败,可能因为运行环境短少依赖,也可能是程序自身起因。
- 启动命令没有执行权限。
- 容器中没有前台过程。容器应该至多蕴含一个long-running的前台过程,不能后盾运行,比方通过nohup这种形式去启动过程,或是用tomcat的startup.sh脚本。
对于容器启动后立刻退出的状况,通常因为容器间接隐没,无奈获取其输入流日志,很难间接通过现场定位问题。一个繁难的排查形式是,通过设置非凡的启动命令卡住容器(比方应用tail -f /dev/null),而后进到容器中手动执行命令看看后果,确认问题起因。
- 运行一段时间后退出,这种状况个别是容器内1过程Crash或者被零碎终止导致退出。此时首先查看容器退出状态码,而后进一步查看上下文信息进行谬误定位。这种状况产生时容器曾经删除隐没,无奈进入容器中查看日志和堆栈等现场信息,所以个别举荐用户对日志、谬误记录等文件配置长久化存储,留存更多现场信息。几种常见的状态码如下:
OOMKilled
常见谬误状态:OOMKilled
K8s中有两种资源概念:可压缩资源(CPU)和不可压缩资源(内存,磁盘 )。当CPU这种可压缩资源有余时,Pod只会“饥饿”,但不会退出;而当内存和磁盘IO这种不可压缩资源有余时,Pod会被kill或者驱赶。因为内存资源有余/超限所导致的Pod异样退出的景象被称为Pod OOMKilled。K8s存在两种导致Pod OOMKilled的场景:
- Container Limit Reached,容器内存用量超限
Pod内的每一个容器都能够配置其内存资源限额,当容器理论占用的内存超额,该容器将被OOMKilled并以状态码137退出。OOMKilled往往产生在Pod曾经失常运行一段时间后,可能是因为流量减少或是长期运行累积的内存逐步减少。这种状况须要查看程序日志以理解为什么Pod应用的内存超出了预期,是否出现异常行为。如果发现程序只是依照预期运行就产生了OOM,就须要适当进步Pod内存限度值。一个很常见的谬误场景是,JAVA容器设置了内存资源限度值Limit,然而JVM堆大小限度值比内存Limit更大,导致过程在运行期间堆空间越开越大,最终因为OOM被终止。对于JAVA容器来说,个别倡议容器内存限度值Limit须要比JVM 最大堆内存稍大一些。
- Limit Overcommit,节点内存耗尽
K8s有两种资源额度概念:申请值Request和限度值Limit,默认调度器依照较小的申请值作为调度根据,保障节点的所有Pod资源申请值总和不超过节点容量,而限度值总和容许超过节点容量,这就是K8s资源设计中的Overcommit(超卖)景象。超卖设计在肯定水平上能进步吞吐量和资源利用率,但会呈现节点资源被耗尽的状况。当节点上的Pod理论应用的内存总和超过某个阈值,K8s将会终止其中的一个或多个Pod。为了尽量避免这种状况,倡议在创立Pod时抉择大小相等或相近的内存申请值和限度值,也能够利用调度规定将内存敏感型Pod打散到不同节点。
Pod驱赶
- 常见谬误状态:Pod Evicted
当节点内存、磁盘这种不可压缩资源有余时,K8s会依照QoS等级对节点上的某些Pod进行驱赶,开释资源保障节点可用性。当Pod产生驱赶后,下层控制器例如Deployment会新建Pod以维持正本数,新Pod会通过调度调配到其余节点创立运行。对于内存资源,前文曾经剖析过能够通过设置正当的申请值和限度值,防止节点内存耗尽。而对于磁盘资源,Pod在运行期间会产生临时文件、日志,所以必须对Pod磁盘容量进行限度,否则某些Pod可能很快将磁盘写满。相似限度内存、CPU 用量的形式,在创立Pod时能够对本地长期存储用量(ephemeral-storage)进行限度。同时,Kubelet驱赶条件默认磁盘可用空间在10%以下,能够调整云监控磁盘告警阈值以提前告警。
Pod失联
- 常见谬误状态:UnkonwnPod
处于Unkonwn状态,无奈获取其详细信息,个别是因为所在节点Kubelet异样,无奈向APIServer上报Pod信息。首先查看节点状态,通过Kubelet和容器运行时的日志信息定位谬误,进行修复。如果无奈及时修复节点,能够先将该节点从集群中删除。
无奈被删除
- 常见谬误状态:卡在Terminating
当一个Pod被执行删除操作后,却长时间处于Terminating状态,这种状况的起因有几种:
- Pod关联的finalizer未实现。首先查看Pod的metadata字段是否蕴含finalizer,通过一些特定上下文信息确认finalizer工作具体是什么,通常finalizer的工作未实现可能是因为与Volume相干。如果finalizer曾经无奈被实现,能够通过patch操作移除对应的Pod上的finalizer实现删除操作。
- Pod对中断信号没有响应。Pod没有被终止可能是过程对信号没有响应,能够尝试强制删除Pod。
- 节点故障。通过查看雷同节点上的其余Pod状态确认是否节点故障,尝试重启Kubelet和容器运行时。如果无奈修复,先将该节点从集群中删除。
EDAS排障工具链
EDAS对利用全生命周期的大部分异样都有积淀和剖析,升高用户学习老本,缩短排障工夫。EDAS提供一系列解决方案和工具帮忙用户解决利用生命周期中的异样问题,包含利用变更前的变更预检、利用变更和运行的事件追踪可观测、利用异样时的诊断工具。
利用变更预检
EDAS在利用变更工作下发前将通过预检环节,利用变更预检能够在利用部署前查看集群状态及变更参数是否无效,可能无效防止利用变更过程出错,升高变更危险。以后利用变更预检提供集群可用资源查看、集群健康检查、各项依赖配置查看等我的项目,对于非预期的预检后果给出剖析和处理倡议。例如对于集群资源余量不满足Pod调度需要的异样场景,变更预检后果将显示资源查看不通过,用户可能第一工夫做出针对性调整。
利用事件观测
EDAS对利用生命周期中的事件进行追踪提供可观测能力。对于利用变更过程提供残缺的事项展现,让用户可能白屏观测到变更中的每一个步骤和相干上下文信息。当出现异常变更状况时,将具体的事件和相干资源信息在白屏透出,并对异样事件进行剖析解读并给出操作倡议。例如给Pod配置了容器服务仓库镜像,但并未正确配置集群免密插件,EDAS将镜像拉取失败事件抛出,并疏导用户查看镜像拉取权限。
诊断工具箱
对于异样Pod,通常须要连贯到Pod容器,对业务过程进行诊断,必要时候还须要对异样进行复现。EDAS提供云原生工具箱,让用户在网页上连贯Pod容器Shell,并且提供Arthas、Tcpdump等工具,补救镜像软件工具包的缺失。对于Pod曾经隐没、不适宜在业务Pod进行诊断等场景,云原生工具箱提供Pod复制能力,依据诊断场景不同,用户能够按需抉择开启诊断Pod。
对于上文中提到的容器过程被大流量阻塞,导致Pod被Liveness打挂的场景,用户能够通过云原生工具箱,开启一个移除Liveness的诊断Pod,设置全链路流量管制规定,打入一些测试流量,应用Arthas提供的trace、stack、watch等工具精准定位问题。
参考文档
- https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/
- https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/
- https://kubernetes.io/docs/tasks/configure-pod-container/quality-service-pod/
- https://docs.docker.com/engine/reference/commandline/pull/#concurrent-downloads
- https://developer.aliyun.com/article/1066441https://alibaba.github.io/arthas
原文链接
本文为阿里云原创内容,未经容许不得转载。