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搭建和管理企业级网站应用
目录
相关文章
|
1月前
|
存储 Kubernetes Docker
【赵渝强老师】Kubernetes中Pod的基础容器
Pod 是 Kubernetes 中的基本单位,代表集群上运行的一个进程。它由一个或多个容器组成,包括业务容器、基础容器、初始化容器和临时容器。基础容器负责维护 Pod 的网络空间,对用户透明。文中附有图片和视频讲解,详细介绍了 Pod 的组成结构及其在网络配置中的作用。
【赵渝强老师】Kubernetes中Pod的基础容器
|
1月前
|
Prometheus Kubernetes 监控
深入探索Kubernetes中的Pod自动扩展(Horizontal Pod Autoscaler, HPA)
深入探索Kubernetes中的Pod自动扩展(Horizontal Pod Autoscaler, HPA)
|
1月前
|
运维 Kubernetes Shell
【赵渝强老师】K8s中Pod的临时容器
Pod 是 Kubernetes 中的基本调度单位,由一个或多个容器组成,包括业务容器、基础容器、初始化容器和临时容器。临时容器用于故障排查和性能诊断,不适用于构建应用程序。当 Pod 中的容器异常退出或容器镜像不包含调试工具时,临时容器非常有用。文中通过示例展示了如何使用 `kubectl debug` 命令创建临时容器进行调试。
|
1月前
|
Kubernetes 调度 容器
【赵渝强老师】K8s中Pod中的业务容器
Pod 是 Kubernetes 中的基本调度单元,由一个或多个容器组成。除了业务容器,Pod 还包括基础容器、初始化容器和临时容器。本文通过示例介绍如何创建包含业务容器的 Pod,并提供了一个视频讲解。示例中创建了一个名为 "busybox-container" 的业务容器,并使用 `kubectl create -f firstpod.yaml` 命令部署 Pod。
|
2天前
|
Prometheus Kubernetes 监控
OpenAI故障复盘 - 阿里云容器服务与可观测产品如何保障大规模K8s集群稳定性
聚焦近日OpenAI的大规模K8s集群故障,介绍阿里云容器服务与可观测团队在大规模K8s场景下我们的建设与沉淀。以及分享对类似故障问题的应对方案:包括在K8s和Prometheus的高可用架构设计方面、事前事后的稳定性保障体系方面。
|
5天前
|
Kubernetes 网络协议 应用服务中间件
Kubernetes Ingress:灵活的集群外部网络访问的利器
《Kubernetes Ingress:集群外部访问的利器-打造灵活的集群网络》介绍了如何通过Ingress实现Kubernetes集群的外部访问。前提条件是已拥有Kubernetes集群并安装了kubectl工具。文章详细讲解了Ingress的基本组成(Ingress Controller和资源对象),选择合适的版本,以及具体的安装步骤,如下载配置文件、部署Nginx Ingress Controller等。此外,还提供了常见问题的解决方案,例如镜像下载失败的应对措施。最后,通过部署示例应用展示了Ingress的实际使用方法。
21 2
|
17天前
|
存储 Kubernetes 关系型数据库
阿里云ACK备份中心,K8s集群业务应用数据的一站式灾备方案
本文源自2024云栖大会苏雅诗的演讲,探讨了K8s集群业务为何需要灾备及其重要性。文中强调了集群与业务高可用配置对稳定性的重要性,并指出人为误操作等风险,建议实施周期性和特定情况下的灾备措施。针对容器化业务,提出了灾备的新特性与需求,包括工作负载为核心、云资源信息的备份,以及有状态应用的数据保护。介绍了ACK推出的备份中心解决方案,支持命名空间、标签、资源类型等维度的备份,并具备存储卷数据保护功能,能够满足GitOps流程企业的特定需求。此外,还详细描述了备份中心的使用流程、控制台展示、灾备难点及解决方案等内容,展示了备份中心如何有效应对K8s集群资源和存储卷数据的灾备挑战。
|
1月前
|
Kubernetes 监控 Cloud Native
Kubernetes集群的高可用性与伸缩性实践
Kubernetes集群的高可用性与伸缩性实践
75 1
|
2月前
|
JSON Kubernetes 容灾
ACK One应用分发上线:高效管理多集群应用
ACK One应用分发上线,主要介绍了新能力的使用场景
|
2月前
|
Kubernetes 持续交付 开发工具
ACK One GitOps:ApplicationSet UI简化多集群GitOps应用管理
ACK One GitOps新发布了多集群应用控制台,支持管理Argo CD ApplicationSet,提升大规模应用和集群的多集群GitOps应用分发管理体验。

热门文章

最新文章