k8s控制器定时把k8s apiserver内存和cpu打得很高

近期发现,k8s apiserver的内存和cpu定时(每隔10h)被客户一个控制器打的很高,有个小突刺。排查发现,用户的控制器开启了resyncPeriod,默认值就是10h。

一般来说controller runtime框架、knative框架,都会默认这个值为10h。不同的是,controller runtime框架会打算到10h附近,而knative框架是严格10h。当然真实时间会受到上次执行的影响,略有偏差。

定位到问题后,解决起来也很简单,构建informer时,将resyncPeriod设置为0就行。

但是,客户有担心,偶发k8s里面缓存不够新的问现象,担心关闭resyncPeriod会影响到informer机制中缓存的更新。第一反应,客户是不是对ResyncPeriod这个参数有什么误解?这个参数不会重新到APIServer拉取全量数据,而只是把indexer里面的缓存的所有key list一遍,重新推进DeltaFIFO,触发controller将所有事件重新调协一遍。

顺带把整个informer机制相关的debug一遍,确认无误。此处粘贴一些图做些记录。

  1. Informer机制各种类详细介绍

https://blog.imoe.tech/2023/02/15/kubernetes-informer-mechanism/

这篇写得非常好。

网上有大量文章,写的并不准确。包括ResyncPeriod参数,竟然把它说成可以重新从K8S APIServer全量拉取informer数据,更新informer缓存,避免watch机制错误导致缓存数据的不一致。这些文章给我带来了很大的干扰。所以我不断调整ResyncPeriod,debug测试。发现控制器并没有根据ResyncPeriod参数,去LIST K8SAPISERVER更新整个缓存;而只是从indexer的ThreadSafeMap里,list所有的key,重新推入DeltaFIFO,触发事件,让控制器重新handler。

client-go 组件

  • Reflector:指的是 cache 包中定义的 Reflector 类,用于监控 Kubernetes 资源变化,其功能由 ListAndWatch 函数实现。当 Reflector 接收到资源变更的事件,会获取到变更的对象并在函数 watchHandler 中放到 Delta Fifo 队列。
  • Delta FIFO:是一个 FIFO 的队列,用来缓存 Reflector 拉取到的变更事件和资源对象;
  • Informor:是流程中最重要的节点,是整个流程的桥梁,Informer 也是在 cache 包中定义的,其功能在 processLoop 函数中实现,负责:
    • 从 Delta FIFO 中 pop 出对象并更新到 Indexer 的 cache 中;
    • 调用自定义 Controller,传递该对象。
  • Indexer:指在 cache 包中定义的 Indexer 类,主要是在资源对象上提供了索引和本地缓存的功能。经典的使用场景是基于对象的 Labels 创建索引,Indexer 可以支持使用索引函数来维护索引,同时 Indexer 使用线程安全的 Data Store 来存储资源对象和对应的 Key。默认使用的是 cache 包里的 MetaNamespaceKeyFunc 函数来生成对象的 Key,格式如:<namespace>/<name>

自定义组件

上图中 Informer referenceIndexer reference 是指在自定义 Controller 中需要自己创建的 Informer 和 Indexer 的实例,用来与整个流程进行交互,需要根据需要的资源创建对应的实例。client-go 提供了 NewIndexerInformer 函数来创建 Informer 和 Indexer 实例,也可以使用 SharedInformerFactory工厂方法来创建实例。

每个资源都会对应一个 Informer,每个 Informer 都通过 Watch 创建一个长连接。如果一个资源创建了多个 Informer 无疑是非常浪费的,所以通常都使用 SharedInformerFactory 工厂方法来创建,这样每种资源都复用一个 Informer,从而降低开销。
2. informer多重缓存的数据结构
上一篇对各种组件和方法有详细介绍,但是缓存的数据结构搞得有点云里雾里。下面这篇文章是个完美的补充:
https://juejin.cn/post/7132767272841510926

不过,各位可能有个小小的疑问,客户每10h全量调协处理一遍数据,正常应该客户的pod cpu和内存打得很高,为什么会K8S APIServer也被打得高?

这里面客户有个错误用法,他没有进行事件过滤,正常来说,重新全量调协,是要处理那些需要处理的数据,不需要的应该用EventHandler过滤掉。客户没有做,导致每个事件,他都会调协处理,处理逻辑里面有大量list资源的操作(list加了labelSelector条件), 导致K8S APIServer缓存打得很高。

不过,在协助处理客户这个问题时,我也有点小小的疑惑:

前期客户的处理逻辑里面有大量list资源操作,甚至部分没有加resourceVersion=0(直查etcd)。这种情况下,把K8S APIServer内存打得很高,可以理解。

但是后面用户把全部list操作加上resourceVersion=0后,仍然会把K8S APIServer打得很高,这个就很奇怪。K8S APIServer在接受list请求时,直接从内存里面拿数据,为何会内存瞬间打得很高?难道针对大量list请求操作,它瞬间产生大量的局部变量?看来得看看k8s相关代码,才能解答。

相关推荐
江畔何人初34 分钟前
k8s静态pod
云原生·容器·kubernetes
u0104058363 小时前
淘客返利系统的CI/CD流水线搭建:Docker镜像构建与K8s部署实践
ci/cd·docker·kubernetes
nix.gnehc4 小时前
深度解析K8s四大核心接口:CRI、CNI、CSI与OCI的设计精髓与实践逻辑
云原生·容器·kubernetes
回忆是昨天里的海4 小时前
k8s部署的微服务动态扩容
java·运维·kubernetes
没有bug.的程序员6 小时前
Docker 与 K8s 生产级实战:从镜像极致优化到集群自动化部署全流程
spring cloud·docker·kubernetes·自动化·k8s·镜像·集群自动化
小韩加油呀6 小时前
jenkins声明式pipline和shell从环境变量配置到打包构建再到发布到k8s
运维·kubernetes·jenkins
A-刘晨阳6 小时前
K8S 部署 CoreDNS 之 DNS 域名获取
运维·云原生·容器·kubernetes·dns·coredns
舰长11510 小时前
使用 kubeadm搭建生产环境的单 master 节点 K8S 集群(二)
linux·docker·kubernetes
久绊A20 小时前
春节前云平台运维深度巡检-实操经验
运维·安全·容器·kubernetes·云平台
!chen21 小时前
银河麒麟v11 kubeadm部署k8s v1.35.0高可用集群
云原生·容器·kubernetes