Kubernetes中基于etcd的watch关键设计

本文介绍了kubernetes针对etcd的watch场景,k8s在性能优化上面的一些设计,逐个介绍缓存、定时器、序列化缓存、bookmark机制、forget机制、针对数据的索引与ringbuffer等组件的场景以及解决的问题,希望能帮助到那些对apiserver中的watch机制实现感兴趣的朋友

01

事件驱动与控制器的集群都一个样

k8s中并没有将业务的具体处理逻辑耦合在rest接口中,rest接口只负责数据的存储,通过控制器模式,分离数据存储与业务逻辑的耦合,保证apiserver业务逻辑的简洁。

控制器通过watch接口来感知对应的资源的数据变更,从而根据资源对象中的期望状态与当前状态之间的差异,来决策业务逻辑的控制,watch本质上做的事情其实就是将感知到的事件发生给关注该事件的控制器。

02

Watch的核心机制

这里我们先介绍基于etcd实现的基础的watch模块

事件类型与etcd

一个数据变更本质上无非就是三种类型:新增、更新和删除,其中新增和删除都比较容易因为都可以通过当前数据获取,而更新则可能需要获取之前的数据,这里其实就是借助了etcd中revision和mvcc机制来实现,这样就可以获取到之前的状态和更新后的状态,并且获取后续的通知

事件管道

事件管道则是负责事件的传递,在watch的实现中通过两级管道来实现消息的分发,首先通过watch etcd中的key获取感兴趣的事件,并进行数据的解析,完成从bytes到内部事件的转换并且发送到输入管道(incomingEventChan)中,然后后台会有线程负责输入管道中获取数据,并进行解析发送到输出管道(resultChan)中,后续会从该管道来进行事件的读取发送给对应的客户端。

事件缓冲区

事件缓冲区是指的如果对应的事件处理程序与当前事件发生的速率不匹配的时候,则需要一定的buffer来暂存因为速率不匹配的事件, 在go里面大家通常使用一个有缓冲的chan构建

到这里基本上就实现了一个基本可用的watch服务,通过etcd的watch接口监听数据,然后启动独立goroutine来进行事件的消费,并且发送到事件管道供其他接口调用。

03

Cacher

kubernetes中所有的数据和系统都基于etcd来实现,如何减轻访问压力呢,答案就是缓存,watch也是这样,本节我们来看看如何实现watch缓存机制的实现,这里的cacher是针对。

Reflector

Reflector是client-go中的一个组件,其通过listwatch接口获取数据存储在自己内部的store中,cacher中通过该组件从etcd进行watch操作,避免为每个组件都创建一个etcd的watch。

watchCache

wacthCache负责存储watch到的事件,并且将watch的事件建立对应的本地索引缓存,同时在构建watchCache还负责将事件的传递,其将watch到的事件通过eventHandler来传递给上层的Cacher组件。

cacheWatcher

cacheWatcher顾名思义其是就是针对cache的一个watcher(watch.Interface)实现, 前端的watchServer负责从起ResultChan里面获取事件进行转发。

Cacher

Cacher基于etcd的store结合上面的watchCache和Reflector共同构建带缓存的REST store, 针对普通的增删改功能其直接转发给etcd的store来进行底层的操作,而对于watch操作则进行拦截,构建并返回cacheWatcher组件。

04

Cacher的优化

看完基础组件的实现,接着我们看下针对watch这个场景k8s中还做了那些优化,学习针对类似场景的优化方案。

序列化缓存

如果我们有多个watcher都wacth同一个事件,在最终的时候我们都需要进行序列化,cacher中在分发的时候,如果发现超过指定数量的watcher, 则会在进行dispatch的时候,为其构建构建一个缓存函数,针对多个watcher只会进行一次的序列化。

nonblocking

在上面我们提到过事件缓冲区,但是如果某个watcher消费过慢依然会影响事件的分发,为此cacher中通过是否阻塞(是否可以直接将数据写入到管道中)来将watcher分为两类,针对不能立即投递事件的watcher, 则会在后续进行重试。

TimeBudget

针对阻塞的watcher在进行重试的时候,会通过dispatchTimeoutBudget构建一个定时器来进行超时控制, 那什么叫Budget呢,其实如果在这段时间内,如果重试立马就成功,则本次剩余的时间,在下一次进行定时的时候,则可以使用之前剩余的余额,但是后台也还有个线程,用于周期性重置。

forget机制

针对上面的TimeBudget如果在给定的时间内依旧无法进行重试成功,则就会通过forget来删除对应的watcher, 由此针对消费特别缓慢的watcher则可以通过后续的重试来重新建立watch,从而减小对apiserver的watch压力。

bookmark机制

bookmark机制是大阿里提供的一种优化方案,其核心是为了避免单个某个资源一直没有对应的事件,此时对应的informer的revision会落后集群很大,bookmark通过构建一种BookMark类型的事件来进行revision的传递,从而让informer再重启后不至于落后特别多。

watchCache中的ringbuffer

watchCache中通过store来构建了对应的索引缓存,但是在listwatch操作的时候,则通常需要获取某个revision后的所有数据,针对这类数据watchCache中则构建了一个ringbuffer来进行历史数据的缓存。

05

设计总结

本文介绍了kubernetes针对etcd的watch场景,k8s在性能优化上面的一些设计,逐个介绍缓存、定时器、序列化缓存、bookmark机制、forget机制、针对数据的索引与ringbuffer等组件的场景以及解决的问题,希望能帮助到对apiserver中的watch机制实现感兴趣的朋友。

作者:8小时coding

原文地址:http://rrd.me/fKges

END

搭载鲲鹏920处理器

提供更高性能、易获取、易运维的 算力平台

点击 阅读原文 立即获取公测名额