kube-apiserver源码剖析与开发(一):架构剖析

1,163 阅读7分钟

我们知道 k8s 存储资源对象使用的是 etcd,kube-apiserver 作为 k8s 的核心组件之一最重要的作用就是跟 etcd 交互。在 k8s 中 对资源的查询、创建、修改等操作的唯一入口就是 kube-apiserver,由 kube-apiserver 负责调用 etcd 的接口进行增删改查操作,所以我们可以把 kube-apiserver 看成一个数据流通的枢纽,也可以看成一个“api网关”。

为了能够妥善处理自客户端的请求,kube-apiserver 做了一些列的工作,具体如下图(忽略了一些非关键路径步骤,如日志打印等):

pCVvjH0.png

  • 认证 确认请求者的身份,判断是不是集群内的合法用户
  • 审计 如果配置了审计的相关参数(如审计日志写入后端),则会对应请求做审计日志记录
  • 限速 根据 PriorityAndFairness 或 MaxInFlight 判断是否要做限速该请求
  • 鉴权 判断请求者是否有权限进行该操作(需要和认证区别开来)
  • 准入 准入分 Mutating 和 Validating
    • Mutating:对请求做变更,如加 annotation 之类的操作
    • Validating:验证请求的请求体是否合法如参数校验之类的工作或者是否满足其他一些要求如配额等
  • 操作ETCD 调用 ETCD 接口,获取数据或者写入数据

上面我们是从 kube-apiserver 对一个请求具体处理流程出发来说明它的大体架构的,下面我们再通过一个请求在 kube-apiserver 中的数据流向来说明它的架构

pCZEG4A.png

实际上,kube-apiserver 并不是一个单独的 server,而是由 AggregatorServer、KubeAPIServer、APIExtensionsServer 三者串联组成的。AggregatorServer 是所有请求的入口,当 AggregatorServer 可以处理该请求就由 AggregatorServer 请求,如果没有匹配的路由,那么这个请求就会被转发给下游 KubeAPIServer 或 UserServer(用户扩展的api服务,后面会细说)。

请求到了 KubeAPIServer,如果 KubeAPIServer 能够处理请求则直接处理,否则就把请求转发给 APIExtensionsServer。

APIExtensionsServer 就是最后一个 server 了,如果他能处理请求,他就处理了,否则就只能返回给用户 404 Not Found了。

上述这种转发的行为在 kube-apiserver 中叫委派(delegate),即自己处理不了的事情就交给别人来处理,各司其职。

这一点我们可以从初始化各个 Server 的源码中看的到

func CreateServerChain(completedOptions completedServerRunOptions) (*aggregatorapiserver.APIAggregator, error) {
	kubeAPIServerConfig, serviceResolver, pluginInitializer, err := CreateKubeAPIServerConfig(completedOptions)
	if err != nil {
		return nil, err
	}

	// If additional API servers are added, they should be gated.
	apiExtensionsConfig, err := createAPIExtensionsConfig(*kubeAPIServerConfig.GenericConfig, kubeAPIServerConfig.ExtraConfig.VersionedInformers, pluginInitializer, completedOptions.ServerRunOptions, completedOptions.MasterCount,
		serviceResolver, webhook.NewDefaultAuthenticationInfoResolverWrapper(kubeAPIServerConfig.ExtraConfig.ProxyTransport, kubeAPIServerConfig.GenericConfig.EgressSelector, kubeAPIServerConfig.GenericConfig.LoopbackClientConfig, kubeAPIServerConfig.GenericConfig.TracerProvider))
	if err != nil {
		return nil, err
	}

	notFoundHandler := notfoundhandler.New(kubeAPIServerConfig.GenericConfig.Serializer, genericapifilters.NoMuxAndDiscoveryIncompleteKey)
	// 创建 APIExtensionsServer
	apiExtensionsServer, err := createAPIExtensionsServer(apiExtensionsConfig, genericapiserver.NewEmptyDelegateWithCustomHandler(notFoundHandler))
	if err != nil {
		return nil, err
	}
    // 创建 KubeAPIServer
	kubeAPIServer, err := CreateKubeAPIServer(kubeAPIServerConfig, apiExtensionsServer.GenericAPIServer)
	if err != nil {
		return nil, err
	}

	// aggregator comes last in the chain
	aggregatorConfig, err := createAggregatorConfig(*kubeAPIServerConfig.GenericConfig, completedOptions.ServerRunOptions, kubeAPIServerConfig.ExtraConfig.VersionedInformers, serviceResolver, kubeAPIServerConfig.ExtraConfig.ProxyTransport, pluginInitializer)
	if err != nil {
		return nil, err
	}
	// 创建 AggregatorServer
	aggregatorServer, err := createAggregatorServer(aggregatorConfig, kubeAPIServer.GenericAPIServer, apiExtensionsServer.Informers)
	if err != nil {
		// we don't need special handling for innerStopCh because the aggregator server doesn't create any go routines
		return nil, err
	}

	return aggregatorServer, nil
}

可以看到,这三个 Server 创建的顺序分别是:APIExtensionsServer、KubeAPIServer、AggregatorServer,这个创建顺序的原因是前一个 Server 创建完后,需要被作为参数传递给后面一个 Server 的创建函数,而需要这个参数的原因就是当前 Server 处理不了请求需要委派给谁,而这个委派对象就是该参数。所以根据上图和源码我们能够看到,请求在这个三个链上的传递顺序和这三个 Server 的初始化正好相反。

那么,这三个 Server 的作用都是干嘛的呢?

先说结论:AggregatorServer 和 APIExtensionsServer 是为了 k8s 扩展而生的,但是这两种扩展又有很大的区别;KubeAPIServer 则是为集群内置核心 api 提供服务的。

  • AggregatorServer

我们先看一下官方文档是如何来描述的

The aggregation layer allows Kubernetes to be extended with additional APIs, beyond what is offered by the core Kubernetes APIs. The additional APIs can either be ready-made solutions such as a metrics server, or APIs that you develop yourself. The aggregation layer is different from Custom Resources, which are a way to make the kube-apiserver recognise new kinds of object.

github readme 的描述

  • an API for registering API servers.
  • Summaries of discovery information from all the aggregated APIs
  • HTTP proxying of requests from clients on to specific API backends

简单来说,它最重要的作用就是提供API扩展和API请求转发能力。

它将 APIServer 这个巨石(monolithic)拆分,用户可以开发自己的 APIServer 集成进来,而不用直接修改 Kubernetes 官方仓库的代码,这样一来也能将 API server 解耦,方便用户使用实验特性,简而言之,它是允许k8s的开发人员编写一个自己的服务,可以把这个服务注册到k8s的api里面,这样,就像k8s自己的api一样,自定义的服务只要运行在k8s集群里面,k8s 的Aggregate通过service名称就可以转发到我们自定义的service里面去了。这些 APIServer 可以跟 kube-apiserver 无缝衔接,使用 kubectl 也可以管理它们。

有人可能会问,我可以开发一个 web server,直接以 deployment 的方式部署在集群内,再创建一个 svc 就能对外提供服务,为什么还需要 AggregatorServer 呢?

确实你可以这么做,但是 AggregatorServer 可以让我们使用 k8s 提供一些的先天能力,如在上面介绍一个请求被处理的过程中需要做认证、鉴权、审计等工作,如果将自己的服务注册成了 apiservice,那么就可以使用这些能力了。

还有比较重要的一点就是,可以把你的API风格跟k8s的API规范保持一致。

举个例子,如果你开发了一个自己的服务,且注册到了 apiservice ,

apiVersion: apiregistration.k8s.io/v1
kind: APIService
metadata:
  name: v1beta1.my.api.k8s.io
  labels: my-apiserver
    apiserver: "true"
spec:
  version: v1beta1 
  group: v1beta1.my.api.k8s.io
  service:
    name: my-apiserver
    namespace: default
  caBundle: ""

其中的service 就是你的服务对应集群内service的名字,那么你就可以以下面的方式访问你的 api 了

curl https://kube-apiserver-host:port/apps/v1beta1/my.api.k8s.io/...

当你这么访问的时候,AggregatorServer 就会把请求转发到 service 为 my-apiserver 的服务后端了。

  • KubeAPIServer 这个就比较好理解了,它提供的是 k8s 内置的那些核心的 api 服务,如创建 deployment,创建 node 等等,都是走到这个 server。

  • APIExtensionsServer

我们也来看看官方描述

It provides an API for registering CustomResourceDefinitions.

所以 APIExtensionsServer 就是提供 api 用来注册 CustomResourceDefinitions 的 server。

那么 CustomResourceDefinitions 是什么?

例如 Pod,Node,Deployment 等都是 k8s 内置资源类型(Kind),如果用户想创建一个自定义的资源,那么就可以通过 APIExtensionsServer 提供的 api 来注册。

例如,下面这个定义就是一个对 Fruit 资源类型的定义,在本质跟 Pod 是类似的,有了这个定义后,它也是 k8s 中也是一种资源,这种定义在 k8s 中也叫 crd。

apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  # metadata.name的内容是由"复数名.组名"构成,如下,fruits是复数名,food.k8s.io是分组名
  name: fruits.food.k8s.io
spec:
  # 分组名,在REST API中也会用到的,格式是: /apis/组名/版本
  group: bolingcavalry.k8s.io
  versions:
    - name: v1
      storage: true
  scope: Namespaced
  names:
    # 复数名
    plural: fruit
    # 单数名
    singular: fruits
    # 类型名
    kind: Fruit
    # 简称
    shortNames:
    - fru

现在你可以使用 kubectl 像操作 Pod 那样操作 Fruit 资源了

kubectl get fruit
kubectl delete fruit

不过现在只有资源的定义,相当于告诉了 k8s 帮我添加一个 Fruit 类型的资源类型,如果你要真正能够使用这个资源,就得创建这个资源类型的实例,然后还需要编写控制器代码去监听实例,监听到实例变化后去做一些实质性的事,就像 k8s 的 controller-manager 监听 deployment 的变化来创建Pod,我们需要一个控制器来监听自定义资源的变化。这部分内容和开发后面会专门有篇章说明。

很显然,APIExtensionsServer 也是为了扩展而生的,但是该扩展跟 AggregatorServer 的扩展是不一样的,APIExtensionsServer 的扩展是为了用户能够新增集群的资源类型;而 AggregatorServer 的扩展是为了新增 api 供用户调用,它并不能在集群内新增资源类型。