作者:风敬
本文篇幅超越 7 千字,通读全文大约需求 20 分钟。文章内容源自很多实在场景的沉积和剖析,主张保藏,以供查阅。
在 K8s 中,Pod 作为工作负载的运转载体,是最为核心的一个资源方针。Pod 具有复杂的生命周期,在其生命周期的每一个阶段,或许发生多种不同的反常状况。K8s 作为一个复杂体系,反常确诊往往要求强大的知识和经历储备。结合实战经历以及 EDAS 用户实在场景的归纳,咱们总结了 K8s Pod 的 13 种常见反常场景,给出各个场景的常见过错状况,剖析其原因和排查思路。
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 po <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 种场景进行描绘和剖析。
调度失利
常见过错状况:Unschedulable
Pod 被创立后进入调度阶段,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 等镜像服务云产品,需求承认敞开公网的拜访入口,装备白名单等拜访操控战略。
-
集群位于专有网络,需求为镜像服务装备专有网络的拜访操控,才干建立集群节点与镜像服务之间的衔接。
-
拉取海外镜像例如 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 字段给指令行界说参数。发动指令装备过错的状况非常多见,要格外留意指令及参数的格式。正确的填写方法可参阅:
初始化失利
常见过错状况:CrashLoopBackOff
K8s 供给 Init Container 特性,用于在发动运用容器之前发动一个或多个初始化容器,完成运用程序所需的预置条件。Init container 与运用容器本质上是相同的,但它们是仅运转一次就完毕的使命,而且有必要在履行完成后,体系才干继续履行下一个容器。假如 Pod 的 Init Container 履行失利,将会 block 事务容器的发动。通过查看 Pod 状况和事情定位到 Init Container 毛病后,需求查看 Init Container 日志进一步排查毛病点。
回调失利
常见过错状况:FailedPostStartHook 或 FailedPreStopHook 事情
K8s 供给 PostStart 和 PreStop 两种容器生命周期回调,分别在容器中的进程发动前或许容器中的进程停止之前运转。PostStart 在容器创立之后当即履行,但由于是异步履行,无法确保和容器发动指令的履行次序相关联。假如 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 失联
常见过错状况:Unkonwn
Pod 处于 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 等东西精准定位问题
参阅文档
- kubernetes.io/docs/concep…
- Managing Compute Resources for Containers
- Configure Quality of Service for Pods – Kubernetes
- docs.docker.com/engine/refe…
- developer.aliyun.com/article/106…
- alibaba.github.io/arthas