1. kube-apiserver(@v1.17.2
)
[TOC]
ApiServer
是K8S
服务调度的核心,其同时负责与外界的交互、集群内其他组件的交互,也是K8S
中唯一与持久化存储Etcd
交互的组件。
下面将从以下几点来解析 ApiSever
:
- 启动过程
Scheme
构建过程- 路由构建过程
- 认证、授权、准入构建过程
- 请求过程
- 读写请求过程
- watch机制
- 其他组件与其交互过程
- informerFactory机制
1. 启动过程
1.1 启动主流程
ApiServer
启动位置:cmd/kube-apiserver/apiserver.go
,启动先需要先启动一个Etcd
。调试启动参数如下:
–secure-port=6445 –storage-backend=etcd3 –etcd-servers=http://127.0.0.1:2379 –enable-swagger-ui=true
首先创建ApiServerCommond
,然后执行命令:(整个k8s
代码体系都引用CLI
框架Cobra
)
创建命令的过程:
- 创建默认服务启动参数
ServerRunOption
; - 通过
Cobra
获取程序启动参数并应用到ServerRunOption
。
执行的过程,如下:
- 首先设置默认参数:
apiserver
的AdvertiseAddress
(ip),apiserver
的SericeIp
、集群里所有Service
Ip范围、授权配置、服务账户配置、etcd缓存配置、api开关配置; - 校验启动参数:如未配置
Etcd
服务节点信息,则返回异常,终止启动。 - 基于
ServerRunOption
运行服务(核心流程)- 创建服务链:创建
APiServer
、ApiExtensionServer
、AggregatorServer
,建立非安全服务端口(默认8080
)提供服务; - 预运行:(安装健康检查探针、存活探针、就绪探针。装配
OpenAPI
(/openapi/v2
路径)); - 运行:运行审计后台(
AuditBackend
),建立安全服务端口(默认6445
)监听提供服务。
- 创建服务链:创建
因此,启动过程最重要的内容在:CreateServerChain
创建服务链中。
其过程如下:
- 创建节点拨号器组件(
NodeTunneler
),设置SSH
等信息,对于不同的云服务提供商其区别即在于AddSSHKeyToAllInstances
:sshKey
分发函数,将SSH
key分发到所有节点上; - 创建
APIServer
配置(CreateKubeAPIServerConfig
):创建为运行APIServer所需要的资源。将ServerRunOption
中配置转换成其所需要的资源。- 构建
apiserver
配置genericapiserver.Config(legacyscheme.Codecs)
,legacyscheme.Codecs
是ApiServer
核心设计,其用于编解码API
资源,后面讲重点讲解- 设置
BuildHandlerChainFunc
,其是ApiServer
的拓展机制。每次请求到达后台处理逻辑之前,会先运行处理链逻辑
,目前其默认实现server.DefaultBuildHandlerChain
见 ^附件1 。其内部包含认证
、授权
、审计
、准入
等处理逻辑。
- 设置
MergedResourceConfig
,其用于表示配置哪些groupVersion
被开启,哪个资源被启用和禁用; - 将服务启动
ServerRunOption
的部分参数应用到genericapiserver.Config
中; - 根据
ServerRunOption
中的InsecureServing
、SecureServing
、Authentication
、Features
、APIEnablement
应用到genericapiserver.Config
中; - 创建
OpenAPI
配置; - 创建存储工厂
storageFactory
,并聚合Etcd
的配置创建genericapiserver.Config
需要的组件RESTOptionsGetter
(其用于构建RESTStorage
,用来访问存储层)storageFactory
的创建过程存在很多细节:StorageConfig
:存储配置Overrides
:覆盖设置,根据EtcdServersOverrides
转换而得,其作用是对于某种特定资源转换到其他etcd
服务上;ResourceEncodingConfig
:用来判断对于某种资源使用什么版本标识(存储的版本、内存中表示的版本);DefaultSerializer
:序列化工具legacyscheme.Codecs
;APIResoureceConfigSource
:存储哪些版本哪些资源可用或被禁用。
- 创建
Clientset
(包含请求所有资源的Client
),其用来请求自己来获取各种类型资源; - 创建
SharedInformerFactory
,其用来为所有已知api组版本的资源提供Informer
(k8s
体系中与apiserver
交互的重要组件); - 构建认证器
Authenticator
- 跟据
ServerRunOption
创建authenticatorConfig
- 创建
ServiceAccountTokenGetter
,用于获取ServiceAccount
、Secrets
,Pod
; - 创建
BootstrapTokenAuthenticator
,; - 构建多个认证器,并包装成一个联合认证插件,目前
k8s
支持如下认证插件(authenticator/config.New
):- basic auth
X509证书
;ServiceAccount
- 静态
Token
文件 - 引导
Token
OpenID
WebHook
- 跟据
- 创建授权工具
Authorizer
,目前k8s
支持6种授权模式:ModeNode
:显示kubelet
只能访问其自身所在的Node
ModeRBAC
:基于角色的访问控制ModeABAC
:基于属性(Attribute
)的访问控制ModeWebhook
:通过webhook的访问控制ModeAlwaysDeny
:拒绝所有ModeAlwaysAllow
:允许所有
- 创建服务调节器
ServiceResolver
给定命名空间、名称、端口返回Service
的URL; - 创建审计策略检测器
AuditPolicyChecker
,审计后台AuditBackend
; - 创建准入控制
AdmissionControl
k8s
目前支持13种准入插件:NamespaceLifecycle,LimitRanger,ServiceAccount,TaintNodesByCondition,Priority,DefaultTolerationSeconds,DefaultStorageClass,StorageObjectInUseProtection,PersistentVolumeClaimResize,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,RuntimeClass,ResourceQuota
;- 插件初始化时会用
PluginInitializers
来对所有的插件进行填充,这也是k8s
设计比较巧妙的点;- 插件需要什么属性,实现相应接口即可。
- 解析
ServiceIpRange
和apiServerServiceIp
; - 创建
master.Config
,并进行赋值返回。
- 构建
- 构建配置
APIExtensionsConfig
,为创建ApiExtensionServer
做准备,ApiExtensionServer
和KubeAPIServer
的区别在于,ApiExtensionServer
为k8s
的拓展服务,其负责API:CustomResourceDefinitions
(简称CRD
)的管理; - 创建
apiExtensionsServer
,首先填充参数再创建:- 创建
GenericAPIServer
- 创建
APIServerHandler
(apiserver/pkg/server/handler.go
),其是众多Hanlder
的包装器和分发器- 其包含属性:
FullHandlerChain
(过滤链)、GoRestfulContainer
、NOGoRestfulMux
、Director
; - 其处理请求的顺序:
FullHandlerChain -> Director -> {GoRestfulContainer,NonGoRestfulMux}
- 其包含属性:
- 创建
GenericAPIServer
,填充参数
- 创建
- 创建
ApiGroupInfo
,标识API
组,包含组内元素的所有数据,其包含主要信息如下:PrioritizedVersions
:标识该API
组有哪些版本;VersionedResourcesStorageMap
:存储API
组中每个版本的每种资源的存储API;Scheme
:包含该组内所有类型,并且可以做不同类型之间的相互转换,同时可以转换来自组外的对象;NegotiatedSerializer
:API
组内数据编解码;ParameterCodec
:查询参数转换
- 填充
apiGroupInfo
- 填充
VersionedResourcesStorageMap
- 填充
- 安装
APIGroupInfo
- 安装
APIResources
- 对于每个版本,首先创建
APIGroupVersion
(用于将存储对象暴露为http.Handlers
的helper
)(每个APIGroupVersion
是一个WebService
) - 调用
apiGroupVersion.InstallREST(s.Handler.GoRestfulContainer)
方法安装Restful
API; - 创建
APIInstaller
执行Install
方法进行安装APIResource
;- 对于
APIGroupVersion
中每种资源执行registerResourceHandlers
方法进行 最核心的转换(rest.Storage-> http.Handler
),详细见1.3
- 对于
- 对于每个版本,首先创建
- 安装
- 创建
KubeApiServer
,其创建过程与创建apiextensionServer
类似- 创建
GenericAPIServer
,但与上面不一样的地方是:其将apiextensionServer
带入其中当nofoundHandler
,当kubeApiServer
不能处理请求时,会将请求转发到apiextensionServer
; - 安装
api/
路径的核心资源(LeagyAPI
)(endpoint
、node
、pod
)。其内部安装逻辑与上面过程类似,则将不累述; - 安装
apis/
路径其他拓展资源;
- 创建
- 创建
APIAggregator
,聚合服务,其是apiserver
重要拓展机制,用户可以自定义API服务,然后以APIService
形式告知apiserver
- 同样先创建
GenericAPIServer
,传入KubeApiServer
作为nofoundHandler
; - 安装
apiregistration
API组; - 初始化
apiserviceRegistrationController
,其用于管理APIService
。当有APIService
更新时,其会将其转换成proxyHandler
然后注册到APIAggregator
的NonGoRestfulMux
中,用于将请求转发到APIService
中定义的自定义API服务;
- 同样先创建
- 构建
InsecureHandlerChain
,并监听insecureServer
端口,接收请求
- 创建
总结其运行图如下:
k8s现阶段,API一共分为13个Group:Core、apps、authentication、authorization、autoscaling、batch、certificates、componentconfig、extensions、imagepolicy、policy、rbac、storage。
1.2 Scheme
Scheme
是k8s
中最重要的组件。其存储API资源信息(类型信息、版本信息等),支持API资源的序列化、反序列化、版本转化。
首先,我们将来分析其结构:
1 | type Scheme struct { |
Scheme
内容的注册时机发生在创建ApiServerCommand
的时候,其引入k8s.io/kubernetes/pkg/master
包,而这个包中包含import_known_versions.go
类,其引入众多install
包:
1 | import ( |
而,Scheme
内容的注入则是在这些install
中进行。下面以k8s.io/kubernetes/pkg/apis/core/install
为例来介绍其注册过程:
1 | func init() { |
首先,会将core
包下的API
资源注册到Scheme
,再将v1
包下的API
资源注册到Scheme
中,设置版本的优先级。core
包下的注册内容如下:
1 | func addKnownTypes(scheme *runtime.Scheme) error { |
将core
包中关键API
资源加入到Scheme
中:
1 | func (s *Scheme) AddKnownTypes(gv schema.GroupVersion, types ...Object) { |
再看v1.AddToScheme
,向Scheme
中添加类型初始化函数v1.addDefaultingFuncs
,向Scheme
中添加转换函数v1.addConversionFuncs
1.3 rest.Storage -> http.Handler
rest.Storage
到http.Handler
的转换是apiServer
中的重要部分。也是其抽象的精髓。位于apiserver/pkg/endpoints/installer.go/registerResourceHandlers
。下面将详细阐述其实现过程:
- 通过
Scheme
、GroupVersion
、Storage
获取类型信息,GroupVersionKind
; - 通过
Scheme
和GroupVersionKind
创建类型实例; - 判断
rest.Storage
实现了哪些接口:是否有创建接口(isCreater
)、是否可列举(isLister
)、是否可查询(isGetter
)。判断的意义在于:确定该类型可以生成的Http行为; - 区分是否对命名空间敏感,形成不同的资源路径;其次,跟据前面的判断结果集生成可以生成httpHandler的
actions
。如LIST
、POST
、GET
、WATCH
…; - 针对每种
action
,为其创建一个route
(go-restful),同时为其构建一个处理函数RouteFunction
; - 需要特别说明的一点是:对于修改操作,同时会将
Admit
传入RouteFunction
中,为了在操作前进行准入的控制; - 最后,将每种
route
加入到WebService
中。
单次请求过程
下面本节将以创建一个deployment
为例来讲解请求执行过程:
首先通过kubectl
向k8s-apiserver
发起创建deployment
命令,kubectl create -f nginx-deployment.yaml
。
其执行流程如下图:
总结起来,其过程如下:
- 首先经过过滤链进行处理,包括认证、准入等通用的功能链;
- 开始依次经过aggregator、apiserver的
go-restful
的Container
,查看是否匹配注册的规则; - 本次请求你
deploymenet
是apps
API组中的资源,因此分发到对应的apps/v1
WebService下POST
route进行处理; - route对应function是一个包装的handler
handlers.createHandlers
,其是整个请求核心逻辑模块,首先利用解码请求体(利用Scheme
保障的ParamterCocdec
),再通过准入控制插件(13种)的检测以及修改,调用rest.Storage
进行操作etcd写入数据。
附件
1. 处理链
1 | handler := genericapifilters.WithAuthorization(apiHandler, c.Authorization.Authorizer, c.Serializer) |
拓展阅读: