大赛介绍
2022 第三届云原生编程挑战赛,是由阿里云、Intel 主办,云原生应用平台、天池联合承办的云原生顶级品牌赛事。
本届大赛将继续深度探索服务网格、边缘容器、Serverless 三大热门技术领域,为热爱技术的年轻人提供一个挑战世界级技术问题的舞台,希望用技术为全社会创造更大价值。大家赶快报名参赛吧!
丰厚奖励等你来报名!
- 瓜分¥510,000 元现金大奖
- 三大热门赛道任意选择
- 邀请小伙伴报名兑换精美礼品
- 完成 Serverless 场景体验领阿里云背包
赛题背景
ACK@Edge 是阿里云容器服务针对边缘计算场景推出的云边一体化云原生解决方案,主打“云端托管、边缘自治”的产品理念,为用户提供云边多维度协同,海量异构算力管理,边缘 AI 套件,可观测,轻量化,弹性等能力。现已广泛应用于边缘云、物联网等典型边缘计算场景,覆盖交通、能源、新零售、智慧驾驶、CDN 等多个行业。同时,ACK@Edge 依托 CNCF OpenYurt 强大的社区生态,积极参与并协同社区持续打磨和完善设备孪生、云边协同网络、高可用等领先技术能力。
为了解决在边缘计算场景下云边网络通信断连时,保证边缘侧节点上业务可以自愈,OpenYurt 在边缘侧组件和 APIServer 之间新增 YurtHub 组件。边缘侧的组件(kubelet,kube-proxy..)对 apiserver 的请求,会首先经过 YurtHub 组件的然后再转发给 apiserver,YurtHub 会将 apiserver 返回的数据缓存到本地。当云边网络断开时,Yurthub 还能将本地缓存的数据返回给边缘侧组件。
但是在实践过程中我们发现,在云边协同的边缘场景架构体系下,有着大量的轻量级的设备,这些设备的配置相对比较低,降低边缘侧组件的资源占用率,为设备上的业务腾出更多的资源,已经成为了必须要解决的问题。
本赛题希望实现一个边缘侧的 edge-proxy 组件,负责转发边缘侧组件例如 kubelet,kube-proxy 的请求,同时能够将云上 apiserver 返回的数据进行过滤,同时在返回给请求端时可以缓存到本地,并尽可能的降低 cpu,内存等资源占用率。
赛题解析
在 Kubernetes 系统中,list/watch 机制主要解决组件间实时数据的异步同步。其中 List 请求是普通的 HTTP GET 请求,一次 List 请求将返回请求类型资源的全量数据。而 watch 请求是基于 HTTP 协议的 chunked 机制实现的长连接,用于实时通知请求资源的数据变化。相关的介绍可以参考:
https://kubernetes.io/docs/reference/using-api/api-concepts/
List/Watch 机制是 Kubernetes 中实现集群控制模块最核心的设计之一,它采用统一的异步消息处理机制,保证了消息的实时性、可靠性、顺序性和性能等,为声明式风格的 API 奠定了良好的基础。Informer 模块是 Kubernetes 中的基础组件,负责各组件与 Apiserver 的资源与事件同步。Kubernetes 中的组件,如果要访问 Kubernetes 中的 Object,绝大部分情况下会使用 Informer 中的 Lister()方法,而非直接请求 Kubernetes API。
client-go:
Reflector:reflector 用来 watch 特定的 K8s API 资源。具体的实现是通过 ListAndWatch 的方法,watch 可以是 K8s 内建的资源或者是自定义的资源。当 reflector 通过 watch API 接收到有关新资源实例存在的通知时,它使用相应的 list API 获取新创建的对象,并将其放入 watchHandler 函数内的 Delta Fifo 队列中。
Informer:informer 从 Delta Fifo 队列中弹出对象。执行此操作的功能是 processLoop。base controller 的作用是保存对象以供以后检索,并调用我们的控制器将对象传递给它。
Indexer:索引器提供对象的索引功能。典型的索引用例是基于对象标签创建索引。Indexer 可以根据多个索引函数维护索引。Indexer 使用线程安全的数据存储来存储对象及其键。
云边挑战
上述 List watch 机制适合在网络通信质量比较好的数据中心内运行, 但是在云边场景,云边网络连接不可靠的情况下带来很大的挑战。以 kubelet 为例,若 kubelet 与云上的 APIServer 网络断开, 此时主机发送了重启, 按照 list-watch 的逻辑, kubelet 要首先通过 APIServer list 全量属于本主机上的 pod 数据, 然后再通过 CRI 接口对 POD 进行重建,由于云边网络断开,kubelet 无法通过 list 机制获取本主机上的 pod 数据,自然无法对 pod 进行重建,导致业务无法正常运行。
解决方案
因此我们需要在边缘侧新增一个组件 edge-proxy, edge-proxy 组件包含能力主要有边缘组件(如 kubelet 等)的 pods, configmaps 的 list/watch 请求的转发,以及对 kube-apiserver 返回 response 的过滤和缓存处理。总结一句话就是实现一个带有数据过滤和缓存功能的 7 层反向代理。不过这里的 7 层请求是 Kubernetes 系统中定制的 List/Watch 请求。同时 edge-proxy 在离线状态下还能返回 kubelet ,kube-proxy 这些边缘侧组件 list-watch 的数据,充当离线的 APIServer。
7 层请求的代理转发,如果云边网络通信正常时,请求需要转发到云端 kube-apiserver。而当云边网络断连时,list请求需要返回本地的缓存数据。
kube-apiserver 返回 response 的过滤和缓存处理,首先返回给请求端的数据应该过滤完成的。而缓存和过滤的处理先后顺序并没有要求,选手可以根据自身需求来决定。但是需要注意 List/Watch 请求的 response 数据不太一样,List 请求的 response 中可以一次性读取出全量数据,而 Watch 请求属于云端实时推送,是长连接,当云端有数据变化时,才能从 response 中读取云端推送过来的变化数据。不管是 List,还是 Watch 请求,edge-proxy 都需要返回给请求方过滤好之后的数据。
同时针对大规模 response 数据的过滤,缓存,以及返回等功能,如何高效且实时的处理,将直接影响到处理的效率,以及 edge-proxy 消耗的资源。
解题思路
实现一个带数据过滤和缓存功能的 7 层反向代理:
- 当接收到 List 请求时,可以考虑直接转发到云端 kube-apiserver。当请求转发失败时(网络断开),则使用本地缓存数据返回到请求方。当然需要保证返回数据是符合过滤需求的。
- 针对 http.Response 数据的缓存和返回处理,尽可能实现并行处理,这样减少对返回处理的阻塞,从而可能获得更好的处理效率。
- http.Response 数据的过滤处理建议可以在缓存前执行。当云边网络断连时,从本地缓存返回的数据将不需要进行二次过滤。
- Watch 请求的 http.Response 数据是 chunked 机制的实时数据推送,可以考虑采用类似流数据的处理方案来解决。
如何拿到好成绩
最终成绩由数据正确性和处理效率来决定,建议在保证正确性的基础上,可以各类优化手段(如乐观锁等)来提升处理效率。期待各位选手都取得自己满意的成绩。
点击这里,立即报名!