Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >理解 K8S 的设计精髓之 List-Watch机制和Informer模块

理解 K8S 的设计精髓之 List-Watch机制和Informer模块

作者头像
云爬虫技术研究笔记
发布于 2019-11-05 10:57:12
发布于 2019-11-05 10:57:12
3.6K00
代码可运行
举报
运行总次数:0
代码可运行

1. 前言

最近想深入了解一下K8S的内部通信机制,因此读了几遍K8S的源码,感慨很深。至今清楚的记得,当了解到K8S 组件之间仅采用HTTP 协议通信,没有依赖中间件时,我非常好奇它是如何做到的。

K8S 内部通信中,肯定要保证消息的实时性。之前以为方式有两种:

  1. 客户端组件(kubelet, scheduler, controller-manager 等)轮询 apiserver,
  2. apiserver 通知客户端。 如果采用轮询,势必会大大增加 apiserver的压力,同时实时性很低。 如果 apiserver 主动发HTTP 请求,又如何保证消息的可靠性,以及大量端口占用问题?

当阅读完 list-watch 源码后,先是所有的疑惑云开雾散,进而为K8S设计理念所折服。List-watchK8S 统一的异步消息处理机制,保证了消息的实时性,可靠性,顺序性,性能等等,为声明式风格的API 奠定了良好的基础,它是优雅的通信方式,是 K8S 架构的精髓。

2. List-Watch 机制具体是什么样的

Etcd存储集群的数据信息,apiserver作为统一入口,任何对数据的操作都必须经过 apiserver。客户端(kubelet/scheduler/controller-manager)通过 list-watch 监听 apiserver 中资源(pod/rs/rc等等)的 create, updatedelete 事件,并针对事件类型调用相应的事件处理函数

那么list-watch 具体是什么呢,顾名思义,list-watch有两部分组成,分别是listwatchlist 非常好理解,就是调用资源的list API罗列资源,基于HTTP短链接实现;watch则是调用资源的watch API监听资源变更事件,基于HTTP 长链接实现,也是本文重点分析的对象。以 pod 资源为例,它的 list watch API 分别为:[List API](https://v1-10.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.10/#list-all-namespaces-63),返回值为 [PodList](https://v1-10.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.10/#podlist-v1-core),即一组 pod`。

GET /api/v1/pods

Watch API,往往带上 watch=true,表示采用 HTTP 长连接持续监听 pod 相关事件,每当有事件来临,返回一个 WatchEvent。

GET /api/v1/watch/pods

K8Sinformer 模块封装 list-watch API,用户只需要指定资源,编写事件处理函数,AddFunc, UpdateFuncDeleteFunc等。如下图所示,informer首先通过list API 罗列资源,然后调用 watch API监听资源的变更事件,并将结果放入到一个 FIFO 队列,队列的另一头有协程从中取出事件,并调用对应的注册函数处理事件。Informer还维护了一个只读的Map Store 缓存,主要为了提升查询的效率,降低apiserver 的负载。

3.Watch 是如何实现的

List的实现容易理解,那么 Watch 是如何实现的呢?Watch是如何通过 HTTP 长链接接收apiserver发来的资源变更事件呢?

秘诀就是 Chunked transfer encoding(分块传输编码),它首次出现在HTTP/1.1。正如维基百科所说:

HTTP 分块传输编码允许服务器为动态生成的内容维持 HTTP 持久链接。通常,持久链接需要服务器在开始发送消息体前发送Content-Length消息头字段,但是对于动态生成的内容来说,在内容创建完之前是不可知的。使用分块传输编码,数据分解成一系列数据块,并以一个或多个块发送,这样服务器可以发送数据而不需要预先知道发送内容的总大小。

当客户端调用 watch API 时,apiserver 在responseHTTP Header 中设置 Transfer-Encoding的值为chunked,表示采用分块传输编码,客户端收到该信息后,便和服务端该链接,并等待下一个数据块,即资源的事件信息。例如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ curl -i http://{kube-api-server-ip}:8080/api/v1/watch/pods?watch=yes
HTTP/1.1 200 OK
Content-Type: application/json
Transfer-Encoding: chunked
Date: Thu, 02 Jan 2019 20:22:59 GMT
Transfer-Encoding: chunked

{"type":"ADDED", "object":{"kind":"Pod","apiVersion":"v1",...}}
{"type":"ADDED", "object":{"kind":"Pod","apiVersion":"v1",...}}
{"type":"MODIFIED", "object":{"kind":"Pod","apiVersion":"v1",...}}

4. 谈谈 List-Watch 的设计理念

当设计优秀的一个异步消息的系统时,对消息机制有至少如下四点要求:

  • 消息可靠性
  • 消息实时性
  • 消息顺序性
  • 高性能

首先消息必须是可靠的,listwatch 一起保证了消息的可靠性,避免因消息丢失而造成状态不一致场景。具体而言,list API可以查询当前的资源及其对应的状态(即期望的状态),客户端通过拿期望的状态实际的状态进行对比,纠正状态不一致的资源。Watch APIapiserver保持一个长链接,接收资源的状态变更事件并做相应处理。如果仅调用 watch API,若某个时间点连接中断,就有可能导致消息丢失,所以需要通过list API解决消息丢失的问题。从另一个角度出发,我们可以认为list API获取全量数据,watch API获取增量数据。虽然仅仅通过轮询 list API,也能达到同步资源状态的效果,但是存在开销大,实时性不足的问题。

消息必须是实时的,list-watch 机制下,每当apiserver 的资源产生状态变更事件,都会将事件及时的推送给客户端,从而保证了消息的实时性

消息的顺序性也是非常重要的,在并发的场景下,客户端在短时间内可能会收到同一个资源的多个事件,对于关注最终一致性K8S 来说,它需要知道哪个是最近发生的事件,并保证资源的最终状态如同最近事件所表述的状态一样。K8S 在每个资源的事件中都带一个 resourceVersion的标签,这个标签是递增的数字,所以当客户端并发处理同一个资源的事件时,它就可以对比 resourceVersion来保证最终的状态和最新的事件所期望的状态保持一致。

List-watch 还具有高性能的特点,虽然仅通过周期性调用list API也能达到资源最终一致性的效果,但是周期性频繁的轮询大大的增大了开销,增加apiserver的压力。而watch 作为异步消息通知机制,复用一条长链接,保证实时性的同时也保证了性能。

5. Informer介绍

InformerClient-go 中的一个核心工具包。在Kubernetes源码中,如果 Kubernetes 的某个组件,需要 List/Get Kubernetes 中的 Object,在绝大多 数情况下,会直接使用Informer实例中的Lister()方法(该方法包含 了 Get 和 List 方法),而很少直接请求Kubernetes APIInformer 最基本 的功能就是List/Get Kubernetes中的 Object

如下图所示,仅需要十行左右的代码就能实现对PodListGet

6. Informer 设计思路

6.1Informer 设计中的关键点

为了让Client-go 更快地返回List/Get请求的结果、减少对 Kubenetes API的直接调用,Informer 被设计实现为一个依赖Kubernetes List/Watch API可监听事件并触发回调函数二级缓存工具包。

6.2 更快地返回 List/Get 请求,减少对 Kubenetes API 的直接调用

使用Informer实例的Lister()方法,List/Get Kubernetes 中的 Object时,Informer不会去请求Kubernetes API,而是直接查找缓存在本地内存中的数据(这份数据由Informer自己维护)。通过这种方式,Informer既可以更快地返回结果,又能减少对 Kubernetes API 的直接调用。

6.3 依赖 Kubernetes List/Watch API

Informer 只会调用Kubernetes ListWatch两种类型的 APIInformer在初始化的时,先调用Kubernetes List API 获得某种 resource的全部Object,缓存在内存中; 然后,调用 Watch APIwatch这种resource,去维护这份缓存; 最后,Informer就不再调用Kubernetes的任何 API。

List/Watch去维护缓存、保持一致性是非常典型的做法,但令人费解的是,Informer 只在初始化时调用一次List API,之后完全依赖 Watch API去维护缓存,没有任何resync机制。

笔者在阅读Informer代码时候,对这种做法十分不解。按照多数人思路,通过 resync机制,重新List一遍 resource下的所有Object,可以更好的保证 Informer 缓存Kubernetes 中数据的一致性。

咨询过Google 内部 Kubernetes开发人员之后,得到的回复是:

Informer 设计之初,确实存在一个relist无法去执 resync操作, 但后来被取消了。原因是现有的这种 List/Watch 机制,完全能够保证永远不会漏掉任何事件,因此完全没有必要再添加relist方法去resync informer的缓存。这种做法也说明了Kubernetes完全信任etcd

6.4 可监听事件并触发回调函数

Informer通过Kubernetes Watch API监听某种 resource下的所有事件。而且,Informer可以添加自定义的回调函数,这个回调函数实例(即 ResourceEventHandler 实例)只需实现 OnAdd(obj interface{}) OnUpdate(oldObj, newObj interface{}) 和OnDelete(obj interface{}) 三个方法,这三个方法分别对应informer监听到创建更新删除这三种事件类型。

Controller的设计实现中,会经常用到 informer的这个功能。

6.5 二级缓存

二级缓存属于 Informer的底层缓存机制,这两级缓存分别是DeltaFIFOLocalStore

这两级缓存的用途各不相同。DeltaFIFO用来存储Watch API返回的各种事件 ,LocalStore 只会被ListerList/Get方法访问 。

虽然InformerKubernetes 之间没有resync机制,但Informer内部的这两级缓存之间存在resync 机制。

6.6 关键逻辑介绍
  1. Informer 在初始化时,Reflector 会先 List API 获得所有的 Pod
  2. Reflect 拿到全部 Pod 后,会将全部 Pod 放到 Store 中
  3. 如果有人调用 Lister 的 List/Get 方法获取 Pod, 那么 Lister 会直接从 Store 中拿数据
  4. Informer 初始化完成之后,Reflector 开始 Watch Pod,监听 Pod 相关 的所有事件;如果此时 pod_1 被删除,那么 Reflector 会监听到这个事件
  5. Reflector 将 pod_1 被删除 的这个事件发送到 DeltaFIFO
  6. DeltaFIFO 首先会将这个事件存储在自己的数据结构中(实际上是一个 queue),然后会直接操作 Store 中的数据,删除 Store 中的 pod_1
  7. DeltaFIFO 再 Pop 这个事件到 Controller 中
  8. Controller 收到这个事件,会触发 Processor 的回调函数
  9. LocalStore 会周期性地把所有的 Pod 信息重新放到 DeltaFIFO 中
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-10-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 云爬虫技术研究笔记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
编辑精选文章
换一批
kubernetes 中 informer 的使用
在实际开发过程中,若想要获取 kubernetes 中某个资源(比如 pod)的所有对象,可以使用 kubectl、k8s REST API、client-go(ClientSet、Dynamic Client、RESTClient 三种方式) 等多种方式访问 k8s 集群获取资源。在笔者的开发过程中,最初都是直接调用 k8s 的 REST API 来获取的,使用 kubectl get pod -v=9 可以直接看到调用 k8s 的接口,然后在程序中直接访问还是比较方便的。但是随着集群规模的增长或者从国内获取海外 k8s 集群的数据,直接调用 k8s 接口获取所有 pod 还是比较耗时,这个问题有多种解决方法,最初是直接使用 k8s 原生的 watch 接口来获取的,下面是一个伪代码:
田飞雨
2019/12/13
4.4K0
kubernetes 中 informer 的使用
Kubernetes Informer机制源码解析
这篇文章来源于云原生社区组织的 Kubernetes 源码研习社的作业,是个人学习Informer机制、理解Informer各个组件的设计的总结。
CNCF
2020/09/14
1.2K0
k8s源码分析- Informer机制
由于Informer这部分的源码比较复杂,调用链路也很长,后面的源码分析,都会围绕这一张图展开。
kinnylee
2020/10/15
5.5K0
k8s源码分析- Informer机制
16.深入k8s:Informer使用及其源码分析
这次讲解我用了很一些图,尽可能的把这个模块给描述清楚,如果感觉对你有所帮助不妨发一封邮件激励一下我~
luozhiyun
2020/10/28
2.6K0
16.深入k8s:Informer使用及其源码分析
浅谈 K8s Informer
进入 K8s 的世界,会发现有很多的 Controller,它们都是为了完成某类资源(如 pod 是通过 DeploymentController, ReplicaSetController 进行管理)的调谐,目标是保持用户期望的状态。
astraw99
2021/09/14
1.5K2
浅谈 K8s Informer
图解K8s源码 - kube-controller-manager篇
在kubernetes master节点中最重要的三个组件是:kube-apiserver、kube-controller-manager、kube-scheduler 分别负责k8s集群的资源访问入口、集群状态管理、集群调度。我们在之前的文章介绍了集群资源访问入口kube-apiserver “图解K8s源码 - kube-apiserver篇”,本篇尝试梳理清楚 kube-controller-manager 是如何“Manage Controller”的。
才浅Coding攻略
2022/12/12
9880
图解K8s源码 - kube-controller-manager篇
如何高效掌控K8s资源变化?K8s Informer实现机制浅析
作者:腾讯云云巢团队研发工程师 王成 导语:本文通过分析 K8s 中 Reflector(反射器)、DeletaFIFO(增量队列)、Indexer(索引器)、Controller(控制器)、SharedInformer(共享资源通知器)、processorListener(事件监听处理器)、workqueue(事件处理工作队列) 等组件,对 Informer 实现机制进行了解析。 PART ONE 概述 进入 K8s 的世界,会发现有很多的 Controller,它们都是为了完成某类资源(如 pod
腾源会
2021/09/15
5370
图解 K8S 源码 - Informer 篇(上)
众所周知,在 Kubernetes 中各组件是通过 HTTP 协议进行通信的,而组件间的通信也并没有依赖任何中间件,那么如何保证消息的实时性、可靠性、顺序性呢?Informer 机制很好的解决了这个问题。Kubernetes 中各组件与 API Server 的通信都是通过 client-go 的 informer 机制来保证和完成的。
郭旭东
2020/12/30
1.2K0
图解 K8S 源码 - Informer 篇(上)
K8s 系列(四) - 浅谈 Informer
进入 K8s 的世界,会发现有很多的 Controller,它们都是为了完成某类资源(如 pod 是通过 DeploymentController, ReplicaSetController 进行管理)的调谐,目标是保持用户期望的状态。
astraw99
2021/09/22
1.4K1
K8s 系列(四) - 浅谈 Informer
K8s 如何提供更高效稳定的编排能力?K8s Watch 实现机制浅析
王成,腾讯云研发工程师,Kubernetes member,从事数据库产品容器化、资源管控等工作,关注 Kubernetes、Go、云原生领域。 目录 概述 从 HTTP 说起    2.1 Content-Length     2.2 Chunked Transfer Encoding     2.3 HTTP/2 APIServer 启动 ETCD 资源封装  客户端 Watch 实现 服务端 Watch 实现 小结 1. 概述 进入 K8s 的世界,会发现几乎所有对象都被抽象为了资源(Resourc
腾讯云原生
2022/05/05
2.6K0
K8s 如何提供更高效稳定的编排能力?K8s Watch 实现机制浅析
Kubernetes Informer基本原理
不论是 k8s 自身组件,还是自己编写 controller,都需要通过 apiserver 监听 etcd 事件来完成自己的控制循环逻辑。
政采云前端团队
2024/01/30
6040
Kubernetes Informer基本原理
k8s-client-go源码剖析(二)
本周是K8S源码研习社第一期第二周,学习内容是学习Informer机制,本文以这个课题进行展开。
用户2672162
2021/02/02
5180
《一起读 kubernetes 源码》揭秘 k8s 关键机制 informer
在第二章我们会去看 k8s 中常用对象的源码,不过在看这些对象之前,我们需要聊一聊 informer 机制。这个机制可以说是 k8s 设计之中的一个重点了。这个机制的设计不仅仅让代码本身变得清晰,更让整个系统的结构更容易扩展。所以这个机制需要放到第二章的第一节来说。
LinkinStar
2024/05/01
3320
《一起读 kubernetes 源码》揭秘 k8s 关键机制 informer
【K8s源码品读】009:Phase 1 - kube-scheduler - Informer监听资源变化
什么是Informer?这一节,我将先抛开代码,重点讲一下这个Informer,因为它是理解k8s运行机制的核心概念。
junedayday
2021/08/05
4850
Kubernetes之Informer机制详解
本文尝试从Informer中的Lister、Watcher、Indexer、Store及Controller 5个组件展开对其进行详细阐述。希望对您有所帮助!
锅总
2024/06/28
1.7K0
Kubernetes之Informer机制详解
k8s informer 是如何保证事件不丢失的?
我们知道 k8s 里重要概念之一就是 声明式 API,比如 kubectl apply 就是声明式 API的实现。
没有故事的陈师傅
2024/01/10
6240
k8s informer 是如何保证事件不丢失的?
kubernetes controller 解析
controller内部有个内存cache,cache 一般和lister/ indexer 一起配合使用, 用一个 Indexer interface进行的包装
王磊-字节跳动
2019/10/07
1.9K0
k8s client-go 的 DeltaFIFO 源码分析
本文阅读代码链接:https://github.com/kubernetes/client-go/tree/release-1.30
rxg456
2025/03/11
1060
k8s client-go 的 DeltaFIFO 源码分析
k8s通过deployment部署一个pod的流程
在学习k8s工作流程之前,我们得再次认识一下上篇k8s架构与组件详解中提到的kube-controller-manager一个k8s中许多控制器的进程的集合。
你大哥
2021/10/11
2K0
k8s通过deployment部署一个pod的流程
client-go 之 Reflector 源码分析
前面我们说了 Informer 通过对 APIServer 的资源对象执行 List 和 Watch 操作,把获取到的数据存储在本地的缓存中,其中实现这个的核心功能就是 Reflector,我们可以称其为反射器,从名字我们可以看出来它的主要功能就是反射,就是将 Etcd 里面的数据反射到本地存储(DeltaFIFO)中。Reflector 首先通过 List 操作获取所有的资源对象数据,保存到本地存储,然后通过 Watch 操作监控资源的变化,触发相应的事件处理,比如前面示例中的 Add 事件、Update 事件、Delete 事件。
我是阳明
2020/08/28
1.7K0
相关推荐
kubernetes 中 informer 的使用
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验