k8s诊断之记一次业务pod被异常删除的分析

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 某用户反馈,头一天晚上21:05左右,某几个deployment的pod被重建了一遍,客户的pod有特殊限制,基本可以保证1个节点上就只有这一个pod独占,客户怀疑后端异常导致

背景信息:

   某用户反馈,头一天晚上21:05左右,某几个deployment的pod被重建了一遍,客户的pod有特殊限制,基本可以保证1个节点上就只有这一个pod独占,客户怀疑后端异常导致,这种问题可以先去看下元集群的信息,看看各种controller 的pod 在异常时间段有没有崩溃,重启,重建等信息,这个集群没有这个问题



排查思路&日志分析:

1,deployment下的所有pod都重建了,当时优先怀疑deployment的变更,但是根据rs的版本号对比,确认前后pod的rs版本号未变

2,有什么job cronjob删除pod,deployment controller拉起新的pod,根据日志排除掉

3,之前遇到过HPA拉pod,然后缩容时删除旧pod只保留新pod,根据hpa日志排除

HPA 相关日志:实际没有扩容的动作,日志解读就是预计需要39个副本,根据cpu的得分来看,但是最后决定不扩容

content:I110713:05:31.5818611horizontal.go:1156]Successfullyupdatedstatusfordeploy-d****ercontent:I110713:05:46.6098491horizontal.go:661]proposing39desiredreplicas(basedoncpuresourceutilization(percentageofrequest)from2022-11-0713:05:20+0000UTC)forDeployment/default/depl****rcontent:I110713:05:46.6098711horizontal.go:700]decidednottoscaleDeployment/default/ddepl****rto39(lastscaletimewas2022-11-0711:16:27+0000UTC)

4,以被删除的pod depl****rr-7b656b54b7-stwj8 为例(老pod 被抢占删除),看时间线

schedulerpodcontent:I110713:05:47.7342711capacity_scheduling.go:662]Poddefault/depl****rr-7b656b54b7-stwj8isapotentialpreemptionvictimonnodeeu-central-1.10.200.104.186.apiservercontent:I110721:05:47.8198771httplog.go:108]"HTTP"verb="DELETE"URI="/api/v1/namespaces/default/pods/depl****r-7b656b54b7-stwj8"latency="7.986746ms"userAgent="kube-scheduler/v1.20.4 (linux/amd64) kubernetes/96c418b/scheduler"srcIP="7.8.121.36:45840"resp=200contentType="application/vnd.kubernetes.protobuf"requestID="49832f6f-aa13-412a-92b4-a5e7e62aa976"stageTimestamp:2022-11-07T13:05:47.819744Zuser:{"username":"kubernetes-admin","groups":["system:masters","system:authenticated"]}userAgent:kube-scheduler/v1.20.4(linux/amd64)kubernetes/96c418b/schedulerverb:delete

我们用日志串联看出来的都是抢占先发生,删除后发生的

5,通过节点绑定这个特性反查当时抢占该pod的新pod名称depl****rr-7b656b54b7-zjmn8 为检索关键字,捋时间线

scheduler:content:I110713:05:45.0684921eventhandlers.go:172]addeventforunscheduledpoddefault/depl****r-7b656b54b7-zjmn8apiserver:content:I110721:06:03.7567671httplog.go:108]"HTTP"verb="PATCH"URI="/api/v1/namespaces/default/pods/depl****rr-7b656b54b7-zjmn8/status"latency="5.035966ms"userAgent="kube-scheduler/v1.20.4 (linux/amd64) kubernetes/96c418b/scheduler"srcIP="7.8.121.36:45840"resp=200contentType="application/vnd.kubernetes.protobuf"requestID="a0698fb2-d843-4158-b7ff-dbcc46e5f961"KCMcontent:I110713:05:45.0683931graph_builder.go:632]GraphBuilderprocessobject:v1/Pod, namespacedefault, namedepl****r-7b656b54b7-zjmn8, uid86773988-20ec-4b44-a62e-97c3cd3db054, eventtypeadd, virtual=falsestageTimestamp:2022-11-07T13:06:03.756637Zuser:{"username":"kubernetes-admin","groups":["system:masters","system:authenticated"]}userAgent:kube-scheduler/v1.20.4(linux/amd64)kubernetes/96c418b/schedulerverb:patch

 看到了新pod的不可调度事件,但是并不能说明该pod抢占了老pod

6,继续寻找pod被抢占的原因,这个时候要考虑抢占的上下文日志,sls有个上下文浏览可以看下,这里涉及一个知识点,调度器在尝试驱逐的时候是串行执行的,所以在驱逐pod A的时候,发起驱逐的pod就是这条日志超上翻的第一条Attempting to schedule pod的pod,并且在条日志后面也有postfiler的结果,返回值是0 [] nil表明抢占是成功的,2则是失败

logtail的pod尝试调度

content:I110713:05:20.7024661eventhandlers.go:172]addeventforunscheduledpodkube-system/logtail-ds-xhzgmcontent:I110713:05:47.7287971scheduling_queue.go:812]Abouttotryandschedulepodkube-system/logtail-ds-xhzgmcontent:I110713:05:47.7288091scheduler.go:460]Attemptingtoschedulepod:kube-system/logtail-ds-xhzgm

这个节点发生了潜在的抢占,scheduler日志要看上下文,所以这个时间点只有一个pod在等待调度,且发送抢占的只有1个节点,因此判定这个logtail的pod要调度的就是186这个机器

content:I110713:05:47.7341061capacity_scheduling.go:433]1potentialnodesforpreemption, first1are:[eu-central-1.10.*.*.86]

老pod被抢占的记录日志

content:I110713:05:47.7342711capacity_scheduling.go:662]Poddefault/depl****r-7b656b54b7-stwj8isapotentialpreemptionvictimonnodeeu-central-1.10.*.*.86.

然后logtail的日志记录到了抢占成功

content:I110713:05:47.8207621scheduler.go:484]StatusafterrunningPostFilterpluginsforpodkube-system/logtail-ds-xhzgm:&{0[]}

   这里的0是成功 2是失败如

     

抢占成功后实际也没调度成功,因为内存不够

content:I110713:05:47.8214001factory.go:322]"Unable to schedule pod; no fit; waiting"pod="kube-system/logtail-ds-xhzgm"err="0/2273 nodes are available: 1 Insufficient memory, 2272 node(s) didn't match Pod's node affinity."

 

     (老pod还没杀掉,新pod又被deployment controller尝试拉起,客户将内存资源占的太满了)

最终logtail的pod调度失败了,被删除的pod也被controller重新拉起了一个新的pod起来填补

一句话小结:

不要将节点的内存超卖太多或者占到几乎全部,大部分的DS类型的pod 优先级比较高,超卖率较高时容易发生抢占,设置合理的QOS类型,重要业务可以使用limit == request的QOS类型来保障不被抢占

Scheduler的扩展知识:

Scheduler的简介:

kube-scheduler是Kubernetes中的关键模块,扮演管家的角色遵从一套机制——为Pod提供调度服务,例如基于资源的公平调度、调度Pod到指定节点、或者通信频繁的Pod调度到同一节点等。容器调度本身是一件比较复杂的事,因为要确保以下几个目标:

  • 公平性:在调度Pod时需要公平的进行决策,每个节点都有被分配资源的机会,调度器需要对不同节点的使用作出平衡决策。
  • 资源高效利用:最大化群集所有资源的利用率,使有限的CPU、内存等资源服务尽可能更多的Pod。
  • 效率问题:能快速的完成对大批量Pod的调度工作,在集群规模扩增的情况下,依然保证调度过程的性能。
  • 灵活性:在实际运作中,用户往往希望Pod的调度策略是可控的,从而处理大量复杂的实际问题。因此平台要允许多个调度器并行工作,同时支持自定义调度器。

为达到上述目标,kube-scheduler通过结合Node资源、负载情况、数据位置等各种因素进行调度判断,确保在满足场景需求的同时将Pod分配到最优节点。显然,kube-scheduler影响着Kubernetes集群的可用性与性能,Pod数量越多集群的调度能力越重要,尤其达到了数千级节点数时,优秀的调度能力将显著提升容器平台性能。

Scheduler controller的组成:

下图是kube-scheduler的主要组件:

1. Policy

Scheduler 的调度策略启动配置目前支持三种方式,配置文件 / 命令行参数 / ConfigMap。调度策略可以配置指定调度主流程中要用哪些过滤器 (Predicates)、打分器 (Priorities) 、外部扩展的调度器 (Extenders),以及最新支持的 SchedulerFramwork 的自定义扩展点 (Plugins)。

2. Informer

Scheduler 在启动的时候通过 K8s 的 informer 机制以 List+Watch 从 kube-apiserver 获取调度需要的数据例如:Pods、Nodes、Persistant Volume(PV), Persistant Volume Claim(PVC) 等等,并将这些数据做一定的预处理作为调度器的的 Cache。

3.调度流水线

通过 Informer 将需要调度的 Pod 插入 Queue 中,Pipeline 会循环从 Queue Pop 等待调度的 Pod 放入 Pipeline 执行。

调度流水线 (Schedule Pipeline) 主要有三个阶段:Scheduler Thread,Wait Thread,Bind Thread。

Scheduler Thread 阶段: 从如上的架构图可以看到 Schduler Thread 会经历 Pre Filter -> Filter -> Post Filter-> Score -> Reserve,可以简单理解为 Filter -> Score -> Reserve。

Filter 阶段用于选择符合 Pod Spec 描述的 Nodes;Score 阶段用于从 Filter 过后的 Nodes 进行打分和排序;Reserve 阶段将 Pod 跟排序后的最优 Node 的 NodeCache 中,表示这个 Pod 已经分配到这个 Node 上, 让下一个等待调度的 Pod 对这个 Node 进行 Filter 和 Score 的时候能看到刚才分配的 Pod。

Wait Thread 阶段:这个阶段可以用来等待 Pod 关联的资源的 Ready 等待,例如等待 PVC 的 PV 创建成功,或者 Gang 调度中等待关联的 Pod 调度成功等等;

Bind Thread 阶段:用于将 Pod 和 Node 的关联持久化 Kube APIServer。

整个调度流水线只有在 Scheduler Thread 阶段是串行的一个 Pod 一个 Pod 的进行调度,在 Wait 和 Bind 阶段 Pod 都是异步并行执行。


调度框架的扩展点:

下图显示了一个 Pod 的调度上下文以及调度框架公开的扩展点。 在此图片中,“过滤器”等同于“断言”,“评分”相当于“优先级函数”。

一个插件可以在多个扩展点处注册,以执行更复杂或有状态的任务。

队列排序

这些插件用于对调度队列中的 Pod 进行排序。 队列排序插件本质上提供less(Pod1, Pod2)函数。 一次只能启动一个队列插件。

PreFilter

这些插件用于预处理 Pod 的相关信息,或者检查集群或 Pod 必须满足的某些条件。 如果 PreFilter 插件返回错误,则调度周期将终止。

Filter

这些插件用于过滤出不能运行该 Pod 的节点。对于每个节点, 调度器将按照其配置顺序调用这些过滤插件。如果任何过滤插件将节点标记为不可行, 则不会为该节点调用剩下的过滤插件。节点可以被同时进行评估。

PostFilter

这些插件在 Filter 阶段后调用,但仅在该 Pod 没有可行的节点时调用。 插件按其配置的顺序调用。如果任何 PostFilter 插件标记节点为“Schedulable”, 则其余的插件不会调用。典型的 PostFilter 实现是抢占,试图通过抢占其他 Pod 的资源使该 Pod 可以调度。

PreScore

这些插件用于执行 “前置评分(pre-scoring)” 工作,即生成一个可共享状态供 Score 插件使用。 如果 PreScore 插件返回错误,则调度周期将终止。

Score

这些插件用于对通过过滤阶段的节点进行排序。调度器将为每个节点调用每个评分插件。 将有一个定义明确的整数范围,代表最小和最大分数。 在标准化评分阶段之后,调度器将根据配置的插件权重 合并所有插件的节点分数

NormalizeScore

这些插件用于在调度器计算 Node 排名之前修改分数。 在此扩展点注册的插件被调用时会使用同一插件的 Score 结果。 每个插件在每个调度周期调用一次。

Reserve

Reserve 是一个信息性的扩展点。 管理运行时状态的插件(也成为“有状态插件”)应该使用此扩展点,以便 调度器在节点给指定 Pod 预留了资源时能够通知该插件。 这是在调度器真正将 Pod 绑定到节点之前发生的,并且它存在是为了防止 在调度器等待绑定成功时发生竞争情况。

这个是调度周期的最后一步。 一旦 Pod 处于保留状态,它将在绑定周期结束时触发Unreserve插件 (失败时)或PostBind插件(成功时)。

Permit

Permit插件在每个 Pod 调度周期的最后调用,用于防止或延迟 Pod 的绑定。 一个允许插件可以做以下三件事之一:

  1. 批准
    一旦所有 Permit 插件批准 Pod 后,该 Pod 将被发送以进行绑定。
  2. 拒绝
    如果任何 Permit 插件拒绝 Pod,则该 Pod 将被返回到调度队列。 这将触发
    Unreserve插件。
  3. 等待(带有超时)
    如果一个 Permit 插件返回 “等待” 结果,则 Pod 将保持在一个内部的 “等待中” 的 Pod 列表,同时该 Pod 的绑定周期启动时即直接阻塞直到得到
    批准。如果超时发生,等待变成拒绝,并且 Pod 将返回调度队列,从而触发Unreserve插件。

说明:尽管任何插件可以访问 “等待中” 状态的 Pod 列表并批准它们 (查看FrameworkHandle)。 我们期望只有允许插件可以批准处于 “等待中” 状态的预留 Pod 的绑定。 一旦 Pod 被批准了,它将发送到PreBind阶段。

PreBind

这些插件用于执行 Pod 绑定前所需的所有工作。 例如,一个 PreBind 插件可能需要制备网络卷并且在允许 Pod 运行在该节点之前 将其挂载到目标节点上。

如果任何 PreBind 插件返回错误,则 Pod 将被拒绝并且 退回到调度队列中。

Bind

Bind 插件用于将 Pod 绑定到节点上。直到所有的 PreBind 插件都完成,Bind 插件才会被调用。 各 Bind 插件按照配置顺序被调用。Bind 插件可以选择是否处理指定的 Pod。 如果某 Bind 插件选择处理某 Pod,剩余的 Bind 插件将被跳过

PostBind

这是个信息性的扩展点。 PostBind 插件在 Pod 成功绑定后被调用。这是绑定周期的结尾,可用于清理相关的资源。

Unreserve

这是个信息性的扩展点。 如果 Pod 被保留,然后在后面的阶段中被拒绝,则 Unreserve 插件将被通知。 Unreserve 插件应该清楚保留 Pod 的相关状态。


Kube-scheduler的整体逻辑大图:


参考文献:

https://kubernetes.io/zh-cn/docs/concepts/scheduling-eviction/kube-scheduler/

https://www.yuque.com/baxiaoshi/tyado3/iy28fu

https://www.yuque.com/baxiaoshi/tyado3/bqs7vg

相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
目录
相关文章
|
4月前
|
Kubernetes Docker 容器
Kubernetes与Docker参数对照:理解Pod中的command、args与Dockerfile中的CMD、ENTRYPOINT。
需要明确的是,理解这些都需要对Docker和Kubernetes有一定深度的理解,才能把握二者的区别和联系。虽然它们都是容器技术的二个重要组成部分,但各有其特性和适用场景,理解它们的本质和工作方式,才能更好的使用这些工具,将各自的优点整合到生产环境中,实现软件的快速开发和部署。
139 25
|
4月前
|
Kubernetes Shell Windows
【Azure K8S | AKS】在AKS的节点中抓取目标POD的网络包方法分享
在AKS中遇到复杂网络问题时,可通过以下步骤进入特定POD抓取网络包进行分析:1. 使用`kubectl get pods`确认Pod所在Node;2. 通过`kubectl node-shell`登录Node;3. 使用`crictl ps`找到Pod的Container ID;4. 获取PID并使用`nsenter`进入Pod的网络空间;5. 在`/var/tmp`目录下使用`tcpdump`抓包。完成后按Ctrl+C停止抓包。
163 12
|
8月前
|
存储 Kubernetes Docker
【赵渝强老师】Kubernetes中Pod的基础容器
Pod 是 Kubernetes 中的基本单位,代表集群上运行的一个进程。它由一个或多个容器组成,包括业务容器、基础容器、初始化容器和临时容器。基础容器负责维护 Pod 的网络空间,对用户透明。文中附有图片和视频讲解,详细介绍了 Pod 的组成结构及其在网络配置中的作用。
135 1
【赵渝强老师】Kubernetes中Pod的基础容器
|
8月前
|
Prometheus Kubernetes 监控
深入探索Kubernetes中的Pod自动扩展(Horizontal Pod Autoscaler, HPA)
深入探索Kubernetes中的Pod自动扩展(Horizontal Pod Autoscaler, HPA)
|
2月前
|
资源调度 Kubernetes 调度
从单集群到多集群的快速无损转型:ACK One 多集群应用分发
本文介绍如何利用阿里云的分布式云容器平台ACK One的多集群应用分发功能,结合云效CD能力,快速将单集群CD系统升级为多集群CD系统。通过增加分发策略(PropagationPolicy)和差异化策略(OverridePolicy),并修改单集群kubeconfig为舰队kubeconfig,可实现无损改造。该方案具备多地域多集群智能资源调度、重调度及故障迁移等能力,帮助用户提升业务效率与可靠性。
|
2月前
|
资源调度 Kubernetes 调度
从单集群到多集群的快速无损转型:ACK One 多集群应用分发
ACK One 的多集群应用分发,可以最小成本地结合您已有的单集群 CD 系统,无需对原先应用资源 YAML 进行修改,即可快速构建成多集群的 CD 系统,并同时获得强大的多集群资源调度和分发的能力。
100 9
|
4月前
|
存储 Kubernetes 监控
K8s集群实战:使用kubeadm和kuboard部署Kubernetes集群
总之,使用kubeadm和kuboard部署K8s集群就像回归童年一样,简单又有趣。不要忘记,技术是为人服务的,用K8s集群操控云端资源,我们不过是想在复杂的世界找寻简单。尽管部署过程可能遇到困难,但朝着简化复杂的目标,我们就能找到意义和乐趣。希望你也能利用这些工具,找到你的乐趣,满足你的需求。
396 33
|
4月前
|
Kubernetes 开发者 Docker
集群部署:使用Rancher部署Kubernetes集群。
以上就是使用 Rancher 部署 Kubernetes 集群的流程。使用 Rancher 和 Kubernetes,开发者可以受益于灵活性和可扩展性,允许他们在多种环境中运行多种应用,同时利用自动化工具使工作负载更加高效。
228 19
|
4月前
|
人工智能 分布式计算 调度
打破资源边界、告别资源浪费:ACK One 多集群Spark和AI作业调度
ACK One多集群Spark作业调度,可以帮助您在不影响集群中正在运行的在线业务的前提下,打破资源边界,根据各集群实际剩余资源来进行调度,最大化您多集群中闲置资源的利用率。
|
7月前
|
Prometheus Kubernetes 监控
OpenAI故障复盘 - 阿里云容器服务与可观测产品如何保障大规模K8s集群稳定性
聚焦近日OpenAI的大规模K8s集群故障,介绍阿里云容器服务与可观测团队在大规模K8s场景下我们的建设与沉淀。以及分享对类似故障问题的应对方案:包括在K8s和Prometheus的高可用架构设计方面、事前事后的稳定性保障体系方面。

推荐镜像

更多