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 ServerKubeAPIServerAPIExtensionsServerAggregatorServer

三种 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 内建立了 /versionv.handleVersion 的路由。

下一节,将继续介绍创建服务链的流程。未完待续...


Kubernetes:kube-apiserver 之启动流程(一)的更多相关文章

  1. apiserver源码分析——启动流程

    前言 apiserver是k8s控制面的一个组件,在众多组件中唯一一个对接etcd,对外暴露http服务的形式为k8s中各种资源提供增删改查等服务.它是RESTful风格,每个资源的URI都会形如 / ...

  2. kubernetes调度概念与工作流程

    Overview [1] kubernetes集群中的调度程序 kube-scheduler 会 watch 未分配节点的新创建的Pod,并未该Pod找到可运行的最佳(特定)节点.那么这些动作或者说这 ...

  3. 2440启动流程 <转载>

    韦东山 博客园 首页 订阅 管理 2440启动过程分析   2440启动过程分析 2440启动过程算是一个难点,不太容易理解,而对于2440启动过程的理解,影响了后面裸机代码执行流程的分析,从而看出2 ...

  4. MyCat源码分析系列之——配置信息和启动流程

    更多MyCat源码分析,请戳MyCat源码分析系列 MyCat配置信息 除了一些默认的配置参数,大多数的MyCat配置信息是通过读取若干.xml/.properties文件获取的,主要包括: 1)se ...

  5. Android进阶系列之源码分析Activity的启动流程

    美女镇楼,辟邪! 源码,是一个程序猿前进路上一个大的而又不得不去翻越障碍,我讨厌源码,看着一大堆.5000多行,要看完得啥时候去了啊.不过做安卓的总有这一天,自从踏上这条不归路,我就认命了.好吧,我慢 ...

  6. Spring Boot启动流程详解(一)

    环境 本文基于Spring Boot版本1.3.3, 使用了spring-boot-starter-web. 配置完成后,编写了代码如下: @SpringBootApplication public ...

  7. linux启动流程及自定义gurb

    linux 启动流程 POST BIOS(boot sequence) 所选择的启动设备次序的MBR中是否有引导程序, ----> MBR(bootloader) 提供内核列表 -------& ...

  8. linux启动流程

    看了深入理解linux内核一书的最后对linux启动流程的介绍,下面就把我能理解的写一下吧: bios(硬件加电自检POST,寻找第一个启动设备) the boot loader(可以从硬盘启动也可以 ...

  9. webapp启动流程

    webapp启动流程 看了这个教程,其实所有的webapp大致都是这个流程了.

  10. Tomcat源码分析之—具体启动流程分析

    从Tomcat启动调用栈可知,Bootstrap类的main方法为整个Tomcat的入口,在init初始化Bootstrap类的时候为设置Catalina的工作路径也就是Catalina_HOME信息 ...

随机推荐

  1. 前端Vuer,请收好这份《Vue组件单元测试》宝典,给自己多一些安全感

    大家好,我是 Kagol. 作为一名前端,在做业务开发的过程中,你是否曾经: 因为担心上线之后出bug,而反复手工验证自己负责的模块 不敢修改现有的"屎山"(别人写的或者是自己1年 ...

  2. 微服务性能分析工具 Pyroscope 初体验

    Go 自带接口性能分析工具 pprof,较为常用的有以下 4 种分析: CPU Profiling: CPU 分析,按照一定的频率采集所监听的应用程序 CPU(含寄存器)的使用情况,可确定应用程序在主 ...

  3. 使用sqlplus

    1. 执行一个SQL脚本文件 SQL>start file_name SQL>@ file_name 可以将多条sql语句保存在一个文本文件中,这样当要执行这个文件中的所有的sql语句时, ...

  4. SpringBoot3文件管理

    目录 一.简介 二.工程搭建 1.工程结构 2.依赖管理 三.上传下载 1.配置管理 2.上传下载 四.Excel文件 1.Excel创建 2.Excel读取 3.解析监听 4.导入导出 五.参考源码 ...

  5. maven系列:简介和安装配置(Mac、Linux、Windows、settings.xml、IDEA配置)

    目录 一.简介 二.安装 三.配置 Mac配置 Centos配置 Window配置 settings.xml配置 IDEA配置 一.简介 官网:https://maven.apache.org mav ...

  6. 【pytorch】目标检测:一文搞懂如何利用kaggle训练yolov5模型

    笔者的运行环境:python3.8+pytorch2.0.1+pycharm+kaggle.yolov5对python和pytorch版本是有要求的,python>=3.8,pytorch> ...

  7. [ABC305D] Sleep Log题解

    题目大意 给 \(N\) 个时刻: 当 \(i\) 为奇数时,\(A_i\) 表示刚刚起床的时刻. 当 \(i\) 为偶数时,\(A_i\) 表示开始睡觉的时刻. 有 \(Q\) 次询问,每次求在 \ ...

  8. Kurator,你的分布式云原生解决方案

    本文分享自华为云社区<DTSE Tech Talk | 第40期:Kurator,你的分布式云原生解决方案>,作者:华为云社区精选. 什么是分布式云原生? 中国信通院给出的定义:分布式云原 ...

  9. 【路由器】OpenWrt 配置使用

    目录 Web 界面 汉化 root 密码 ssh 升级 LuCI 美化 锐捷认证 MentoHUST MiniEAP 防火墙 开放端口 端口转发 IPv6 USB 安装 USB 驱动 自动挂载 Ext ...

  10. Linux ALSA 核心简单分析

    Linux 内核 ALSA 框架通过向用户空间导出多个设备文件,以使用户空间程序可以与内核的音频子系统交互,可以访问音频硬件设备. Linux 内核 ALSA 音频框架初始化 Linux 内核 ALS ...