热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

如何用clientgo拓展Kubernetes的API【转载】

本文是转载信息如下,如有侵权,立删,原作者信息章骏 |才云科技云开源软件工程师毕业于武汉大学软件工程专业,之前就职于百度,担任算法策略研发工程师,主要负责搜索相关性的指标。加入才云

本文是转载信息如下,如有侵权,立删,原作者信息

章骏 | 才云科技云开源软件工程师
毕业于武汉大学软件工程专业,之前就职于百度,担任算法策略研发工程师,主要负责搜索相关性的指标。加入才云科技后,现主要负责负载均衡和灰度发布的工作。
 

今天给大家介绍一下如何使用 client-go 来拓展 Kubernetes API,写一个 Kubernetes 的控制器。

client-go 是 Kubernetes 官方推出的一个库,方便我们来调用 Kubernetes 的 RESTful API。

控制流
Overview首先,控制器需要与 kubernetes apiserver 进行通讯,则需要一个 client, 这个 client 需要有以下的信息:

  • apiserver 的地址以及连接 apiserver 的认证信息,如用户名密码或者 token。
  • kubernetes 的 API resource 的 group 和 version,以及结构体的定义。
  • 一个 serializer 来控制序列化与反序列化 apiserver 的结果。

然后你就可以用这个 client 去 apiserver list/watch 特定的类型的资源。
一般是建议使用 client-go 中提供的 informer 来 watch 资源的变更而不是轮询 apiserver,因为 informer 的方式性能更好。
Controller

下面这张图很好的展示了一个 controller 的工作流程和原理,典型的 controller 一般会有一个或者多个 informer 来跟踪 resource,把最新的状态反映到本地的 cache 中。配图:来自徐超大神分享

如何用 client-go 拓展 Kubernetes 的 API 【转载】

 

1. Controller 使用 informer 来 list/watch apiserver,然后将资源存储于本地的 cache 中。
2. 如果 informer 监听到了资源的变化(创建/更新/删除),就会调用事先注册的 ResourceEventHandler 回调函数。
3. 在 ResourceEventHandler 回调函数中,其实只是做了一些很简单的过滤,然后将关心变更的 Object 放到 workqueue 里面。

4. Controller 从 workqueue 里面取出 Object,启动一个 worker 来执行自己的业务逻辑,业务逻辑通常是计算目前集群的状态和用户希望达到的状态有多大的区别,然后孜孜不倦地让 apiserver 将状态演化到用户希望达到的状态,比如为 deployment 创建新的 pods,或者是扩容/缩容 deployment。
5. 在worker中就可以使用 lister 来获取 resource,而不用频繁的访问 apiserver,因为 apiserver 中 resource 的变更都会反映到本地的 cache 中。

Clients


下面介绍 client-go 中的三种 client。
ClientsetClientset 是我们最常用的 client,你可以在它里面找到 kubernetes 目前所有原生资源对应的 client。 获取方式一般是,指定 group 然后指定特定的 version,然后根据 resource 名字来获取到对应的 client。
Dynamic ClientDynamic client 是一种动态的 client,它能同时处理 kubernetes 所有的资源。并且同时,它也不同于 clientset,dynamic client 返回的对象是一个 map[string]interface{},如果一个 controller 中需要控制所有的 API,可以使用dynamic client,目前它被用在了 garbage collector 和 namespace controller。
RESTClientRESTClient 是 clientset 和 dynamic client 的基础,前面这两个 client 本质上都是 RESTClient,它提供了一些 RESTful 的函数如 Get(),Put(),Post(),Delete()。由 Codec 来提供序列化和反序列化的功能。
如何选择 Client 的类型呢?
如果你的 Controller 只是需要控制 Kubernetes 原生的资源,如 Pods,Nodes,Deployments等,那么 clientset 就够用了。
如果你需要使用 ThirdPartyResource 来拓展 Kubernetes 的 API,那么需要使用 Dynamic Client 或 RESTClient。
需要注意的是,Dynamic Client 目前只支持 JSON 的序列化和反序列化。

在1.7+版本需要将 ThirdPartyResource 迁移到 CustomResourceDefinition

Informer
最佳实践

  • 等待所有的 cache 同步完成: 这是为了避免生成大量无用的资源,比如 replica set controller 需要watch replica sets 和 pods, 在 cache 还没有同步完之前,controller 可能为一个 replica set 创建了大量重复的 pods,因为这个时候 controller 觉得目前还没有任何的 pods。
  • 修改 resource 对象前先 deepcopy 一份: 在 Informer 这个模型中,我们的 resource 一般是从本地 cache 中取出的,而本地的 cache 对于用户来说应该是 read-only 的,因为它可能是与其他的 informer 共享的,如果你直接修改 cache 中的对象,可能会引起读写的竞争。
  • 处理 DeletedFinalStateUnknown 类型对象: 当你的收到一个删除事件时,这个对象有可能不是你想要的类型,即它可能是一个 DeletedFinalStateUnknown,你需要单独处理它。
  • 注意 informer 的 resync 行为, informer 会定期从 apiserver resync 资源,这时候会收到大量重复的更新事件,这个事件有一个特点就是更新的 Object 的 ResourceVersion 是一样的,将这种不必要的更新过滤掉。
  • 在创建事件中注意 Object 已经被删除的情况: 在 Controller 重启的过程中,可能会有一些对象被删除了,重启后,Controller 会收到这些已删除对象的创建事件,请把这些对象正确地删除。
  • SharedInformer: 建议使用 SharedInformer, 它会在多个 Informer ***享一个本地 cache,这里有一 个 factory 来方便你编写一个新的 Informer。

Factory:在 client-go 中提供了一个 SharedInformerFactory 来简化 informer 的构建,具体代码在:https://github.com/kubernetes/client-go/blob/v3.0.0/informers/factory.go#L54
Lister
Lister 是用来帮助我们访问本地 cache 的一个组件。

Workqueue
Workqueue 是一个简单的 queue 提供了以下的特性:

  • 公平性:每个item 按顺序处理。
  • 严格性:一个 item 不会被并发地多次处理,而且一个相同的 item 被多次加入 queue 的话也只会处理一次。
  • 支持多个生产者和消费者:它允许一个正在被处理的 item 再次加入队列。

我们建议使用 RateLimitingQueue,它相比普通的 workqueue 多了以下的功能: 

  • 限流:可以限制一个 item 被 reenqueued 的次数。
  • 防止 hot loop:它保证了一个 item 被 reenqueued 后,不会马上被处理。

Workqueue helper:这里有一个 workqueue 的封装,来简化 queue 的操作,代码在以下位置:https://github.com/caicloud/loadbalancer-controller/blob/master/pkg/util/controller/helper.go
控制流总结
我们来总结一个控制器的整体工作流程。
1. 创建一个控制器

  • 为控制器创建 workqueue
  • 创建 informer, 为 informer 添加 callback 函数,创建 lister

2. 启动控制器

  • 启动 informer
  • 等待本地 cache sync 完成后, 启动 workers

3. 当收到变更事件后,执行 callback 

  • 等待事件触发
  • 从事件中获取变更的 Object
  • 做一些必要的检查
  • 生成 object key,一般是 namespace/name 的形式
  • 将 key 放入 workqueue 中

4. worker loop

  • 等待从 workqueue 中获取到 item,一般为 object key
  • 用 object key 通过 lister 从本地 cache 中获取到真正的 object 对象
  • 做一些检查
  • 执行真正的业务逻辑
  • 处理下一个 item

到这里已经讲完了一个完整的 Kubernetes 的 Controller 的构建过程。但是还想要多啰嗦几句关于 kubernetes 的设计原则和 API 习俗,它们是指导我们写出更加可靠的 Controller 的白皮书。
设计原则

  • 功能设计基于 level_based,这意味系统应该在给定的 desired state 和 current/observed state 情况下也能正确运行,不管这中间有多少更新的信息被丢失了。Edge-triggered 只能用来进行优化(应该有一个类似于 CAP 的理论去指导我们权衡应该轮询还是使用事件驱动的方式去控制我们的流程,在高性能,可靠性和简单些三者之间选其二)。
  • 假定我们的系统是一个开放的环境:应该不断的去验证系统假设,优雅地接受外部的事件和修改。比如用户可以随意地删除正在被 replica set 管理的 pods,而 replica set 发现了之后只是简单的重新创建一个新的pod 而已。
  • 不要为 object 建立大而全的状态机,从而把系统的行为和状态机的变迁关联起来。
  • 不要假设所有的组件都能正常运行,任何组件都有可能出错或者拒绝你的请求。etcd 可能会拒绝写入,kubelet 可能会拒绝 pod, scheduler 可能会拒绝调度,尽量进行重试或者有别的解决方案。
  • 系统组件能够自愈:比如说 cache 需要定期的进行同步,这样如果有一些 object 被错误的修改或者存储了, 删除的事件被丢失等问题能够在人类发现之前被自动修复。
  • 优雅地进行降级和熔断,优先满足最重要的功能而忽略一些无关紧要的小错误。

Kubernetes API 习俗 
Spec and status

  • Spec 表示系统希望到达的状态,Status 表示系统目前观测到的状态。
  • PUT 和 POST 的请求中应该把 Status 段的数据忽略掉,Status 只能由系统组件来修改。
  • 有一些对象可能跟 Spec 和 Status 模型相去甚远,可以吧 Spec 改成更加适合的名字。
  • 如果对象符合 Spec 和 Status 的标准的话,那么除了 type,object metadata 之外不应该有其他***的字段。
  • Status 中 phase 已经是 deprecated。因为 pahse 本质上是状态机的枚举类型,它不太符合 Kubernetes 系统设计原则, 并且阻碍系统发展,因为每当你需要往里面加一个新的 pahse 的时候你总是很难做到向后兼容性,建议使用 Condition 来代替。

Primitive types

  • 避免使用浮点数,永远不要在 Spec 中使用它们,浮点数不好规范化,在不同的语言和计算机体系结构中有 不同的精度和表示。
  • 在 Javascript 和其他的一部分语言中,所有的数字都会被转换成 float,所以数字超过了一定的大小最好使 用 string。
  • 不要使用 unsigned integers,因为不同的语言和库对它的支持不一样。
  • 不要使用枚举类型,建立一个 string 的别名类型。
  • API 中所有的 integer 都必须明确使用 Go(int32, int64), 不要使用 int,在32位和64位的操作系统中他们的位数不一样。
  • 谨慎地使用 bool 类型的字段,很多时候刚开始做 API 的时候是 true or false,但是随着系统的扩张,它可能 有多个可选值,多为未来打算。
  • 对于可选的字段,使用指针来表示,比如 *string *int32 , 这样就可以用 nil 来判断这个值是否设置了, 因为 Go 语言中string int 这些类型都有零值,你无法判断他们是没被设置还是被设置了零值。

总结
为 Kubernetes 拓展一个功能,实现一个 controller 是简单的。 但是设计一个系统,抽象出其中的设计哲学,更加值得我们学习和深思。
下面这个项目可以视为 controller 的一个例子:https://github.com/caicloud/loadbalancer-controller
References

  • Client-go:https://github.com/kubernetes/client-go/tree/master/examples
  • Kubernetes controller:https://github.com/kubernetes/kubernetes/tree/master/pkg/controller
  • Ingress controller task queue:https://github.com/kubernetes/ingress/tree/master/core/pkg/task
  • Use client go to extend Kubernetes API — Xu Chao:https://my.oschina.net/caicloud/blog/829365
  • Loadbalacner-controller:https://github.com/caicloud/loadbalancer-controller

推荐阅读
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 移动端常用单位——rem的使用方法和注意事项
    本文介绍了移动端常用的单位rem的使用方法和注意事项,包括px、%、em、vw、vh等其他常用单位的比较。同时还介绍了如何通过JS获取视口宽度并动态调整rem的值,以适应不同设备的屏幕大小。此外,还提到了rem目前在移动端的主流地位。 ... [详细]
  • 全面介绍Windows内存管理机制及C++内存分配实例(四):内存映射文件
    本文旨在全面介绍Windows内存管理机制及C++内存分配实例中的内存映射文件。通过对内存映射文件的使用场合和与虚拟内存的区别进行解析,帮助读者更好地理解操作系统的内存管理机制。同时,本文还提供了相关章节的链接,方便读者深入学习Windows内存管理及C++内存分配实例的其他内容。 ... [详细]
  • php缓存ri,浅析ThinkPHP缓存之快速缓存(F方法)和动态缓存(S方法)(日常整理)
    thinkPHP的F方法只能用于缓存简单数据类型,不支持有效期和缓存对象。S()缓存方法支持有效期,又称动态缓存方法。本文是小编日常整理有关thinkp ... [详细]
  • 在单位的一台4cpu的服务器上部署了esxserver,挂载了6个虚拟机,目前运行正常。在安装部署过程中,得到了cnvz.net论坛精华区 ... [详细]
  • 篇首语:本文由编程笔记#小编为大家整理,主要介绍了SpringCloudRibbon部分源码相关的知识,希望对你有一定的参考价值。1:ribbon是提供通过servi ... [详细]
  • 目录Atlas介绍Atlas部署Atlas基本管理Atlas结合MHA故障恢复读写分离建议Atlas介绍Atlas是由Qihoo360Web平台部基础架构团队开发维护的一个基于My ... [详细]
  • Nginxgaodaima.comnginx属于七层架构,支持的是http协议,本身对tcp协议没有支持。所以不能代理mysql等实现负载均衡。但是lvs这个东西不熟悉,主要是公司 ... [详细]
  • 朱晔的互联网架构实践心得S1E7:三十种架构设计模式(上)【下载本文PDF进行阅读】设计模式是前人通过大量的实践总结出来的一些经验总结和最佳实践。在经过多年的软件开发实践之后,回过头 ... [详细]
  • Kubernetes(k8s)基础简介
    Kubernetes(k8s)基础简介目录一、Kubernetes概述(一)、Kubernetes是什么(二& ... [详细]
  • 本文介绍了在使用Python中的aiohttp模块模拟服务器时出现的连接失败问题,并提供了相应的解决方法。文章中详细说明了出错的代码以及相关的软件版本和环境信息,同时也提到了相关的警告信息和函数的替代方案。通过阅读本文,读者可以了解到如何解决Python连接服务器失败的问题,并对aiohttp模块有更深入的了解。 ... [详细]
  • 本文讨论了Kotlin中扩展函数的一些惯用用法以及其合理性。作者认为在某些情况下,定义扩展函数没有意义,但官方的编码约定支持这种方式。文章还介绍了在类之外定义扩展函数的具体用法,并讨论了避免使用扩展函数的边缘情况。作者提出了对于扩展函数的合理性的质疑,并给出了自己的反驳。最后,文章强调了在编写Kotlin代码时可以自由地使用扩展函数的重要性。 ... [详细]
  • k8s+springboot+Eureka如何平滑上下线服务
    k8s+springboot+Eureka如何平滑上下线服务目录服务平滑上下线-k8s版本目录“上篇介绍了springboot+Euraka服务平滑上下线的方式,有部分小伙伴反馈k ... [详细]
  • ZooKeeper 学习
    前言相信大家对ZooKeeper应该不算陌生。但是你真的了解ZooKeeper是个什么东西吗?如果别人面试官让你给他讲讲ZooKeeper是个什么东西, ... [详细]
  • 这篇文章给大家讲解如何利用dhtmlxGantt在服务器端集成数据。脚本数据保存如果您已初始化dataProcessor,则用户或以编程方式所做的任何更改都将自动 ... [详细]
author-avatar
努力学习的PHP程序员
什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有