云原生学习路线导航页(持续更新中)
本文是 Kubernetes api-server源码阅读 系列第三篇,主要学习 kubernetes api-server 的 源码
- 参考b站视频地址:Kubernetes源码开发之旅三
Kubernetes APIServer的地位类似于我们的数据库,必不可少
从业务上来说,APIServer并不是核心,它不处理具体的业务,具体的业务有其他组件来负责,如schedule、kubelet等
但是,业务的处理,却又少不了APIServer。
因此,APIServer地位极其重要
API Object
API Group
Legacy API Object
API Resource
定义一个命令对象
var rootCmd = &cobra.Command{
Use: "hugo",
Short: "Hugo is a very fast static site generator",
Long: `A Fast and Flexible Static Site Generator built with
love by spf13 and friends in Go.
Complete documentation is available at http://hugo.spf13.com`,
Run: func(cmd *cobra.Command, args []string) {
// Do Stuff Here
},
}
为命令指定参数 flags
编写flags和args的校验逻辑
编写 命令响应代码
执行命令
可以通过两种方法获取一个命令的 FlagSet,然后就可以对这个Set做 获取或添加操作了,二者略有不同:
func (c *Command) Flags() *flag.FlagSet
func (c *Command) PersistentFlags() *flag.FlagSet
在为FlagSet设置值的写法上,Flags()和PersistentFlags()是一样的,都有4种不同的写法,方法名称里有两个规定:
P
的表示 需要给参数指定一个短名称shorthand,如下面的gVar
的表示 需要给参数指定一个 接受对象,如下面的&prootCmd.Flags().Bool("gender", ......)
rootCmd.Flags().BoolP("gender", "g", ......)
rootCmd.Flags().BoolVar(&p, "gender", ......)
rootCmd.Flags().BoolVarP(&p, "gender", "g", ......)
rootCmd.PersistentFlags().Bool("gender", ......)
rootCmd.PersistentFlags().BoolP("gender", "g", ......)
rootCmd.PersistentFlags().BoolVar(&p, "gender", ......)
rootCmd.PersistentFlags().BoolVarP(&p, "gender", "g", ......)
Run功能的执行先后顺序如下:
PersistentPreRun
PreRun
Run
PostRun
PersistentPostRun
RunE功能的执行先后顺序如下:
main函数,在 /cmd/kube-apiserver/apiserver.go
中
app.NewAPIServerCommand()
就是创建了一个 Cobra.Command
cli
包下的 Run
方法,把这个command执行起来,其实就是调了command的Execute
方法func main() {
command := app.NewAPIServerCommand()
code := cli.Run(command)
os.Exit(code)
}
app.NewAPIServerCommand()
内容如下:
Cobra.Command
// NewAPIServerCommand creates a *cobra.Command object with default parameters
func NewAPIServerCommand() *cobra.Command {
// 用于包含所有用户可选flag的,最完整的一个结构体
// 用户在命令行的设置,最终都会合并到这个s中去
s := options.NewServerRunOptions()
cmd := &cobra.Command{
// 命令的名称
Use: "kube-apiserver",
// 命令的解释,长解释
Long: `The Kubernetes API server validates and configures data
for the api objects which include pods, services, replicationcontrollers, and
others. The API Server services REST operations and provides the frontend to the
cluster's shared state through which all other components interact.`,
// SilenceUsage 设置为 true 时,表示在命令执行遇到输入错误时,不显示使用方法
SilenceUsage: true,
// 在RunE方法执行之前,会先执行这个方法,做一些预设置
PersistentPreRunE: func(*cobra.Command, []string) error {
// 禁用 kube-apiserver 自己的警告
rest.SetDefaultWarningHandler(rest.NoWarnings{})
return nil
},
// 真正的执行方法,最后会返回一个 error
RunE: func(cmd *cobra.Command, args []string) error {
// 检查参数是否指定了 -version,如果指定了,则打印版本后退出
verflag.PrintAndExitIfRequested()
// 取出当前执行命令的 FlagSet (这个已经是 合并之后的了 = 默认+用户输入)
fs := cmd.Flags()
// Activate logging as soon as possible, after that
// show flags with the final logging configuration.
if err := s.Logs.ValidateAndApply(utilfeature.DefaultFeatureGate); err != nil {
return err
}
// 打印所有的flags
cliflag.PrintFlags(fs)
// 将s中,没有设置值的那些,进行默认值的设置,返回值completedOptions
completedOptions, err := Complete(s)
if err != nil {
return err
}
// 对completedOptions,进行校验(比如校验是否有冲突),校验失败,直接退出
if errs := completedOptions.Validate(); len(errs) != 0 {
return utilerrors.NewAggregate(errs)
}
// 真正的Server启动逻辑(会把options配置传进去,并传入一个 停止信号channel)
return Run(completedOptions, genericapiserver.SetupSignalHandler())
},
Args: func(cmd *cobra.Command, args []string) error {
// 做了参数的校验,不允许有任何的args,有的话,就报错
for _, arg := range args {
if len(arg) > 0 {
return fmt.Errorf("%q does not take any arguments, got %q", cmd.CommandPath(), args)
}
}
return nil
},
}
// 获取用户在命令行输入的flags
fs := cmd.Flags()
// 获取s中,默认的flags
namedFlagSets := s.Flags()
// 下面这段是将s中的默认设置,都合并到 命令cmd的flags 上去
verflag.AddFlags(namedFlagSets.FlagSet("global"))
globalflag.AddGlobalFlags(namedFlagSets.FlagSet("global"), cmd.Name(), logs.SkipLoggingConfigurationFlags())
options.AddCustomGlobalFlags(namedFlagSets.FlagSet("generic"))
for _, f := range namedFlagSets.FlagSets {
fs.AddFlagSet(f)
}
// 设置用法和帮助函数。打印我们需要的flags,而不是所有的flags。
cols, _, _ := term.TerminalSize(cmd.OutOrStdout())
cliflag.SetUsageAndHelpFunc(cmd, namedFlagSets, cols)
// 最后把command返回给main函数
return cmd
}
API Server并不是一个Server,而是一个Server的集合,包含了4个小Server
需要注意
Aggregation Server更像是一个分发器,由他来判断 请求应该由 Master APIServer处理,还是由用户自定义的Custom APIServer处理
由4.3.1可知,NewAPIServerCommand()
方法创建的 Cobra.Command
中,RunE
方法最后调用了Run(completedOptions, genericapiserver.SetupSignalHandler())
。本节我们就来学习这个Run方法
Run方法如下:
// Run runs the specified APIServer. This should never exit.
func Run(completeOptions completedServerRunOptions, stopCh <-chan struct{}) error {
// 为了方便debug,打印当前版本信息(包括git、编译器等)
klog.Infof("Version: %+v", version.Get())
// 记录golang的一些设置
klog.InfoS("Golang settings", "GOGC", os.Getenv("GOGC"), "GOMAXPROCS", os.Getenv("GOMAXPROCS"), "GOTRACEBACK", os.Getenv("GOTRACEBACK"))
// 这里就是构建我们说的 Server Chain 了,返回的就是 链头对象
server, err := CreateServerChain(completeOptions, stopCh)
if err != nil {
return err
}
prepared, err := server.PrepareRun()
if err != nil {
return err
}
return prepared.Run(stopCh)
}
下面我们进入 CreateServerChain()
,查看 Server Chain
的创建过程
// CreateServerChain creates the apiservers connected via delegation.
func CreateServerChain(completedOptions completedServerRunOptions, stopCh <-chan struct{}) (*aggregatorapiserver.APIAggregator, error) {
// 先根据 completedOptions,创建出 kube API Server 的 Config(实际上对好几个Server都起作用)
kubeAPIServerConfig, serviceResolver, pluginInitializer, err := CreateKubeAPIServerConfig(completedOptions)
if err != nil {
return nil, err
}
// 然后 创建出 Extension server 的Config
apiExtensionsConfig, err := createAPIExtensionsConfig(*kubeAPIServerConfig.GenericConfig, kubeAPIServerConfig.ExtraConfig.VersionedInformers, pluginInitializer, completedOptions.ServerRunOptions, completedOptions.MasterCount,
serviceResolver, webhook.NewDefaultAuthenticationInfoResolverWrapper(kubeAPIServerConfig.ExtraConfig.ProxyTransport, kubeAPIServerConfig.GenericConfig.EgressSelector, kubeAPIServerConfig.GenericConfig.LoopbackClientConfig, kubeAPIServerConfig.GenericConfig.TracerProvider))
if err != nil {
return nil, err
}
// 下面就开始创建Server了
// 第一个创建的是 notFoundHandler
notFoundHandler := notfoundhandler.New(kubeAPIServerConfig.GenericConfig.Serializer, genericapifilters.NoMuxAndDiscoveryIncompleteKey)
// 第二个创建的是 apiExtensionsServer,并将链条下一个设置为 notFoundHandler
apiExtensionsServer, err := createAPIExtensionsServer(apiExtensionsConfig, genericapiserver.NewEmptyDelegateWithCustomHandler(notFoundHandler))
if err != nil {
return nil, err
}
// 第三个创建的是 kubeAPIServer,并将链条下一个设置为 apiExtensionsServer
kubeAPIServer, err := CreateKubeAPIServer(kubeAPIServerConfig, apiExtensionsServer.GenericAPIServer)
if err != nil {
return nil, err
}
// 聚合Server在最后,先创建 Aggregator Config
aggregatorConfig, err := createAggregatorConfig(*kubeAPIServerConfig.GenericConfig, completedOptions.ServerRunOptions, kubeAPIServerConfig.ExtraConfig.VersionedInformers, serviceResolver, kubeAPIServerConfig.ExtraConfig.ProxyTransport, pluginInitializer)
if err != nil {
return nil, err
}
// 第四个创建的是 aggregatorServer,并将链条下一个设置为 kubeAPIServer
aggregatorServer, err := createAggregatorServer(aggregatorConfig, kubeAPIServer.GenericAPIServer, apiExtensionsServer.Informers)
if err != nil {
// we don't need special handling for innerStopCh because the aggregator server doesn't create any go routines
return nil, err
}
// 最后将链头 aggregatorServer 返回
return aggregatorServer, nil
}
我们暂时说到这里,不再深入了
API Server 通过暴露Restful服务,对外提供API Object的能力。那么API Object究竟是什么时候完成装载的?
流程图如下:
下面用文字描述一下整个过程:
在上面的API Server的Run()中,我们看到有一个 CreateKubeAPIServer()
,是创建 Master Server
的,该方法代码如下:
// CreateKubeAPIServer creates and wires a workable kube-apiserver
func CreateKubeAPIServer(kubeAPIServerConfig *controlplane.Config, delegateAPIServer genericapiserver.DelegationTarget) (*controlplane.Instance, error) {
// 先调用Complete(),对 kubeAPIServer 的Config进行最后的设置
// 然后就调用了New(),该方法才是去完成 Master Server 创建的核心方法
kubeAPIServer, err := kubeAPIServerConfig.Complete().New(delegateAPIServer)
if err != nil {
return nil, err
}
return kubeAPIServer, nil
}
上述代码 调用的 New(),是属于 pkg/controlplane
包下的 instance.go
文件
func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget) (*controlplane.Instance, error)
先理解一下New()要做什么事情,主要做两件事:
知道New()要做的事情后,我们看一下 New() 的源码
func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget) (*Instance, error) {
......
// 创建了一个 Instance 结构体变量,待会作为返回值
m := &Instance{
GenericAPIServer: s,
ClusterAuthenticationInfo: c.ExtraConfig.ClusterAuthenticationInfo,
}
// 安装 LegacyAPI,方法内部会把元老APIObject装载进来,下面还会详细介绍
if err := m.InstallLegacyAPI(&c, c.GenericConfig.RESTOptionsGetter); err != nil {
return nil, err
}
// 这里是为每一个APIGroup,创建一个 RESTStorageProvider,放在一个切片里
restStorageProviders := []RESTStorageProvider{
apiserverinternalrest.StorageProvider{},
authenticationrest.RESTStorageProvider{Authenticator: c.GenericConfig.Authentication.Authenticator, APIAudiences: c.GenericConfig.Authentication.APIAudiences},
authorizationrest.RESTStorageProvider{Authorizer: c.GenericConfig.Authorization.Authorizer, RuleResolver: c.GenericConfig.RuleResolver},
autoscalingrest.RESTStorageProvider{},
batchrest.RESTStorageProvider{},
certificatesrest.RESTStorageProvider{},
coordinationrest.RESTStorageProvider{},
discoveryrest.StorageProvider{},
networkingrest.RESTStorageProvider{},
noderest.RESTStorageProvider{},
policyrest.RESTStorageProvider{},
rbacrest.RESTStorageProvider{Authorizer: c.GenericConfig.Authorization.Authorizer},
schedulingrest.RESTStorageProvider{},
storagerest.RESTStorageProvider{},
flowcontrolrest.RESTStorageProvider{InformerFactory: c.GenericConfig.SharedInformerFactory},
appsrest.StorageProvider{},
admissionregistrationrest.RESTStorageProvider{},
eventsrest.RESTStorageProvider{TTL: c.ExtraConfig.EventTTL},
}
// 安装 APIs,方法内部会把所有 group 下的 API Object 装载进来
if err := m.InstallAPIs(c.ExtraConfig.APIResourceConfigSource, c.GenericConfig.RESTOptionsGetter, restStorageProviders...); err != nil {
return nil, err
}
......
return m, nil
}
New()
中 m.InstallLegacyAPI(&c, c.GenericConfig.RESTOptionsGetter)
源码分析
// InstallLegacyAPI will install the legacy APIs for the restStorageProviders if they are enabled.
func (m *Instance) InstallLegacyAPI(c *completedConfig, restOptionsGetter generic.RESTOptionsGetter) error {
// 为 元老APIObject们,创建一个 RESTStorageProvider
legacyRESTStorageProvider := corerest.LegacyRESTStorageProvider{
StorageFactory: c.ExtraConfig.StorageFactory,
ProxyTransport: c.ExtraConfig.ProxyTransport,
KubeletClientConfig: c.ExtraConfig.KubeletClientConfig,
EventTTL: c.ExtraConfig.EventTTL,
ServiceIPRange: c.ExtraConfig.ServiceIPRange,
SecondaryServiceIPRange: c.ExtraConfig.SecondaryServiceIPRange,
ServiceNodePortRange: c.ExtraConfig.ServiceNodePortRange,
LoopbackClientConfig: c.GenericConfig.LoopbackClientConfig,
ServiceAccountIssuer: c.ExtraConfig.ServiceAccountIssuer,
ExtendExpiration: c.ExtraConfig.ExtendExpiration,
ServiceAccountMaxExpiration: c.ExtraConfig.ServiceAccountMaxExpiration,
APIAudiences: c.GenericConfig.Authentication.APIAudiences,
}
// 使用这个 RESTStorageProvider,创建一个 APIGroupInfo 出来
// APIGroupInfo 包含了该Group的所有APIObject,还含有一个map。记录了rest.storage的信息
legacyRESTStorage, apiGroupInfo, err := legacyRESTStorageProvider.NewLegacyRESTStorage(c.ExtraConfig.APIResourceConfigSource, restOptionsGetter)
if err != nil {
return fmt.Errorf("error building core storage: %v", err)
}
if len(apiGroupInfo.VersionedResourcesStorageMap) == 0 { // if all core storage is disabled, return.
return nil
}
......
// 在GenericAPIServer中,完成 LegacyAPIGroupInfo 的安装
if err := m.GenericAPIServer.InstallLegacyAPIGroup(genericapiserver.DefaultLegacyAPIPrefix, &apiGroupInfo); err != nil {
return fmt.Errorf("error in registering group versions: %v", err)
}
return nil
}
New()
中 m.InstallAPIs(c.ExtraConfig.APIResourceConfigSource, c.GenericConfig.RESTOptionsGetter, restStorageProviders...)
源码分析
func (m *Instance) InstallAPIs(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter, restStorageProviders ...RESTStorageProvider) error {
// 上来先创建了一个 APIGroupInfo的切片,名称为 apiGroupsInfo
apiGroupsInfo := []*genericapiserver.APIGroupInfo{}
......
// 遍历参数传进来的 restStorageProviders 切片
for _, restStorageBuilder := range restStorageProviders {
groupName := restStorageBuilder.GroupName()
// 对每一个StorageProvider,都创建一个apiGroupInfo出来
apiGroupInfo, err := restStorageBuilder.NewRESTStorage(apiResourceConfigSource, restOptionsGetter)
......
// 把创建出来的apiGroupInfo,都加入切片 apiGroupsInfo
apiGroupsInfo = append(apiGroupsInfo, &apiGroupInfo)
}
// 最后将 apiGroupsInfo切片,传入方法,完成 所有APIObject 的安装
if err := m.GenericAPIServer.InstallAPIGroups(apiGroupsInfo...); err != nil {
return fmt.Errorf("error in registering group versions: %v", err)
}
return nil
}
Scheme是一个结构体,内含处理 内外部Version之间转换、GVK和Go Type之间转换 的数据和方法。
Scheme有很多应用场景,下面列举3个:
GVK 与 Go Type之间转换
填充API Object的默认值
内外部Version之间Convert
所有外部Version之间的转换,都会先转成内部Version,然后再转成另一个外部version。
v1-->internal version-->v2
v2-->internal version-->v1
转换函数都是自动生成,然后注册到scheme之内
如下图:
json/yaml
的 apiObject
数据,apiVersion是v2
,通过 Marshal
的方法转成 v2 的 go struct
结构API Server
所有的算法和逻辑,都是基于 internal version
写的 Scheme
转成内部版本 internal version
,在程序中使用,处理完后,再转成v2向外输出。json/yaml
的apiVersion是v1
,也是可以正常处理的。这样的设计,就使得版本的兼容性变得特别好,程序也只需要处理Internal Version,编写也变得简单Scheme的结构体如下:
type Scheme struct {
// map,记录 gvk-->type。其中type是通过反射的方式记录的
gvkToType map[schema.GroupVersionKind]reflect.Type
// map,记录 type-->gvk
typeToGVK map[reflect.Type][]schema.GroupVersionKind
// map,记录 type-->gvk。像pod这种,只有一个version的,就记录在这里。
unversionedTypes map[reflect.Type]schema.GroupVersionKind
// map,记录 gvk-->type。像pod这种,只有一个version的,就记录在这里。
unversionedKinds map[string]reflect.Type
// Map from version and resource to the corresponding func to convert
// resource field labels in that version to internal version.
fieldLabelConversionFuncs map[schema.GroupVersionKind]FieldLabelConversionFunc
// map,记录默认方法。为某一个具体的type,设置默认值
defaulterFuncs map[reflect.Type]func(interface{})
// 转换器
converter *conversion.Converter
// 记录version的优先级。当没有选择version的时候,优先使用谁
versionPriority map[string][]string
// observedVersions keeps track of the order we've seen versions during type registration
observedVersions []schema.GroupVersion
// schemeName is the name of this scheme. If you don't specify a name, the stack of the NewScheme caller will be used.
// This is useful for error reporting to indicate the origin of the scheme.
schemeName string
}
Scheme的方法
Scheme是什么时候填充的?
流程图如下:
下面用文字描述一下整个过程:
在 cmd/kube-apiserver/app/server.go
,导入了这么一个包:pkg/controlplane
pkg/controlplane
导入过程中,它又导入了 pkg/controlplane/import_known_versions.go
文件的所有 import
我们看一下 pkg/controlplane/import_known_versions.go
文件的源代码
package controlplane
import (
// These imports are the API groups the API server will support.
_ "k8s.io/kubernetes/pkg/apis/admission/install"
_ "k8s.io/kubernetes/pkg/apis/admissionregistration/install"
_ "k8s.io/kubernetes/pkg/apis/apiserverinternal/install"
_ "k8s.io/kubernetes/pkg/apis/apps/install"
_ "k8s.io/kubernetes/pkg/apis/authentication/install"
_ "k8s.io/kubernetes/pkg/apis/authorization/install"
_ "k8s.io/kubernetes/pkg/apis/autoscaling/install"
_ "k8s.io/kubernetes/pkg/apis/batch/install"
_ "k8s.io/kubernetes/pkg/apis/certificates/install"
_ "k8s.io/kubernetes/pkg/apis/coordination/install"
_ "k8s.io/kubernetes/pkg/apis/core/install"
_ "k8s.io/kubernetes/pkg/apis/discovery/install"
_ "k8s.io/kubernetes/pkg/apis/events/install"
_ "k8s.io/kubernetes/pkg/apis/extensions/install"
_ "k8s.io/kubernetes/pkg/apis/flowcontrol/install"
_ "k8s.io/kubernetes/pkg/apis/imagepolicy/install"
_ "k8s.io/kubernetes/pkg/apis/networking/install"
_ "k8s.io/kubernetes/pkg/apis/node/install"
_ "k8s.io/kubernetes/pkg/apis/policy/install"
_ "k8s.io/kubernetes/pkg/apis/rbac/install"
_ "k8s.io/kubernetes/pkg/apis/scheduling/install"
_ "k8s.io/kubernetes/pkg/apis/storage/install"
)
我们挑一个常见的 "_ k8s.io/kubernetes/pkg/apis/apps/install"
看一看
install包下只有一个文件:install.go
install.go
中,通过 init()
,触发 Install()
apps.AddToScheme(scheme)
就是把 apps 包下的所有 APIObject 加入Scheme中v1beta1.AddToScheme(scheme)
就是把 v1beta1 版本所有 APIObject 加入Scheme中package install
import (
"k8s.io/apimachinery/pkg/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/apps"
"k8s.io/kubernetes/pkg/apis/apps/v1"
"k8s.io/kubernetes/pkg/apis/apps/v1beta1"
"k8s.io/kubernetes/pkg/apis/apps/v1beta2"
)
func init() {
Install(legacyscheme.Scheme)
}
// Install registers the API group and adds types to a scheme
func Install(scheme *runtime.Scheme) {
utilruntime.Must(apps.AddToScheme(scheme))
utilruntime.Must(v1beta1.AddToScheme(scheme))
utilruntime.Must(v1beta2.AddToScheme(scheme))
utilruntime.Must(v1.AddToScheme(scheme))
utilruntime.Must(scheme.SetVersionPriority(v1.SchemeGroupVersion, v1beta2.SchemeGroupVersion, v1beta1.SchemeGroupVersion))
}
综上所述,在API Server一启动的时候,利用pkg的import机制,就把scheme填充上了,属于非常靠前的了