Kubernetes:kube-apiserver 之启动流程(一)
0. 前言
前面两篇文章 Kubernetes:kube-apiserver 之 scheme(一) 和 Kubernetes:kube-apiserver 之 scheme(二) 重点介绍了 kube-apiserver 中的资源注册表 scheme。这里进入正题,开始介绍 kube-apiserver 的核心实现。
1. kube-apiserver 启动流程
kube-apiserver 使用 Cobra 作为 CLI 框架,其初始化示意图如下。

结合示意图和代码看初始化过程效果更佳。代码在 kubernetes/cmd/kube-apiserver/apiserver.go。
# kubernetes/cmd/kube-apiserver/apiserver.go
package main
func main() {
command := app.NewAPIServerCommand()
code := cli.Run(command)
os.Exit(code)
}
# kubernetes/cmd/kube-apiserver/app/server.go
func NewAPIServerCommand() *cobra.Command {
s := options.NewServerRunOptions()
cmd := &cobra.Command{
Use: "kube-apiserver",
...
RunE: func(cmd *cobra.Command, args []string) error {
// set default options
completedOptions, err := s.Complete()
if err != nil {
return err
}
// validate options
if errs := completedOptions.Validate(); len(errs) != 0 {
return utilerrors.NewAggregate(errs)
}
return Run(completedOptions, genericapiserver.SetupSignalHandler())
},
}
# parse flags to options
fs := cmd.Flags()
namedFlagSets := s.Flags()
verflag.AddFlags(namedFlagSets.FlagSet("global"))
return cmd
}
首先调用 options.NewServerRunOptions() 实例化 options 选项,接着 s.Complete() 补全默认 options,将补全的 options 送入 Validate() 方法进行验证。验证通过后进入 Run(completedOptions, genericapiserver.SetupSignalHandler()),Run() 函数是不会退出的函数,在函数内运行 kube-apiserver。
有一点要注意的是,kube-apiserver 的 参数 通过 flag 解析赋给 options,这是框架的用法,不多讲。
进入 Run() 函数内。
func Run(opts options.CompletedOptions, stopCh <-chan struct{}) error {
// 实例化 kube-apiserver 配置 config
config, err := NewConfig(opts)
if err != nil {
return err
}
// 补全默认配置
completed, err := config.Complete()
if err != nil {
return err
}
// 创建服务链
server, err := CreateServerChain(completed)
if err != nil {
return err
}
prepared, err := server.PrepareRun()
if err != nil {
return err
}
return prepared.Run(stopCh)
}
如注释所示,Run() 函数内 kube-apiserver 的启动流程相当清晰。
下面分步看各个流程。
1.1 实例化配置
进入 NewConfig(opts) 看实例化 config 过程。
# kubernetes/cmd/kube-apiserver/app/config.go
func NewConfig(opts options.CompletedOptions) (*Config, error) {
// 根据 options 实例化 Config
c := &Config{
Options: opts,
}
// 创建 controlPlane 配置文件
controlPlane, serviceResolver, pluginInitializer, err := CreateKubeAPIServerConfig(opts)
if err != nil {
return nil, err
}
c.ControlPlane = controlPlane
// 创建 apiExtensions 配置文件
apiExtensions, err := apiserver.CreateAPIExtensionsConfig(*controlPlane.GenericConfig, controlPlane.ExtraConfig.VersionedInformers, pluginInitializer, opts.CompletedOptions, opts.MasterCount,
serviceResolver, webhook.NewDefaultAuthenticationInfoResolverWrapper(controlPlane.ExtraConfig.ProxyTransport, controlPlane.GenericConfig.EgressSelector, controlPlane.GenericConfig.LoopbackClientConfig, controlPlane.GenericConfig.TracerProvider))
if err != nil {
return nil, err
}
c.ApiExtensions = apiExtensions
// 创建 aggregator 配置文件
aggregator, err := createAggregatorConfig(*controlPlane.GenericConfig, opts.CompletedOptions, controlPlane.ExtraConfig.VersionedInformers, serviceResolver, controlPlane.ExtraConfig.ProxyTransport, controlPlane.ExtraConfig.PeerProxy, pluginInitializer)
if err != nil {
return nil, err
}
c.Aggregator = aggregator
return c, nil
}
将 kube-apiserver 的所有 REST 服务组合在一起是极为复杂的,这里 kube-apiserver 将服务拆分,解耦为三种 HTTP Server:KubeAPIServer,APIExtensionsServer 和 AggregatorServer。

三种 HTTP Server 拥有各自的配置文件。这里以 APIExtensionsServer 为例,查看其启动流程,其它两种 HTTP Server 与此类似。
进入 CreateKubeAPIServerConfig(opts)。
func CreateKubeAPIServerConfig(opts options.CompletedOptions) (
*controlplane.Config,
aggregatorapiserver.ServiceResolver,
[]admission.PluginInitializer,
error,
) {
// 创建通用配置
genericConfig, versionedInformers, storageFactory, err := controlplaneapiserver.BuildGenericConfig(
opts.CompletedOptions,
[]*runtime.Scheme{legacyscheme.Scheme, extensionsapiserver.Scheme, aggregatorscheme.Scheme},
generatedopenapi.GetOpenAPIDefinitions,
)
config := &controlplane.Config{
GenericConfig: genericConfig,
ExtraConfig: controlplane.ExtraConfig{
...
},
}
// setup admission
admissionConfig := &kubeapiserveradmission.Config{
ExternalInformers: versionedInformers,
LoopbackClientConfig: genericConfig.LoopbackClientConfig,
CloudConfigFile: opts.CloudProvider.CloudConfigFile,
}
err = opts.Admission.ApplyTo(
genericConfig,
versionedInformers,
clientgoExternalClient,
dynamicExternalClient,
utilfeature.DefaultFeatureGate,
pluginInitializers...)
if err != nil {
return nil, nil, nil, fmt.Errorf("failed to apply admission: %w", err)
}
...
return config, serviceResolver, pluginInitializers, nil
}
试想,三种 HTTP Server 肯定有通用的配置。kube-apiserver 在函数 CreateKubeAPIServerConfig(opts) 内调用 BuildGenericConfig() 创建 HTTP Server 通用配置。
创建完通用配置后,实例化 KubeAPIServer 配置 config。接着,实例化 admission 准入相关配置,通过 opts.Admission.ApplyTo() 将准入配置赋给 config。
进入 BuildGenericConfig 看通用配置创建了什么。
func BuildGenericConfig(
s controlplaneapiserver.CompletedOptions,
schemes []*runtime.Scheme,
getOpenAPIDefinitions func(ref openapicommon.ReferenceCallback) map[string]openapicommon.OpenAPIDefinition,
) (
genericConfig *genericapiserver.Config,
versionedInformers clientgoinformers.SharedInformerFactory,
storageFactory *serverstorage.DefaultStorageFactory,
lastErr error,
) {
// NewConfig returns a Config struct with the default values
genericConfig = genericapiserver.NewConfig(legacyscheme.Codecs)
genericConfig.MergedResourceConfig = controlplane.DefaultAPIResourceConfigSource()
// ApplyTo applies the run options to the method receiver and returns self
if lastErr = s.GenericServerRunOptions.ApplyTo(genericConfig); lastErr != nil {
return
}
// wrap the definitions to revert any changes from disabled features
getOpenAPIDefinitions = openapi.GetOpenAPIDefinitionsWithoutDisabledFeatures(getOpenAPIDefinitions)
namer := openapinamer.NewDefinitionNamer(schemes...)
genericConfig.OpenAPIConfig = genericapiserver.DefaultOpenAPIConfig(getOpenAPIDefinitions, namer)
genericConfig.OpenAPIConfig.Info.Title = "Kubernetes"
genericConfig.OpenAPIV3Config = genericapiserver.DefaultOpenAPIV3Config(getOpenAPIDefinitions, namer)
genericConfig.OpenAPIV3Config.Info.Title = "Kubernetes"
// New returns a new storage factory created from the completed storage factory configuration.
storageFactoryConfig := kubeapiserver.NewStorageFactoryConfig()
storageFactoryConfig.APIResourceConfig = genericConfig.MergedResourceConfig
storageFactory, lastErr = storageFactoryConfig.Complete(s.Etcd).New()
if lastErr != nil {
return
}
// ApplyWithStorageFactoryTo mutates the provided server.Config. It must never mutate the receiver (EtcdOptions).
if lastErr = s.Etcd.ApplyWithStorageFactoryTo(storageFactory, genericConfig); lastErr != nil {
return
}
// Authentication.ApplyTo requires already applied OpenAPIConfig and EgressSelector if present
if lastErr = s.Authentication.ApplyTo(&genericConfig.Authentication, genericConfig.SecureServing, genericConfig.EgressSelector, genericConfig.OpenAPIConfig, genericConfig.OpenAPIV3Config, clientgoExternalClient, versionedInformers); lastErr != nil {
return
}
// BuildAuthorizer constructs the authorizer
genericConfig.Authorization.Authorizer, genericConfig.RuleResolver, err = BuildAuthorizer(s, genericConfig.EgressSelector, versionedInformers)
if err != nil {
lastErr = fmt.Errorf("invalid authorization config: %v", err)
return
}
...
return
}
通用配置内创建了一系列配置,概括如下。

这里不继续往下探各个配置的详细信息,后续需要再回头看。配置文件创建好后,返回 Run() 函数看服务链的创建。
1.2 创建服务链
进入 CreateServerChain 查看服务链。
func CreateServerChain(config CompletedConfig) (*aggregatorapiserver.APIAggregator, error) {
// New returns an HTTP handler that is meant to be executed at the end of the delegation chain.
// It checks if the request have been made before the server has installed all known HTTP paths.
// In that case it returns a 503 response otherwise it returns a 404.
notFoundHandler := notfoundhandler.New(config.ControlPlane.GenericConfig.Serializer, genericapifilters.NoMuxAndDiscoveryIncompleteKey)
// New returns a new instance of CustomResourceDefinitions from the given config.
apiExtensionsServer, err := config.ApiExtensions.New(genericapiserver.NewEmptyDelegateWithCustomHandler(notFoundHandler))
if err != nil {
return nil, err
}
crdAPIEnabled := config.ApiExtensions.GenericConfig.MergedResourceConfig.ResourceEnabled(apiextensionsv1.SchemeGroupVersion.WithResource("customresourcedefinitions"))
kubeAPIServer, err := config.ControlPlane.New(apiExtensionsServer.GenericAPIServer)
if err != nil {
return nil, err
}
// aggregator comes last in the chain
aggregatorServer, err := createAggregatorServer(config.Aggregator, kubeAPIServer.GenericAPIServer, apiExtensionsServer.Informers, crdAPIEnabled)
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
}
服务链中创建三种 HTTP Server,这里还是介绍 apiExtensionsServer 服务。
首先将 notFoundHandler handler 赋给 apiExtensionsServer,当 REST 路由不到指定 API 时会路由到 notFoundHandler 处理请求。
进入 config.ApiExtensions.New。
func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget) (*CustomResourceDefinitions, error) {
// 创建通用 APIServer
genericServer, err := c.GenericConfig.New("apiextensions-apiserver", delegationTarget)
if err != nil {
return nil, err
}
// 实例化 APIExtensions server: CustomResourceDefinitions
s := &CustomResourceDefinitions{
GenericAPIServer: genericServer,
}
// 创建 apiGroupInfo,通过 apiGroupInfo 建立 REST API 到资源实例的路由
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(apiextensions.GroupName, Scheme, metav1.ParameterCodec, Codecs)
storage := map[string]rest.Storage{}
// customresourcedefinitions
if resource := "customresourcedefinitions"; apiResourceConfig.ResourceEnabled(v1.SchemeGroupVersion.WithResource(resource)) {
// 实例化 REST 资源:customResourceDefinitionStorage
customResourceDefinitionStorage, err := customresourcedefinition.NewREST(Scheme, c.GenericConfig.RESTOptionsGetter)
if err != nil {
return nil, err
}
storage[resource] = customResourceDefinitionStorage
storage[resource+"/status"] = customresourcedefinition.NewStatusREST(Scheme, customResourceDefinitionStorage)
}
if len(storage) > 0 {
// 建立 version 到 REST 资源的 mapping
apiGroupInfo.VersionedResourcesStorageMap[v1.SchemeGroupVersion.Version] = storage
}
// 通过 apiGroupInfo 建立 REST API 到资源实例的路由
if err := s.GenericAPIServer.InstallAPIGroup(&apiGroupInfo); err != nil {
return nil, err
}
...
}
config.ApiExtensions.New 中定义的流程如注释所示。下面逐层展开各个流程。
1.2.1 创建通用 APIServer
进入 c.GenericConfig.New 查看通用 APIServer 创建过程。
func (c completedConfig) New(name string, delegationTarget DelegationTarget) (*GenericAPIServer, error) {
// 创建通用 APIServer 的 handler
apiServerHandler := NewAPIServerHandler(name, c.Serializer, handlerChainBuilder, delegationTarget.UnprotectedHandler())
// 实例化通用 APIServer 并将前面创建的 apiServerHandler 赋给通用 APIServer
s := &GenericAPIServer{
Handler: apiServerHandler,
listedPathProvider: apiServerHandler,
}
// 建立通用 REST 路由
installAPI(s, c.Config)
return s, nil
}
首先,进入 NewAPIServerHandler 查看通用 APIServer handler 的创建。
func NewAPIServerHandler(name string, s runtime.NegotiatedSerializer, handlerChainBuilder HandlerChainBuilderFn, notFoundHandler http.Handler) *APIServerHandler {
// 建立非 REST 资源的路由
nonGoRestfulMux := mux.NewPathRecorderMux(name)
if notFoundHandler != nil {
nonGoRestfulMux.NotFoundHandler(notFoundHandler)
}
// 创建 go-restful container 处理 REST 资源的路由
gorestfulContainer := restful.NewContainer()
gorestfulContainer.ServeMux = http.NewServeMux()
gorestfulContainer.Router(restful.CurlyRouter{}) // e.g. for proxy/{kind}/{name}/{*}
gorestfulContainer.RecoverHandler(func(panicReason interface{}, httpWriter http.ResponseWriter) {
logStackOnRecover(s, panicReason, httpWriter)
})
gorestfulContainer.ServiceErrorHandler(func(serviceErr restful.ServiceError, request *restful.Request, response *restful.Response) {
serviceErrorHandler(s, serviceErr, request, response)
})
// 将两种路由 REST 资源路由和 非 REST 资源路由赋给 director
director := director{
name: name,
goRestfulContainer: gorestfulContainer,
nonGoRestfulMux: nonGoRestfulMux,
}
// 返回 APIServerHandler
return &APIServerHandler{
FullHandlerChain: handlerChainBuilder(director),
GoRestfulContainer: gorestfulContainer,
NonGoRestfulMux: nonGoRestfulMux,
Director: director,
}
}
kube-apiserver 基于 go-restful 框架建立 RESTful API 的路由。
创建好 apiServerHandler 后将该 handler 赋给通用 APIServer GenericAPIServer。接着进入 installAPI(s, c.Config) 看通用 REST 路由的创建过程。
func installAPI(s *GenericAPIServer, c *Config) {
...
routes.Version{Version: c.Version}.Install(s.Handler.GoRestfulContainer)
}
这里看一种 REST 路由 version 的建立过程,进入 Version.Install。
// Install registers the APIServer's `/version` handler.
func (v Version) Install(c *restful.Container) {
if v.Version == nil {
return
}
// Set up a service to return the git code version.
versionWS := new(restful.WebService)
versionWS.Path("/version")
versionWS.Doc("git code version from which this is built")
versionWS.Route(
versionWS.GET("/").To(v.handleVersion).
Doc("get the code version").
Operation("getCodeVersion").
Produces(restful.MIME_JSON).
Consumes(restful.MIME_JSON).
Writes(version.Info{}))
c.Add(versionWS)
}
// handleVersion writes the server's version information.
func (v Version) handleVersion(req *restful.Request, resp *restful.Response) {
responsewriters.WriteRawJSON(http.StatusOK, *v.Version, resp.ResponseWriter)
}
可以看到,在 Install 内建立了 /version 到 v.handleVersion 的路由。
下一节,将继续介绍创建服务链的流程。未完待续...
Kubernetes:kube-apiserver 之启动流程(一)的更多相关文章
- apiserver源码分析——启动流程
前言 apiserver是k8s控制面的一个组件,在众多组件中唯一一个对接etcd,对外暴露http服务的形式为k8s中各种资源提供增删改查等服务.它是RESTful风格,每个资源的URI都会形如 / ...
- kubernetes调度概念与工作流程
Overview [1] kubernetes集群中的调度程序 kube-scheduler 会 watch 未分配节点的新创建的Pod,并未该Pod找到可运行的最佳(特定)节点.那么这些动作或者说这 ...
- 2440启动流程 <转载>
韦东山 博客园 首页 订阅 管理 2440启动过程分析 2440启动过程分析 2440启动过程算是一个难点,不太容易理解,而对于2440启动过程的理解,影响了后面裸机代码执行流程的分析,从而看出2 ...
- MyCat源码分析系列之——配置信息和启动流程
更多MyCat源码分析,请戳MyCat源码分析系列 MyCat配置信息 除了一些默认的配置参数,大多数的MyCat配置信息是通过读取若干.xml/.properties文件获取的,主要包括: 1)se ...
- Android进阶系列之源码分析Activity的启动流程
美女镇楼,辟邪! 源码,是一个程序猿前进路上一个大的而又不得不去翻越障碍,我讨厌源码,看着一大堆.5000多行,要看完得啥时候去了啊.不过做安卓的总有这一天,自从踏上这条不归路,我就认命了.好吧,我慢 ...
- Spring Boot启动流程详解(一)
环境 本文基于Spring Boot版本1.3.3, 使用了spring-boot-starter-web. 配置完成后,编写了代码如下: @SpringBootApplication public ...
- linux启动流程及自定义gurb
linux 启动流程 POST BIOS(boot sequence) 所选择的启动设备次序的MBR中是否有引导程序, ----> MBR(bootloader) 提供内核列表 -------& ...
- linux启动流程
看了深入理解linux内核一书的最后对linux启动流程的介绍,下面就把我能理解的写一下吧: bios(硬件加电自检POST,寻找第一个启动设备) the boot loader(可以从硬盘启动也可以 ...
- webapp启动流程
webapp启动流程 看了这个教程,其实所有的webapp大致都是这个流程了.
- Tomcat源码分析之—具体启动流程分析
从Tomcat启动调用栈可知,Bootstrap类的main方法为整个Tomcat的入口,在init初始化Bootstrap类的时候为设置Catalina的工作路径也就是Catalina_HOME信息 ...
随机推荐
- 详解共识算法的Raft算法模拟数
摘要:Raft算法是一种分布式共识算法,用于解决分布式系统中的一致性问题. 本文分享自华为云社区<共识算法之Raft算法模拟数>,作者: TiAmoZhang . 01.Leader选举 ...
- 教师节专题:AI互动课来了,即构方案助推在线教育创新升级
打开热门综艺,乘风破浪的姐姐们告诉你"用瓜瓜龙英语给孩子启蒙":走出家门,电梯口.公交站的大幅广告跟你说"2-8岁上斑马". 如果说去年的AI互动课还是浮于媒体 ...
- Centos7中Oracle占用CPU过高(解决方案)
Centos7中Oracle占用CPU过高(解决方案) 前言: 99%的问题几乎都是SQL的问题,一般SQL可能会出现以下几种情况: 相关SQL搜索条件没有加索引 索引失效 联合查询过多 数据量过大 ...
- 打造原生 WebGL 2D 引擎:一场创意与技术的融合
打造原生 WebGL 2D 引擎:一场创意与技术的融合 1.引言 在当今数字化时代,网页的功能越来越丰富,已经远远超越了传统的文本和图片呈现.我们生活在一个充满交互性和视觉魅力的网络世界.每天都会遇到 ...
- 【go语言】2.3.2 error 接口
在 Go 语言中,error 是一个内置的接口类型,用于表示错误情况.error 接口的定义如下: type error interface { Error() string } 任何具有 Error ...
- Centos7制作本地yum仓库,共享给局域网其他设备
环境准备: 准备好安装好Centos7的虚机A(服务端)和虚机B(客户端) 配置两台虚机网络使其互通,关闭selinux和firewalld等限制 下载完整的ISO镜像(CentOS-7-x86_64 ...
- 【go语言】2.4.2 自定义包的创建和使用
在 Go 中,任何一个目录都可以被视为一个包.创建自定义包的基本步骤是: 新建一个目录,用于存放包的源文件. 在新建的目录中编写 Go 代码,代码的第一行应该是 package 包名. 使用 impo ...
- 预处理器 Less 的十个语法
Less 是一门 CSS 预处理语言,它扩充了 CSS 语言,增加了诸如变量.混合(mixin).函数等功能,让 CSS 更易维护.方便制作主题.扩充. 不过浏览器只能识别 CSS 语言,所以 Les ...
- 华为云GaussDB(for Influx)单机版上线,企业降本增效利器来了
本文分享自华为云社区<华为云GaussDB(for Influx)单机版上线,企业降本增效利器来了>,作者:GaussDB 数据库 . 1.背景 华为云GaussDB(for Influx ...
- Docker下elasticsearch8部署、扩容、基本操作实战(含kibana)
欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇概览 本篇记录了用docker搭建ElasticS ...