赚钱的十大个人网站,免费的网页模版,seosem顾问,网站备案需要多久时间在K8s中#xff0c;Pod作为工作负载的运行载体#xff0c;是最为核心的一个资源对象。Pod具有复杂的生命周期#xff0c;在其生命周期的每一个阶段#xff0c;可能发生多种不同的异常情况。K8s作为一个复杂系统#xff0c;异常诊断往往要求强大的知识和经验储备。结合实战…在K8s中Pod作为工作负载的运行载体是最为核心的一个资源对象。Pod具有复杂的生命周期在其生命周期的每一个阶段可能发生多种不同的异常情况。K8s作为一个复杂系统异常诊断往往要求强大的知识和经验储备。结合实战经历以及EDAS用户真实场景的归纳我们总结了K8s Pod的13种常见异常场景给出各个场景的常见错误状态分析其原因和排查思路。
本文篇幅超过7千字通读全文大概需要20分钟。文章内容源自大量真实场景的沉淀和分析建议收藏以供查阅。Pod生命周期
在整个生命周期中Pod会出现5种阶段Phase。
PendingPod被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调度出现了问题。
容器也有其生命周期状态StateWaiting、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进行资源使用量限制包括两个维度
1. 限定某个对象类型如Pod可创建对象的总数。
2. 限定某个对象类型可消耗的资源总数。
如果在创建或更新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状态。此时需要查看节点状态排查不可用节点问题并修复或进行集群扩容。
镜像拉取失败
常见错误状态ImagePullBackOffPod经过调度后分配到目标节点节点需要拉取Pod所需的镜像为创建容器做准备。拉取镜像阶段可能存在以下几种原因导致失败
镜像名字拼写错误或配置了错误的镜像
出现镜像拉取失败后首先要确认镜像地址是否配置错误。
私有仓库的免密配置错误
集群需要进行免密配置才能拉取私有镜像。自建镜像仓库时需要在集群创建免密凭证Secret在Pod指定ImagePullSecrets或者将Secret嵌入ServicAccount让Pod使用对应的ServiceAccount。而对于acr等镜像服务云产品一般会提供免密插件需要在集群中正确安装免密插件才能拉取仓库内的镜像。免密插件的异常包括集群免密插件未安装、免密插件Pod异常、免密插件配置错误需要查看相关信息进行进一步排查。
网络不通
网络不通的常见场景有三个
1. 集群通过公网访问镜像仓库而镜像仓库未配置公网的访问策略。对于自建仓库可能是端口未开放或是镜像服务未监听公网IP对于acr等镜像服务云产品需要确认开启公网的访问入口配置白名单等访问控制策略。
2. 集群位于专有网络需要为镜像服务配置专有网络的访问控制才能建立集群节点与镜像服务之间的连接。
3. 拉取海外镜像例如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等非法路径也会导致容器创建失败。
容器创建失败
常见错误状态ErrorPod容器创建过程中出现了错误。常见原因包括
违反集群的安全策略比如违反了 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达到PodK8s使用Readiness Probe就绪探针来确定容器是否已经就绪可以接受流量。只有当Pod 中的容器都处于就绪状态时K8s 才认定该Pod 处于就绪状态才会将服务流量转发到该容器。一般就绪探针失败分为几种情况
容器内应用原因 健康检查所配置规则对应的端口或者脚本无法成功探测如容器内应用没正常启动等。探针配置不当写错检查端口导致探测失败检测间隔和失败阈值设置不合理例如每次检查间隔1s一次不通过即失败启动延迟设置太短例如应用正常启动需要15s而设置容器启动10s后启用探针。系统层问题节点负载高导致容器进程hang住。CPU资源不足CPU资源限制值过低导致容器进程响应慢。
需要特别说明的是对于微服务应用服务的注册和发现由注册中心管理流量不会经过Service直接从上游Pod流到下游Pod。然而注册中心并没有如K8s就绪探针的检查机制对于启动较慢的JAVA应用来说服务注册成功后所需资源仍然可能在初始化中导致出现上线后流量有损的情况。对于这一类场景EDAS提供延迟注册和服务预热等解决方案解决K8s微服务应用上线有损的问题。
存活探针失败
常见错误状态CrashLoopBackOffK8s使用Liveness Probe存活探针来确定容器是否正在运行。如果存活态探测失败则容器会被杀死随之按照重启策略决定是否重启。存活探针失败的原因与就绪探针类似然而存活探针失败后容器会被kill消失所以排障过程要棘手得多。一个典型的用户场景是用户在压测期间通过HPA弹性扩容出多个新Pod然而新Pod一启动就被大流量阻塞无法响应存活探针导致Pod被kill。kill后又重启重启完又挂掉一直在Running和CrashLoopBackOff状态中振荡。微服务场景下可以使用延迟注册和服务预热等手段避免瞬时流量打挂容器。如果是程序本身问题导致运行阻塞建议先将Liveness探针移除通过Pod启动后的监控和进程堆栈信息找出流量涌入后进程阻塞的根因。
容器退出
常见错误状态CrashLoopBackOff容器退出分为两种场景
启动后立即退出可能原因是
1. 启动命令的路径未包含在环境变量PATH中。
2. 启动命令引用了不存在的文件或目录。
3. 启动命令执行失败可能因为运行环境缺少依赖也可能是程序本身原因。
4. 启动命令没有执行权限。
5. 容器中没有前台进程。容器应该至少包含一个long-running的前台进程不能后台运行比如通过nohup这种方式去启动进程或是用tomcat的startup.sh脚本。
对于容器启动后立即退出的情况通常因为容器直接消失无法获取其输出流日志很难直接通过现场定位问题。一个简易的排查方式是通过设置特殊的启动命令卡住容器比如使用tail -f /dev/null然后进到容器中手动执行命令看看结果确认问题原因。
运行一段时间后退出这种情况一般是容器内1进程Crash或者被系统终止导致退出。此时首先查看容器退出状态码然后进一步查看上下文信息进行错误定位。这种情况发生时容器已经删除消失无法进入容器中查看日志和堆栈等现场信息所以一般推荐用户对日志、错误记录等文件配置持久化存储留存更多现场信息。几种常见的状态码如下
状态码含义分析0正常退出容器的启动程序不是一个long-running的程序。如果正常退出不符合预期需要检查容器日志对程序的执行逻辑进行调整。137外部终止137 表示容器已收到来自主机操作系统的 SIGKILL 信号。该信号指示进程立即终止没有宽限期。可能原因包含容器运行时将容器kill例如docker kill命令Linux 用户向进程发送 kill -9 命令触发K8s尝试终止容器超过优雅下线窗口期后直接kill容器由节点系统触发比如遭遇了OOM。139段错误139表示容器收到了来自操作系统的 SIGSEGV 信号。这表示分段错误 —— 内存违规由容器试图访问它无权访问的内存位置引起。143优雅终止143 表示容器收到来自操作系统的 SIGTERM 信号该信号要求容器正常终止。该退出码可能的原因是容器引擎停止容器例如使用 docker stop 停止了容器K8s终止了容器比如缩容行为将Pod删除。
OOMKilled
常见错误状态OOMKilledK8s中有两种资源概念可压缩资源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-downloadshttps://developer.aliyun.com/article/1066441https://alibaba.github.io/arthas
原文链接
本文为阿里云原创内容未经允许不得转载。