前言
在前一篇开胃菜-基础结构信息对APIServer各类基础信息、结构铺垫的基础上,开始进入APIServer源码的探讨环节。本篇拆分为 预启动、启动种两部分来分析。
预启动
资源注册
前面的基础篇有讲过,scheme是一种内存型的注册表,提供给各类gvk进行注册。在APIServer http服务启动前的第一步,就是将所支持的gvk注册到scheme中,后面的步骤会依赖scheme注册表信息。
值得注意的是,并没有函数方法来显示地注册scheme,而是通过go语言的包导入init机制来初始化注册的。
Go语言在导入包时,被导入的包中的var/const变量以及init()函数会默认装载
cmd/kube-apiserver/app/server.go:63
1 | import ( |
这里列举的两个包,legacyscheme包在导入时会初始化空的scheme注册表,master包初始化时会向scheme表install注册gvk。
legacyscheme包
pkg/api/legacyscheme/scheme.go:29
1 | // Scheme is the default instance of runtime.Scheme to which types in the Kubernetes API are already registered. |
####master包
pkg/master/import_known_versions.go:20
1 | // These imports are the API groups the API server will support. |
这里会把所有内置group的install包导入,列举其中的"k8s.io/kubernetes/pkg/apis/apps/install"
group为例,进入其中:
pkg/apis/apps/install/install.go:31
1 | func init() { |
这里可以看到,依次将group、version注册到了scheme中,并设置各version的优先级
Cobra 命令参数解析
关于Cobra的参数解析,在源码系列第一篇中就有详细说明,这里略过。
初始化默认配置
从Run()开始进入,找到初始化默认配置的方法:
cmd/kube-apiserver/app/server.go:144
1 | // Run runs the specified APIServer. This should never exit. |
–> cmd/kube-apiserver/app/server.go:157
CreateServerChain()
–> cmd/kube-apiserver/app/server.go:270
CreateKubeAPIServerConfig()
–> cmd/kube-apiserver/app/server.go:285
buildGenericConfig(s.ServerRunOptions, proxyTransport)
1 | // BuildGenericConfig takes the master server options and produces the genericapiserver.Config associated with it |
APIServer因其模块众多,相应的配置参数也非常之多,分类一下:
- genericConfig 通用配置
- OpenAPI配置
- Storage (etcd)配置
- Authentication 认证配置
- Authorization 授权配置
genericConfig
cmd/kube-apiserver/app/server.go:382
1 | genericConfig = genericapiserver.NewConfig(legacyscheme.Codecs) |
DefaultAPIResourceConfigSource()
方法用来启用、禁用默认的GV及其之下Resource:
pkg/master/master.go:478
1 | func DefaultAPIResourceConfigSource() *serverstorage.ResourceConfig { |
OpenAPI配置
cmd/kube-apiserver/app/server.go:405
1 | genericConfig.OpenAPIConfig = genericapiserver.DefaultOpenAPIConfig(generatedopenapi.GetOpenAPIDefinitions, openapinamer.NewDefinitionNamer(legacyscheme.Scheme, extensionsapiserver.Scheme, aggregatorscheme.Scheme)) |
根据OpenAPI规范的要求,定义OpenAPIDefinition文件,关于OpenAPI规范,可参考:
https://swagger.io/specification/#introduction
Storage (etcd)配置
cmd/kube-apiserver/app/server.go:415
1 | storageFactoryConfig := kubeapiserver.NewStorageFactoryConfig() |
这里实例化了etcd storage对象,定义了etcd地址、认证、存储路径prefix等信息。
认证配置
APIServer支持如下的认证策略:
X509 Client Certs
Static Token File
Bootstrap Tokens
Service Account Tokens
OpenID Connect Tokens(OIDC)
Webhook Token Authentication
Authenticating Proxy
参考官方文档:
https://kubernetes.io/docs/reference/access-authn-authz/authentication/#authentication-strategies
每一种认证策略都会被实例化为一个Authenticator认证器,被封装进http.Handler函数中来接收和处理客户端的认证请求。
cmd/kube-apiserver/app/server.go:444
1 | genericConfig.Authentication.Authenticator, genericConfig.OpenAPIConfig.SecurityDefinitions, err = BuildAuthenticator(s, clientgoExternalClient, versionedInformers) |
–> cmd/kube-apiserver/app/server.go:515
1 | // BuildAuthenticator constructs the authenticator |
–> pkg/kubeapiserver/authenticator/config.go:85
1 | func (config Config) New() (authenticator.Request, *spec.SecurityDefinitions, error) { |
这个New()方法依次生成每一种认证方式的Authenticator,New()方法的流程图如下:
union.New(authenticators...)
方法集合所有的认证器
vendor/k8s.io/apiserver/pkg/authentication/request/union/union.go:36
1 | func New(authRequestHandlers ...authenticator.Request) authenticator.Request { |
–> vendor/k8s.io/apiserver/pkg/authentication/request/union/union.go:36
1 | // New returns a request authenticator that validates credentials using a chain of authenticator.Request objects. |
当认证请求进来时,遍历各个认证器,只要有一个认证器返回true则视为认证成功,全部为false则认证失败。
授权配置
授权配置的流程与认证配置基本一致,快速略过。
APIServer支持如下的授权策略:
AlwaysAllow
AlwaysDeny
webhook授权
node授权
ABAC授权
RBAC授权
参考官方文档:
https://kubernetes.io/docs/reference/access-authn-authz/authorization/
cmd/kube-apiserver/app/server.go:519
1 | func BuildAuthorizer(s *options.ServerRunOptions, versionedInformers clientgoinformers.SharedInformerFactory) (authorizer.Authorizer, authorizer.RuleResolver, error) { |
–> pkg/kubeapiserver/authorizer/config.go:59
1 | func (config Config) New() (authorizer.Authorizer, authorizer.RuleResolver, error) { |
创建APIExtensionsServer
上面在初始化完默认的配置后,并没有立即装载创建APIServer,K8s专门设计了一个扩展型的APIExtensionsServer,为CRD提供服务,首先创建的是APIExtensionsServer.
回到这里:
cmd/kube-apiserver/app/server.go:163
1 | // 初始化默认配置 |
下面来看一下创建APIExtensionsServer的流程:
cmd/kube-apiserver/app/apiextensions.go:93
–> vendor/k8s.io/apiextensions-apiserver/pkg/apiserver/apiserver.go:130
1 | func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget) (*CustomResourceDefinitions, error) { |
拆解一下这个函数的流程:
1、创建GenericServer
2、实例化CRD 的APIServer
3、将CRD的GV/GVR及子资源与storage对象进行关联映射
4、注册APIGroup(####注册APIGroup)
####创建GenericServer
1 | genericServer, err := c.GenericConfig.New("apiextensions-apiserver", delegationTarget) |
genericServer提供了一个通用的http server,定义了通用的模板,例如地址、端口、认证、授权、健康检查等等通用功能。无论是APIServer还是APIExtensionsServer都依赖于genericServer。
实例化CRD和APIGroup
1 | s := &CustomResourceDefinitions{ |
重点是里面的customresourcedefinition.NewREST()
方法:
vendor/k8s.io/apiextensions-apiserver/pkg/registry/customresourcedefinition/etcd.go:41
1 | // NewREST returns a RESTStorage object that will work against API services. |
核心是genericregistry.Store{}
结构体:
vendor/k8s.io/apiserver/pkg/registry/generic/registry/store.go:81
1 | // The intended use of this type is embedding within a Kind specific |
从注释中可以看出,Store结构体是一个对于各类k8s资源通用的REST封装的实现,可以实现CURD功能以及满足相应的CURD策略,同时实现了对接后端etcd存储进行相应的增删改查操作。
在这里注册了CRD对应的Store后,即可实现其对应的CURD方法,例如查询某个CRD对象对应的api是:
GET /apis/apiextensions.k8s.io/v1beta1/namespaces/${namespace}/crd/${crd-name}
注册APIGroup
vendor/k8s.io/apiextensions-apiserver/pkg/apiserver/apiserver.go:152
–> vendor/k8s.io/apiserver/pkg/server/genericapiserver.go:438
–> vendor/k8s.io/apiserver/pkg/server/genericapiserver.go:385
1 | func (s *GenericAPIServer) InstallAPIGroups(apiGroupInfos ...*APIGroupInfo) error { |
来看看s.installAPIResources()
方法:
vendor/k8s.io/apiserver/pkg/server/genericapiserver.go:341
1 | // installAPIResources is a private method for installing the REST storage backing each api groupversionresource |
遍历GroupVersion,调用apiGroupVersion.InstallREST()
方法:
vendor/k8s.io/apiserver/pkg/endpoints/groupversion.go:99
1 | // InstallREST registers the REST handlers (storage, watch, proxy and redirect) into a restful Container. |
调用installer.Install()安装VersionGroup内的Resource:
vendor/k8s.io/apiserver/pkg/endpoints/installer.go:95
1 | func (a *APIInstaller) Install() ([]metav1.APIResource, *restful.WebService, []error) { |
在这里,用到了上一章基础篇提到的go-restful模块,通过go-restful WebService对象,a.registerResourceHandlers()
方法将http访问路径与上一步得到的RESTStorage对象进行绑定,即可把相应路径的请求路由到RESTStorage对应的方法上去。
APIExtensionsServer启动完成后,可以通过如下路径访问该GV下的资源列表:
1 | 先起一个免认证代理 |
启动中
创建Kube APIServer (master apiserver)
回到cmd/kube-apiserver/app/server.go:179
1 | kubeAPIServer, err := CreateKubeAPIServer(kubeAPIServerConfig, apiExtensionsServer.GenericAPIServer, admissionPostStartHook) |
–>cmd/kube-apiserver/app/server.go:214
–> pkg/master/master.go:299
1 | func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget) (*Master, error) { |
APIServer启动分为以下几步:
1、创建GenericServer
2、实例化master APIServer(Kube APIServer)
3、注册/api下的资源(LegacyAPI)
4、注册/apis下的资源
其实与上面启动APIExtensionsServer的流程基本大差不差,篇幅有限重复的地方尽量简略跳过,直接进入第三步。
注册LegacyAPI
1 | // install legacy rest storage |
–> pkg/master/master.go:374
1 | func (m *Master) InstallLegacyAPI(c *completedConfig, restOptionsGetter generic.RESTOptionsGetter, legacyRESTStorageProvider corerest.LegacyRESTStorageProvider) { |
展开legacyRESTStorageProvider.NewLegacyRESTStorage(restOptionsGetter)
可以看到,这里注册的都是core/v1组里的资源,例如(pod/service/replicas/nodes)等等,所谓Legacy,指的即是核心组,路径是/api。后面的步骤与APIExtensionsServer里的相似,不再赘述。
注册/apis下的资源
1 | restStorageProviders := []RESTStorageProvider{ |
注册/apis路径下的GV内的资源,例如apps/extension组等。
创建AggregatorServer
再回到cmd/kube-apiserver/app/server.go:192
,进入下一步,创建AggregatorServer:
1 | // aggregator comes last in the chain |
AggregatorServer也与APIExtensionsServer类似,只不过group换成了apiregistration.k8s.io,其余基本一致,不再赘述,这个组里的资源列表可以这样查看:
1 | root@ubuntu238:~# curl http://127.0.0.1:8000/apis/apiregistration.k8s.io/v1beta1/ |
启动web服务
回到最初起点这里 cmd/kube-apiserver/app/server.go:153
1 | server, err := CreateServerChain(completeOptions, stopCh) |
–> vendor/k8s.io/apiserver/pkg/server/genericapiserver.go:272
s.NonBlockingRun(stopCh)
–> vendor/k8s.io/apiserver/pkg/server/genericapiserver.go:311
s.SecureServingInfo.Serve()
1 | secureServer := &http.Server{ |
配置tls证书相关配置
vendor/k8s.io/apiserver/pkg/server/secure_serving.go:126
1 | go func() { |
可以看到这里使用的是go标准库内置的http模块提供web服务
总结
APIServer的总体启动流程分为这几个步骤:
- 资源注册
- 默认配置初始化
- 注册server chain组
- 启动服务