kubernetes代码阅读-apiserver基础篇

本文涉及的产品
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
简介: apiserver是整个kubernetes的核心模块,做的事情多,代码量也较大。市面上已经有不少apiserver代码解读的文章了,但问题在于,由于k8s的代码变化很快,想写一篇长久能用的未必能做到。

apiserver是整个kubernetes的核心模块,做的事情多,代码量也较大。市面上已经有 不少apiserver代码解读的文章了,但问题在于,由于k8s的代码变化很快,想写一篇长久能用的未必能做到。所以,我参照了《Kubernetes权威指南》和浙大SEL实验室的一些文章,先把我看到的东西记下来,待后观是否有用。

kubernetes源代码版本1.2.0

代码阅读方法

先简单讲讲整个代码的目录结构

目录 说明
api 输出接口文档用
build 构建脚本
cluster 适配不同I层的云,例如亚马逊AWS,微软Azure,谷歌GCE的集群启动脚本
cmd 所有的二进制可执行文件入口代码,例如apiserver/scheduler/kubelet
contrib 项目贡献者
docs 文档,包括了用户文档、管理员文档、设计、新功能提议
example 使用案例
Godeps 项目中依赖使用的Go第三方包,例如docker客户端SDK,rest等
hack 工具箱,各种编译、构建、测试、校验的脚本都在这里面
hooks git提交前后触发的脚本
pkg 项目代码主目录,cmd的只是个入口,这里是所有的具体实现
plugin 插件,k8s认为调度器是插件的一部分,所以调度器的代码在这里
release 应该是Google发版本用的?
test 测试相关的工具
third_party 一些第三方工具,应该不是强依赖的?
www UI,不过已经被移动到新项目了

可以看到,关键实现代码都放在pkg这个目录下。对于apiserver这种跨度很广的组件而言,唯一有效的阅读方式估计就是

  1. 遍历pkg下所有的目录,概览大概知道这个目录是干啥的
  2. 从cmd这个入口来看apiserver的代码,然后一点点由浅入深,看apiserver的大致实现
  3. 分特性,看具体某个大的特性是怎么实现的,例如安全,例如和etcd存储对接
  4. 在上面这几步的过程中可以看看别人的代码阅读文档,能有效的节省时间

0. apiserver主要实现了什么?

输入图片说明

apiserver是k8s系统中所有对象的增删查改盯的http/restful式服务端,其中盯是指watch操作。数据最终存储在分布式一致的etcd存储内,apiserver本身是无状态的,提供了这些数据访问的认证鉴权、缓存、api版本适配转换等一系列的功能。

  • restful服务入门

对于http服务和使用go语言实现方式,可以看go-restful的文档例子,对这个有基本的了解,这个文档对入门者和一知半解者极为有效!

1. 对象的数据结构

输入图片说明

古人有言,程序就是算法+数据结构,搞懂了数据结构,整个程序的处理过程就明白了一半。对于apiserver的任何一个api请求来说,上图说明了所有的数据结构关系。

k8s放在etcd内的存储对象是api.Pod对象(无版本),从不同版本的请求路径标识来操作,例如api/v1,最后获取到的是不同版本,例如v1.Pod的json文本。这里就经历了几个过程,包括

  1. http client访问/api/v1/pod/xyz,想要获取这个Pod的数据
  2. 从etcd获取到api.Pod对象
  3. api.Pod对象转换为v1.Pod对象
  4. v1.Pod对象序列化为json或yaml文本
  5. 文本通过http的response体,返回给http client

其中用于处理业务数据的关键数据结构是APIGroupVersion,里面的几个成员变量的作用是:

成员 作用
GroupVersion 包含 api/v1这样的string,用于标识这个实例
Serializer 对象序列化和反序列化器
Converter 这是一个强大的数据结构,这里放的是个接口,本体在/pkg/conversion/conversion.go,几乎可以转换任意一种对象到另一种,只要你事先注入了相应的转换函数
Storage 这个map的key,用于对象的url,value是一个rest.Storage结构,用于对接etcd存储,在初始化注册时,会把这个map化开,化为真正的rest服务到存储的一条龙服务

2. 入口和启动

文件 主要数据结构/函数 用途
kubernetes/cmd/kube-apiserver/apiserver.go 入口
kubernetes/cmd/kube-apiserver/app/options/options.go structAPIServer 启动选项
kubernetes/cmd/kube-apiserver/apiserver.go func Run 初始化一些客户端、启动master对象
kubernetes/pkg/genericapiserver/genericapiserver.go func Run 启动安全和非安全的http服务

3. API分组、多版本的初始化注册(Rest)

输入图片说明

k8s采用ApiGroup来管理所有的api分组和版本升级,目前有的API分组包括

  1. 核心组,REST路径在 /api/v1 ,但这个路径不是固定的,v1是当前的版本。与之相对应的代码里面的apiVersion 字段的值是v1
  2. 扩展组,REST路径在 /apis/extensions/$VERSION,相对应的代码里面的 apiVersion: extensions/$VERSION (例如当前的apiVersion: extensions/v1beta1)。这里提供的API对象今后有可能会被移动到别的组内。
  3. "componentconfig"和 "metrics"这这些组。

这个文档里面讲述了实现ApiGroup的几个目标,包括api分组演化,对旧版API的向后兼容(Backwards compatibility),包括用户可以自定义自己的api等。接下来我们看看他么是怎么初始化注册的,这里都是缩减版代码,去掉了其他部分。

kubernetes/pkg/master/master.go
  • api注册入口
func New(c *Config) (*Master, error) {
	m.InstallAPIs(c)
}
  • 根据Config往APIGroupsInfo内增加组信息,然后通过InstallAPIGroups进行注册
func (m *Master) InstallAPIs(c *Config) {
	if err := m.InstallAPIGroups(apiGroupsInfo); err != nil {
		glog.Fatalf("Error in registering group versions: %v", err)
	}
}
  • 转换为APIGroupVersion这个关键数据结构,然后进行注册
func (s *GenericAPIServer) installAPIGroup(apiGroupInfo *APIGroupInfo) error {
		apiGroupVersion, err := s.getAPIGroupVersion(apiGroupInfo, groupVersion, apiPrefix)

		if err := apiGroupVersion.InstallREST(s.HandlerContainer); err != nil {
			return fmt.Errorf("Unable to setup API %v: %v", apiGroupInfo, err)
		}
}		
  • 关键数据结构
kubernetes/pkg/apiserver/apiserver.go
type APIGroupVersion struct {
	Storage map[string]rest.Storage

	Root string // GroupVersion is the external group version
	GroupVersion unversioned.GroupVersion
}

实际注册的Storage的map如下:

kubernetes/pkg/master/master.go
	m.v1ResourcesStorage = map[string]rest.Storage{
		"pods": podStorage.Pod,
		"pods/attach": podStorage.Attach,
		"pods/status": podStorage.Status,
		"pods/log": podStorage.Log,
		"pods/exec": podStorage.Exec,
		"pods/portforward": podStorage.PortForward,
		"pods/proxy": podStorage.Proxy,
		"pods/binding": podStorage.Binding,
		"bindings": podStorage.Binding,

那么,这里的map[string]rest.Storage最后是怎么变成一个具体的API来提供服务的呢?例如这么一个URL:

GET /api/v1/namespaces/{namespace}/pods/{name}
  • restful服务的实现

k8s使用的一个第三方库github.com/emicklei/go-restful,里面提供了一组核心的对象,看例子

数据结构 功能 在k8s内的位置
restful.Container 代表一个http rest服务对象,包括一组restful.WebService genericapiserver.go - GenericAPIServer.HandlerContainer
restful.WebService 由多个restful.Route组成,处理这些路径下所有的特殊的MIME类型等 api_installer.go - NewWebService()
restful.Route 路径——处理函数映射map api_installer.go - registerResourceHandlers()
  • 实际注册过程
kubernetes/pkg/apiserver/api_installer.go
func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storage, ws *restful.WebService, proxyHandler http.Handler) (*unversioned.APIResource, error) {
}

最终的API注册过程是在这个函数中完成的,把一个rest.Storage对象转换为实际的getter, lister等处理函数,并和实际的url关联起来。

4.etcd存储的操作(ORM)

上面已经基本厘清了从http请求 -> restful.Route -> rest.Storage这条线路,那rest.Storage仅仅是一个接口,有何德何能,可以真正的操作etcd呢?

输入图片说明

这段也是牵涉到多个文件,但还比较清晰,首先,所有的对象都有增删改查这些操作,如果为Pod单独搞一套,Controller单独搞一套,那代码会非常重复,不可复用,所以存储的关键目录是在这里:

kubernetes/pkg/registry/generic/etcd/etcd.go

这个文件定义了所有的对etcd对象的操作,get,list,create等,但具体的对象是啥,这个文件不关心;etcd客户端地址,这个文件也不关心。这些信息都是在具体的PodStorage对象创建的时候注入的。以Pod为例子,文件在:

kubernetes/pkg/registry/pod/etcd/etcd.go

这里的NewStorage方法,把上述的信息注入了etcd里面去,生成了PodStorage这个对象。

// REST implements a RESTStorage for pods against etcd
type REST struct {
	*etcdgeneric.Etcd
	proxyTransport http.RoundTripper
}

由于PodStorage.Pod是一个REST类型,而REST类型采用了Go语言的struct匿名内部成员,天然就拥有Get, List等方法。

kubernetes/pkg/apiserver/api_installer.go

最后在这里把PodStorage转换成了Getter对象,并最终注册到ApiGroup里面去。

本文转移开源中国-kubernetes代码阅读-apiserver基础篇

相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
相关文章
|
5月前
|
缓存 Kubernetes API
K8S 性能优化 - K8S APIServer 调优
K8S 性能优化 - K8S APIServer 调优
|
2月前
|
Kubernetes 负载均衡 监控
在K8S中,apiserver的高可用是如何实现的?
在K8S中,apiserver的高可用是如何实现的?
|
5月前
|
Kubernetes 搜索推荐 应用服务中间件
通过keepalived+nginx实现 k8s apiserver节点高可用
通过keepalived+nginx实现 k8s apiserver节点高可用
319 17
|
5月前
|
存储 Kubernetes Cloud Native
云原生|kubernetes|apiserver审计日志的开启
云原生|kubernetes|apiserver审计日志的开启
169 0
|
Prometheus Kubernetes 监控
Kubernetes APIServer 内存爆满分析
董江,容器技术布道者及实践者,中国移动高级系统架构专家,曾担任华为云核心网技术专家,CloudNative社区核心成员,KubeServiceStack社区发起者,Prometheus社区PMC,Knative Committer,Grafana社区Contributer。 欢迎关注:https://kubeservice.cn/
Kubernetes APIServer 内存爆满分析
|
存储 JSON Kubernetes
一文窥探 Kubernetes ApiServer
Hello folks, 作为 Kubernetes 编排生态中最重要的核心组件之一,kube-apiserver 用于集群管理的 REST API 接口,包括身份验证和授权、数据验证和集群状态更改等以及其他模块之间数据交互和通信的枢纽。
97 0
|
JSON Kubernetes 数据格式
【Kubernetes】开发中与 APIServer 常见的几种认证方式
介绍了几种在开发过程中与 APIServer 交互的认证方式
916 0
【Kubernetes】开发中与 APIServer 常见的几种认证方式
|
存储 Kubernetes API
kubernetes代码阅读-apiserver之list-watch篇
apiserver的list-watch代码解读 list-watch,作为k8s系统中统一的异步消息传递方式,对系统的性能、数据一致性起到关键性的作用。今天我想从代码这边探究一下list-watch的实现方式。
1490 0
|
存储 Kubernetes API
kubernetes代码阅读-apiserver之list-watch篇
apiserver的list-watch代码解读 list-watch,作为k8s系统中统一的异步消息传递方式,对系统的性能、数据一致性起到关键性的作用。今天我想从代码这边探究一下list-watch的实现方式。
6006 0
|
21天前
|
Kubernetes Cloud Native 云计算
云原生之旅:Kubernetes 集群的搭建与实践
【8月更文挑战第67天】在云原生技术日益成为IT行业焦点的今天,掌握Kubernetes已成为每个软件工程师必备的技能。本文将通过浅显易懂的语言和实际代码示例,引导你从零开始搭建一个Kubernetes集群,并探索其核心概念。无论你是初学者还是希望巩固知识的开发者,这篇文章都将为你打开一扇通往云原生世界的大门。
90 17