kubernetes kube-apiserver源码阅读1启动流程
kubernetes代码版本: v1.20.2
个人认为kube-apiserver
是k8s中最核心的组件,承上启下,无论是k8s其他组件还是是外部客户端都需要跟kube-apiserver
组件进行交互,kube-apiserver
负责接受请求并将数据持久化到后端存储(一般来说就是etcd.)。
总的来说,apiserver就做一件事,就是为k8s资源提供增删改查以及监听的接口,而这件事可以大致分为以下三个方面
- 处理请求前,这部分包括认证, 鉴权, 准入控制
- 处理请求中,这部分包括两个部分
- 内部资源 转发给对应hanlder
- 外部资源 转发给对应的注册方,比如metric server或者健康检查
- 处理请求后,这部分主要包括后端持久化,以及如何响应
因为kube-apiserver
非常核心和重要,所以细节会非常多,并且逻辑也会很复杂,所以不能一上来钻各种细节,而应该从上到下,从外到内,依次阅读,本文只是大致过一下apiserver的启动逻辑。
环境准备
直接下载的的源代码并不会包括自动生成的文件,比如deepcopy, openapi之类的代码文件,这两部份的文件可以通过下面的命令生成
我生成的时候有报错,但是对应的文件还是生成了
生成转换, 拷贝, 设置默认值等函数
go mod vendor && ./hack/update-codegen.sh
可以搜索是否生成了以下文件
zz_generated.conversion.go
zz_generated.deepcopy.go
zz_generated.defaults.go
这些文件会在被导入的时候回在对应的Scheme里注册转换, 拷贝, 设置默认值等函数
生成openapi代码, 如果生成编辑器会报错,说是GetOpenAPIDefinitions找不到之类的。
cp -afr staging/src/k8s.io/code-generator/* vendor/k8s.io/code-generator/
go mod tidy && make gen_openapi ; ls -lh pkg/generated/openapi/
生成的文件应该在pkg\generated\openapi\zz_generated.openapi.go
启动流程
如果不深入kube-apiserver
的初始化细节,kube-apiserver
的启动逻辑还是比较清晰的,代码如下:
func main() {
// 1. 惯用操作了
command := app.NewAPIServerCommand()
if err := command.Execute(); err != nil {
os.Exit(1)
}
}
func NewAPIServerCommand() *cobra.Command {
// 2.
// Options对象
s := options.NewServerRunOptions()
cmd := &cobra.Command{
Use: "kube-apiserver",
RunE: func(cmd *cobra.Command, args []string) error {
verflag.PrintAndExitIfRequested()
fs := cmd.Flags()
cliflag.PrintFlags(fs)
err := checkNonZeroInsecurePort(fs)
// 3.
// 设置默认值
completedOptions, err := Complete(s)
// 检验命令行参数
if errs := completedOptions.Validate(); len(errs) != 0 {
return utilerrors.NewAggregate(errs)
}
// 4.
return Run(completedOptions, genericapiserver.SetupSignalHandler())
},
}
// 添加命令行参数
// 帮助文档显示逻辑
return cmd
}
// 5.
func Run(completeOptions completedServerRunOptions, stopCh <-chan struct{}) error {
// 创建服务链对象
server, err := CreateServerChain(completeOptions, stopCh)
// 启动前准备
prepared, err := server.PrepareRun()
return prepared.Run(stopCh)
}
代码分解如下:
- 创建
cobra.Command
对象, 并执行。cobra的常见操作 - 创建
Options
对象用于绑定命令行参数。k8s惯用模式。 - 运行主逻辑前设置默认值,校验参数。k8s的惯用模式
- 主逻辑入口
- 主逻辑的业务代码
总结起来就是,创建Options对象,解析命令行参数到Options参数,校验,补全Options对象,最后基于Options对象生成server,最后做启动前准备,然后启动。
但是仅是了解到这个层面,显然是无法理解kube-apiserver
的,所以继续深入CreateServerChain
,CreateServerChain
。
CreateServerChain
为了使得k8s易于扩展,所以k8s将扩展的部分和自身的核心部分分离开的,分别叫做apiExtensionsServer
, kubeAPIServer
,那么怎么在两者之间分配请求和做服务发现呢? 那就再引入一个叫做aggregatorServer
的组件。
func CreateServerChain() (*aggregatorapiserver.APIAggregator, error) {
// 1.
kubeAPIServerConfig, serviceResolver, pluginInitializer, err := CreateKubeAPIServerConfig(completedOptions, nodeTunneler, proxyTransport)
// 2.
apiExtensionsConfig, err := createAPIExtensionsConfig()
apiExtensionsServer, err := createAPIExtensionsServer(apiExtensionsConfig, genericapiserver.NewEmptyDelegate())
// 3.
kubeAPIServer, err := CreateKubeAPIServer(kubeAPIServerConfig, apiExtensionsServer.GenericAPIServer)
// 4.
aggregatorConfig, err := createAggregatorConfig()
aggregatorServer, err := createAggregatorServer(aggregatorConfig, kubeAPIServer.GenericAPIServer, apiExtensionsServer.Informers)
return aggregatorServer, nil
}
代码分解如下:
- 基于
Options
对象创建一个新的Config
对象, 这个对象在三个组件中都会用到,所以就先于三者之前创建。 - 创建
apiExtensionsServer
需要的Config
对象, 基于这个Config
对象创建apiExtensionsServer
。 - 创建
kubeAPIServer
- 跟第二步差不多。
这里Options对象和Config对象其实本质功能差不多,都是存储配置信息,但是可能是为了抽象和解耦,所以Options对象会跟命令行参数联系的更紧密,而Config对象在Options对象的基础上创建服务所需的各个对象。
从代码可以知道,apiserver由三个组件组成,apiExtensionsServer
,kubeAPIServer
, aggregatorServer
请求的处理逻辑大致如下:
这里其实有一个疑问,既然是委托调用的这种设计模式,似乎只需要kubeAPIServer
, apiExtensionsServer
两个组件也行,为啥还需要一个aggregatorServer
,这是因为两者只能返回自己所有的可以处理的资源,那么怎么同时获得两者的所有资源呢? 这个问题的解决方案是aggregatorServer
,aggregatorServer
会将两者的资源聚合在一起提供给调用方,调用者一般是发现客户端(看过client-go代码的人应该不陌生)。
PrepareRun
启动流程总结起来就两步,CreateServerChain
, PrepareRun
, 前者创建一个APIAggregator
, 它会将请求发送到后面的 kubeAPIServer
,apiExtensionsServer
, 也负责聚合两者的所有资源, 我们继续看看他的PrepareRun方法
func (s *APIAggregator) PrepareRun() (preparedAPIAggregator, error) {
// 1.
prepared := s.GenericAPIServer.PrepareRun()
return preparedAPIAggregator{APIAggregator: s, runnable: prepared}, nil
}
func (s *GenericAPIServer) PrepareRun() preparedGenericAPIServer {
// 2.
s.delegationTarget.PrepareRun()
s.installHealthz()
s.installLivez()
s.installReadyz()
return preparedGenericAPIServer{s}
}
代码分解如下:
- 调用
GenericAPIServer.PrepareRun()
delegationTarget
就是ServerChain上一个个组件, 会依次传递到kubeAPIServer
,apiExtensionsServer
代码并不是太复杂,就是调用GenericAPIServer.PrepareRun()
, 如果你仔细阅读CreateServerChain
的代码你会发现三个组件都会创建自己的GenericAPIServer
, 它是apiserver中一个非常重要的对象,负责处理请求,而资源对象都是会注册到它上面。
总结
apiserver内部根据设计需要分为三个部分, apiExtensionsServer
,kubeAPIServer
, aggregatorServer
,分别处理不同的逻辑。