【博客682】k8s apiserver bookmarks机制以更高效检测变更

k8s apiserver bookmarks机制以更高效检测变更

list-watch背景:

List-Watch 是kubernetes中server和client通信的最核心的机制, 比如说api-server监听etcd, kubelet监听api-server, scheduler监听api-server等等,其实其他模块监听api-server相当于监听etcd,因为在k8s的设计中,只有api-server能跟etcd通信,其他模块需要etcd的数据就只好监听api-server了。

etcd默认保留5分钟以内的变更记录,每个资源发生变更都会更新一个更大的资源版本ResourceVersion,ResourceVersion是一个所有资源类型共享的全局变量。

  • 对于watch请求来说,你可以指定一个resourceVersion=0来获取5分钟以内的任意变更记录及其之后,这种表现很奇怪,所以不建议指定0。可以指定一个resourceVersion来获取这个资源版本之后的变更记录,但这个资源版本早于5分钟以内保留的最小版本,则会回复一个410状态码,如果大于最大版本,则可能会一直等下去,直到超时。

  • 对于list,请求后会返回一个Kind=XXList的资源类型,XXList这种资源类型是按照惯例附带创建的,比如Pod和PodList,如果你写过CRD应该能明白了;items字段内包含资源列表,metadata包含的了resourceVersion,但这个resourceVersion是PodList的资源版本,而不是Pod的资源版本,指定resourceVersion=0来获取任意的PodList,也可以指定一个resourceVersion来获取这个资源版本或之后的PodList,如果指定的resourceVersion小于当前最新资源版本,它总是返回最新的PodList,如果大于则返回504状态码。但如果你指定了limit参数或resourceVersionMatch=Excat,就意味着apiserver必须精准匹配你填写的resourceVersion,这时候就和watch一样了,如果找不到指定的resourceVersion(可能是超过了5分钟),则会返回410状态码。

  • 变更事件有四种:ADD, DELETE, MODIFY, BOOKMARK。BOOKMARK是干什么的?正如前面所说etcd只保留5分钟的变更记录,万一客户端很长时间内都没有watch到变更,然后断连之后又重连到apiserver时,客户端可能按常规的把上次收到的resourceVersion传到url里,但这个resourceVersion已经是一个过期的资源版本,apiserver找不到资源版本,就会回复一个410状态码。那么这时客户端为了能获取最新的资源版本号就不得不先list一次。为了防止这种情况,apiserver会定期发送BOOKMARK事件,BOOKMARK将包含一个当前最新的资源版本号,尽管这个版本号对应的资源类型并不是你监听的那种,但这样是为了客户端能更新最新的资源版本号,而不至于需要发起list请求

bookmarks机制出现背景以及解决了什么问题:

先提List-Watch,简单来讲就是先list当前时间点为止的全量变化,然后watch增量变化。

实现这个逻辑的模块就是go-client中的Reflector。

这一机制很好,减轻了workload,但是有一个场景有问题: 断开重连(watch因为某些原因断开,然后reconnect)

因为有可能在断开期间resource有更新,但是没watch到,这样就丢失了event(断开期间),怎么解决这个问题呢,kubernetes给resource添加了resourceversion,这样当reconnect的时候,client只要发送断开前的resourceversion, server就会把这个resourceversion之后的所有event发给client,这样就避免了丢失event。

但是还有一个问题,etcd保存历史变更时间太短,默认etcd3仅仅保存5分钟的变更。 另外resourceversion是一类资源共用一个自增长的数列,举例来讲:所有的pod都使用同一个自增数列,而List-Watch机制是带filter的,比如说某一个kubelet就只关心位于自己node上的pod,所以在该kubelet看来,resourceversion只是增长的,但是并不连续, 比如改kubelet看到的resourceversion是(1,3,8, 23, 44), 没有的resourceversion因为该pod并不在该kubelet所在的node上,所以该kubelet并不关心。

想象一个场景,某kubelet的watch connection断开了,reconnect的时候上次断开前的resourceversion是5,但是此时api-server保存的历史变更已经是resourceversion = 10了, 并不是说这个reconnct花了超过5分钟,而是resourceversion = 5之后的几个版本该kubelet并不关心,比如:由于pod调度到别的node,kubelet不关心别的node上的pod,所以没有更新version,一直保持resourceversion=5,一旦reconnect只能拿着5来找server(这段要好好理解), server也没办法啊,只要返回一个错:too old version error,然后client(kubelet)看到这个错只好清空自己之前的积累(cache),重新List,如果累计了太多的历史变更,这得花较长的时间。

bookmark其实就是server到client的一个通知机制,不管你关心不关心(由于filter),一旦发生变更我通知你,但是因为你不关心,所以我仅仅通知你变更的resourceversion,至于变更是什么内容,不告诉你,这样client就有了最新的resourceversion,下次断掉重连可以拿着新的resourceversion来发起watch,这样就大大减少了需要发起List的几率。

k8s apiserver高效检测变更





相关推荐
鹤落晴春34 分钟前
【K8s】Pod调度、configMaps
云原生·容器·kubernetes
张忠琳1 小时前
【runc 1.4.2】(Part 2)runc 1.4.2 超深度分析 — CLI层:main.go、命令文件、runner、信号处理、TTY
云原生·kubernetes·runc
极客先躯2 小时前
高级java每日一道面试题-2026年02月02日-实战篇[Docker]-如何实现容器的持久化存储?
docker·容器·面试宝典·持久化·存储·韵味·java高级面试题
阿里云云原生3 小时前
AI 提效是“假象”还是“红利”?用 LoongSuite + SLS 构建组织级 AI 编码度量看板
云原生
极客先躯3 小时前
高级java每日一道面试题-2026年02月01日-实战篇[Docker]-Docker Volume 的生命周期管理是怎样的?
java·运维·docker·容器·持久化·架构图·容器卷
Java识堂4 小时前
如何对微服务进行拆分?
微服务·云原生·架构
某林2125 小时前
Isaac Sim 5.1.0 无头服务器部署与 RTX 显存段错误排障全记录
运维·服务器·docker·容器·isaac
m0_738120725 小时前
Docker 环境下 Vulfocus 靶场搭建全流程(附镜像源问题解决方案)
运维·服务器·网络·安全·docker·容器
Plastic garden7 小时前
K8s知识(3) Pod亲和性,调度
云原生·容器·kubernetes