组件概述

Kubernetes API Server 通过名为 kube-apiserver 进程提供服务,它的核心功能是提供集群管理 RESTful API 接口,将 API 操作的对象存储到持久化存储到 ETCD 后端,本质是集群中所有用户和组件的数据交互中心。

架构解析

Kubernetes API Server 从上到下可以分为四层:接口层,访问控制层,注册表层和数据库层。

kube-apiserver2

API 层

Kube Core API Server 核心接口服务器

负责对请求的一些通用处理,认证、鉴权等,以及处理各个内建资源的 REST 服务。

API Extensions Server 可扩展接口服务器

主要处理 CustomResourceDefinition(CRD)和 CustomResource(CR)的 REST 请求,也是 Delegation 的最后一环,如果对应 CR 不能被处理的话则会返回 404。

Aggregator Server 聚合服务器

暴露的功能类似于一个七层负载均衡,将来自用户的请求拦截转发给其他服务器,并且负责整个 APIServer 的 Discovery 功能。

访问控制层

访问控制层对用户进行身份鉴权,然后根据配置的各种访问许可逻辑(Admission Controller),判断是否允许访问。当请求到达 kube-apiserver 时,kube-apiserver 首先会执行在 http filter chain 中注册的过滤器链,该过滤器对其执行一系列过滤操作,主要有认证、鉴权等检查操作。

我们以请求正向的方式进行分析,API Server 监听端口的 HTTP 请求会首先打到 FullHandlerChain。

1
2
3
func (a *APIServerHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    a.FullHandlerChain.ServeHTTP(w, r)
}

FullHandlerChain 是由 DefaultBuildHandlerChain 实际构建出来的处理链。

1
2
3
4
5
// staging/src/k8s.io/apiserver/pkg/server/config.go
// filter chain 处理链的注册函数
func DefaultBuildHandlerChain(apiHandler http.Handler, c *Config) http.Handler {
    handler := genericapifilters.WithAuthorization(apiHandler, c.Authorization.Authorizer, c.Serializer)
}

上面代码片段中的 apiHandler 是 director 对象,所以 filter 结束会进入 director 的 http.Handler。导向器 director 根据初始化的配置,如果 goRestfulContainer 的 WebServices 的 RootPath 中是 /apis,或者请求前缀与 RootPath 匹配,则进入 Restful 处理链路,否则进入非 Restful 处理链路。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
func (d director) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    path := req.URL.Path

    for _, ws := range d.goRestfulContainer.RegisteredWebServices() {
        switch {
        case ws.RootPath() == "/apis":
            // ...
            d.goRestfulContainer.Dispatch(w, req)
            return
        case strings.HasPrefix(path, ws.RootPath()):
            // ...
            d.goRestfulContainer.Dispatch(w, req)
            return
        }
    }

    d.nonGoRestfulMux.ServeHTTP(w, req)
}

注册表层

Kubernetes 把所有资源都保存在注册表(Registry)中,针对注册表中的各种资源对象,都定义了相应的资源对象类型,以及如何创建资源,转换不同版本的资源,编码解码资源。

InstallREST 函数在启动时候就已经把路由注册进 goRestfulContainer 中,进入 Dispatch 分发路由,会路由到不同的 endpoint handler 中。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// k8s.io/apiserver/pkg/endpoints/groupversion.go
// 注册路由位置
func (g *APIGroupVersion) InstallREST(container *restful.Container) error {
    prefix := path.Join(g.Root, g.GroupVersion.Group, g.GroupVersion.Version)
    installer := &APIInstaller{
        group:             g,
        prefix:            prefix,
        minRequestTimeout: g.MinRequestTimeout,
    }

    apiResources, ws, registrationErrors := installer.Install()
    versionDiscoveryHandler := discovery.NewAPIVersionHandler(g.Serializer, g.GroupVersion, staticLister{apiResources})
    versionDiscoveryHandler.AddToWebService(ws)
    container.Add(ws)
    return utilerrors.NewAggregate(registrationErrors)
}
1
2
3
4
type DryRunnableStorage struct {
    Storage storage.Interface
    Codec   runtime.Codec
}

ETCD 数据库

通过 KV 持久化存储 Kubernetes 对象,API Server 就是利用 ETCD 的 Watch 特性实现了经典 List-Watch 机制。

流程剖析

Create Pod 流程

当使用 kubectl 运行创建 Pod 的命令,按下回车键以后 kubectl 会先执行一些客户端验证操作,然后封装 HTTP 请求。建立 TLS 建立连接后,会进行认证处理。如果请求认证失败,则请求被拒绝,返回 401 状态码。如果认证成功,则被认证为具体的 username,该用户名可供随后的步骤中使用。,客户端认证方式支持如下:

  • x509 客户端证书;
  • Bearer Token;
  • 基于用户名密码的基本认证;
  • OpenID 认证。

认证成功后,会进行授权处理。Kubernetes 支持多种授权模块,例如 ABAC 模式,RBAC 模式和 Webhook 模式。管理员创建集群时,会配置 API 服务器应用的授权模块。如果多种授权模式同时被启用,Kubernetes 将检查所有模块,如果其中一种通过授权,则请求授权通过。如果所有的模块全部拒绝,则请求被拒绝(HTTP 状态码 403)。

然后进入准入控制处理流程,准入控制模块是能够修改或拒绝请求的软件模块。作为授权模块的补充,准入控制模块会访问被创建或更新的对象的内容。它们作用于对象的创建,删除,更新和连接(proxy)阶段,但不包括对象的读取。与认证和授权模块不同的是,如果任一个准入控制器拒绝请求,那么整个请求会立即被拒绝。除了拒绝请求外,准入控制器还可以为对象设置复杂的默认值。

一旦请求通过所有准入控制器,将使用对应 API 对象的验证流程对其进行验证,然后写入对象存储。

Watch Pod 流程

在 Watch 在进度到注册表层时,前面经过的流程和创建流程一样。首先会解析资源版本,等待 Cacher 对象 ready,通过 newCacheWatcher 初始化对象,进入 Watch 流程。Kubernetes 为了避免后端存储压力,通过缓存资源数据,避免每次都从后端获取,watchCache 是一个固定大小的用于监测缓存数据变化的数组。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
func newCacheWatcher(chanSize int, filter filterWithAttrsFunc, forget func(), versioner storage.Versioner, deadline time.Time, allowWatchBookmarks bool, objectType reflect.Type) *cacheWatcher {
    return &cacheWatcher{
        input:               make(chan *watchCacheEvent, chanSize),
        result:              make(chan watch.Event, chanSize),
        done:                make(chan struct{}),
        filter:              filter,
        stopped:             false,
        forget:              forget,
        versioner:           versioner,
        deadline:            deadline,
        allowWatchBookmarks: allowWatchBookmarks,
        objectType:          objectType,
    }
}

以下代码块是启动 goroutine 进入处理流程,process 接收 input chan 对象中的信息。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
func (c *Cacher) Watch(ctx context.Context, key string, resourceVersion string, pred storage.SelectionPredicate) (watch.Interface, error) {
    // ...
    func() {
        // ...
        c.watchers.addWatcher(watcher, c.watcherIdx, triggerValue, triggerSupported)
        c.watcherIdx++
        // ...
    }()
    // ...
    go watcher.process(ctx, initEvents, watchRV)
	// ...
}

Cacher 初始化的时候会启动,在 dispatchEvents 函数中将每个事件分发到 cacheWatcher 之中。

1
2
3
type Cacher struct {
    incoming chan watchCacheEvent
}

扩展文档

启动参数

  • --client-ca-file 请求到达 Kubernetes API Server 时检查请求的证书。如果它是由 –client-ca-file 引用的文件中的 CA 证书之一签名的,并且用户是公用名 CN= 的值,而组是组织 O= 的取值,则该请求被视为合法请求。请参阅关于 TLS 身份验证的文档
  • --service-account-key-file 参数将对应的公钥传入 kube-apiserver。公钥用于认证过程中的 token 校验。
  • --kubelet-certificate-authority 给 apiserver 提供一个根证书捆绑,用于 kubelet 的服务证书。

经典代码

1
2
3
4
// staging/src/k8s.io/apiserver/pkg/server/genericapiserver.go
// GenericAPIServer 是构造出来的 API Server 对象
type GenericAPIServer struct {
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// staging/src/k8s.io/apiserver/pkg/server/genericapiserver.go
// 完成启动流程
func (s preparedGenericAPIServer) NonBlockingRun(stopCh <-chan struct{}) error {
    // ...
    if s.SecureServingInfo != nil && s.Handler != nil {
        // ...
        stoppedCh, err = s.SecureServingInfo.Serve(s.Handler, s.ShutdownTimeout, internalStopCh)
        // ...
    }
    // ...
}
1
2
3
4
// cmd/kube-apiserver/app/options/options.go  
// NewServerRunOptions 构造了服务运行所有的对象
func NewServerRunOptions() *ServerRunOptions {
}
1
2
# 实现具体存储的接口
pkg/storage/etcd3/store.go
1
2
3
4
5
# Admission Controller 的具体实现位置
k8s.io/apiserver/pkg/admission/initializer/initializer.go

# Admission Controller 各个策略的实现位置
plugin/pkg/admission/*