k8s代码走读---client-go编程交互基础
前言
代码 clone 地址:https://github.com/kubernetes/client-go。实际上在 kubernetes 的源码中也包含了这部分代码:vendor/k8s.io/client-go/
。看代码都没问题。
client-go 是 kubernetes 中比较重要的一个组件,从我上一篇文章中梳理的图中可以看出来,apiserver 是一个核心,其它组件都要和这个核心模块交互,所以 client-go 的出现就是为了统一封装对 apiserver 的交互访问。还是放这个图哈。
*我思故我在*
client-go 这种设计思路还是不错的,当然是适合 kubernetes 这样的项目,几乎所有的模块都在围绕 apiserver,那么和 apiserver 的交互就显的尤为重要,那么这部分代码的抽象封装也就顺理成章了。这种解偶方式也是挺特别的,在看了书,走读了这部分的源码之后也才发现,同样的 client 在使用方式,使用对象不一样,就需要不一样的封装方式。
kubernetes 的 client-go 根据具体使用的方式进行了基础库封装和另外 4 种(书上介绍 3 种,我认为 scale client 也是一种 client)类型的 client 封装,分别针对不同的应用场景。discovery client,dynamic client,clientset,scale client。
今天在公司讨论 sdk 的封装上就讨论到这一点,sdk 的封装很多时候不能过分也不能太简单。太简单,开发体验不好,开发者还有设置或者配置或者开发很多东西,封装太过,封装了很多开发者不想要的内容也很麻烦。所以这样的封装一定要多元化,多层次的来提供。
目录结构
认识代码还是从目录结构开始,我认为 golang 的规范做的非常好,有官方自己的代码规范工具,还有指导的工程代码目录结构规范。这里针对 client-go 我只列出我认为重要的一些目录。
目录名 |
用途 |
---|---|
discovery |
这个是 discovery client 的代码,是对 rest 客户端的进一步封装,用于发现 apiserver 所支持的能力和信息 |
dynamic |
这个是 dynamic client 的代码,是对 rest 客户端的进一步封装,动态客户端,面向处理 CRD |
examples |
这里面有一些例子,比如对 deployment 创建、修改,如何选主,workqueue 如何使用等等 |
informers |
这就是 client-go 中非常有名的 informer 机制的核心代码 |
kubernetes |
clientset 的代码,也是对 rest 客户端的进一步封装,提供复杂的资源访问和管理能力 |
listers |
为每个 k8s 资源提供 lister 功能,提供了只读缓存功能 |
metadata |
|
pkg |
主要是一些功能函数,比如版本函数 |
rest |
这是最基础的 client,其它的 client 都是基于此派生的 |
scale |
scale client 的代码 |
tools |
工具函数库,主要是和 k8s 相关的工具函数 |
util |
通用的一些工具函数 |
transport |
提供安全 tcp 链接 |
rest client 核心数据结构
首先还是从核心数据结构走起:
type RESTClient struct {
// 这个初始化的 apiserver 的地址,下面我也贴了一个 kubeconfig 文件的内容,这个地址就是 cluster 的 server。
base *url.URL
// 这个是 apiVersion
versionedAPIPath string
// 对客户端编解码的设置
content ClientContentConfig
// creates BackoffManager that is passed to requests.
createBackoffMgr func() BackoffManager
// 限流控制,是针对这个客户端的所有请求的。这个也是非常好的一个设计,一般 sdk 的设计很少考虑这个,大多数只考虑功能
rateLimiter flowcontrol.RateLimiter
// warningHandler is shared among all requests created by this client.
// If not set, defaultWarningHandler is used.
warningHandler WarningHandler
// http 请求客户端
Client *http.Client
}
clinet-go 启动配置文件
客户端启动是需要配置文件的,要知道要去链接那个 apiserver,使用那个账户信息等等。这个配置文件保存在 ~/.kube/config
。我机器上的格式如下:
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: xxx
server: https://192.168.1.10:60002
name: local
contexts:
- context:
cluster: local
user: admin
name: master
current-context: master
kind: Config
preferences: {}
users:
- name: admin
user:
token: xxx
主要分为 clusters,contexts,users,用户保存集群信息,可以支持多个集群,多个账户访问使用,每个集群和用户有自己独立的鉴权数据或是 token。
client 封装
client-go 的 rest client 实际上在很多时候使用起来还是不方便的,而且在逐步的发展中 对 client 的要求会有多种多样,所以针对这一需求 client-go 又进行了抽象和封装,把常用到的 client 类型进行专门封装,形成独立的库,以方便直接使用,这样避免了直接使用基础 rest client 时又要进行再次编码封装。目前形成的常用到的封装 client 有这样几个:discovery client,dynamic client,clientset 和 scaleClient,scaleClient 在《kubernetes源码剖析》这本书中没有提到,但是我看了 1.14 版本的代码中是有这个的实现和介绍的。在它的代码介绍中是这样介绍的:
// scaleClient is an implementation of ScalesGetter
// which makes use of a RESTMapper and a generic REST
// client to support an discoverable resource.
// It behaves somewhat similarly to the dynamic ClientPool,
// but is more specifically scoped to Scale.
// 简单翻译最后一句话:它的功能有点像 dynamic ClientPool,但是它更聚焦在扩展能力上。
这种封装在一定程度上是可以提升开发者体验的,对开发者比较好,很多代码直接由官方来提供,而不用开发者自己再写。避免了直接使用裸的基础 client。但是这种封装也一定得有限制不能无限多,不然维护成本会很高。
后记
针对这几种客户端,我后面想写一些例子来实际跑一跑,目前只是简单的认识这些 client。当然源代码中也有例子可以参考学习,目前由于我的时间问题只是走读了代码,没有对官方的源码例子进行编译执行。实际上公司内有同学开发了一个对 k8s 事件收集的模块,这个模块的代码我也是参与开发了一部分,从编译执行上来说是没问题,之前是没有从 client-go 这部分代码上去理解,现在感觉对这部分代码的认识更深了一些。
- 神经网络
- Node.js Leap Motion Hello World——开启AR的小窗
- 微信小游戏:无法进行网络请求的解决方案
- 微信跳一跳之深度实践
- 前端工程师在业余时间如何提高自身能力——造轮子
- 我是如何Hack掉一个机器人!
- SliferMenu详解
- Android 深入ViewPager补间动画,实现类京东商城首页广告Banner切换效果
- android抓屏
- android 仿qq手写板涂鸦
- listview动态获取数据
- toggbutton
- android软件开发之webView.addJavascriptInterface循环渐进【一】
- android的listview item点击详解
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 【LeetCode两题选手】算法类题目(7.29)
- 【LeetCode两题选手】算法类题目(7.27)
- 【奇技淫巧】-- 走地图的不同路径
- 【奇技淫巧】-- 搜索旋转数组
- 【奇技淫巧】-- 原地旋转链表
- 【奇技淫巧】-- 盛水最多的容器
- 【leetcode两题选手】MySQL类题目(八)
- 【leetcode两题选手】MySQL类题目(七)
- 【leetcode两题选手】MySQL类题目(六)
- 【leetcode两题选手】MySQL类题目(五)
- 【leetcode两题选手】MySQL类题目(四)
- 【leetcode两题选手】MySQL类题目(三)
- 【leetcode两题选手】MySQL类题目(二)
- 剑指offer(01-03)题解
- mybatis动态sql之分支选择(学习choose、when、otherwise标签)