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高效检测变更

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐