在kube-scheduler源码解读的第一篇文章中,我们简要分析了kubernetes scheduler组件的启动过程和pod调度的主要流程。
从本篇文章开始,我们将详细的分析kubernetes scheduler组件启动和pod调度过程中一些重要的流程。
本篇文章我们主要关注runCommand函数详细执行流程:
// runCommand runs the scheduler.
func runCommand(cmd *cobra.Command, args []string, opts *options.Options, registryOptions ...Option) error {
// 1. 验证version flag,并打印flags
verflag.PrintAndExitIfRequested()
utilflag.PrintFlags(cmd.Flags())
// 2. 判断args参数
if len(args) != 0 {
fmt.Fprint(os.Stderr, "arguments are not supported\n")
}
// 3. 验证配置选项的设置是否有效
if errs := opts.Validate(); len(errs) > 0 {
return utilerrors.NewAggregate(errs)
}
// 4. 判断是否将配置信息写入到文件中
if len(opts.WriteConfigTo) > 0 {
c := &schedulerserverconfig.Config{}
if err := opts.ApplyTo(c); err != nil {
return err
}
if err := options.WriteConfigFile(opts.WriteConfigTo, &c.ComponentConfig); err != nil {
return err
}
klog.Infof("Wrote configuration to: %s\n", opts.WriteConfigTo)
return nil
}
// 5. 获取config
c, err := opts.Config()
if err != nil {
return err
}
// Get the completed config
cc := c.Complete()
// 6. 注册configz
// Configz registration.
if cz, err := configz.New("componentconfig"); err == nil {
cz.Set(cc.ComponentConfig)
} else {
return fmt.Errorf("unable to register configz: %s", err)
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// 7. 根据根据指定的配置开始执行scheduler组件
return Run(ctx, cc, registryOptions...)
}
runCommand函数的主要流程为:
1)验证是否提供了version flag,打印命令的flags
2)判断args参数
3)验证配置选项的设置是否有效
4)判断是否提供了“write-config-to”flag
5)获取config
6)注册configz
7)根据根据指定的配置开始执行scheduler组件
1)runCommand函数刚开始先验证执行kube-scheduler命令时是否提供了version flag,如果提供了则调用PrintAndExitIfRequested函数打印版本号并退出执行。
// PrintAndExitIfRequested will check if the -version flag was passed
// and, if so, print the version and exit.
func PrintAndExitIfRequested() {
if *versionFlag == VersionRaw {
fmt.Printf("%#v\n", version.Get())
os.Exit(0)
} else if *versionFlag == VersionTrue {
fmt.Printf("Kubernetes %s\n", version.Get())
os.Exit(0)
}
}
2)如果没有提供version flag,则继续调用PrintFlags函数打印kube-scheduler命令的flags
// PrintFlags logs the flags in the flagset
func PrintFlags(flags *pflag.FlagSet) {
flags.VisitAll(func(flag *pflag.Flag) {
klog.V(1).Infof("FLAG: --%s=%q", flag.Name, flag.Value)
})
}
判断args参数是否为空,如果不为空,则打印错误,并退出执行
调用Validate函数验证配置参数的有效性
// Validate validates all the required options.
func (o *Options) Validate() []error {
var errs []error
if err := validation.ValidateKubeSchedulerConfiguration(&o.ComponentConfig).ToAggregate(); err != nil {
errs = append(errs, err.Errors()...)
}
errs = append(errs, o.SecureServing.Validate()...)
errs = append(errs, o.CombinedInsecureServing.Validate()...)
errs = append(errs, o.Authentication.Validate()...)
errs = append(errs, o.Authorization.Validate()...)
errs = append(errs, o.Deprecated.Validate()...)
errs = append(errs, metrics.ValidateShowHiddenMetricsVersion(o.ShowHiddenMetricsForVersion)...)
return errs
}
如果设置了此标志,则执行以下代码:
// 4. 判断是否将配置信息写入到文件中
if len(opts.WriteConfigTo) > 0 {
c := &schedulerserverconfig.Config{}
if err := opts.ApplyTo(c); err != nil {
return err
}
if err := options.WriteConfigFile(opts.WriteConfigTo, &c.ComponentConfig); err != nil {
return err
}
klog.Infof("Wrote configuration to: %s\n", opts.WriteConfigTo)
return nil
}
1)声明一个Config结构体,结构体定义如下:
// Config has all the context to run a Scheduler
type Config struct {
// ComponentConfig is the scheduler server's configuration object.
ComponentConfig kubeschedulerconfig.KubeSchedulerConfiguration
// LoopbackClientConfig is a config for a privileged loopback connection
LoopbackClientConfig *restclient.Config
InsecureServing *apiserver.DeprecatedInsecureServingInfo // nil will disable serving on an insecure port
InsecureMetricsServing *apiserver.DeprecatedInsecureServingInfo // non-nil if metrics should be served independently
Authentication apiserver.AuthenticationInfo
Authorization apiserver.AuthorizationInfo
SecureServing *apiserver.SecureServingInfo
Client clientset.Interface
InformerFactory informers.SharedInformerFactory
PodInformer coreinformers.PodInformer
// TODO: Remove the following after fully migrating to the new events api.
CoreEventClient v1core.EventsGetter
CoreBroadcaster record.EventBroadcaster
EventClient v1beta1.EventsGetter
Broadcaster events.EventBroadcaster
// LeaderElection is optional.
LeaderElection *leaderelection.LeaderElectionConfig
}
2)将kube-scheduler命令的配置选项通过调用ApplyTo函数设置到上边生命的Config对象
// ApplyTo applies the scheduler options to the given scheduler app configuration.
func (o *Options) ApplyTo(c *schedulerappconfig.Config) error {
if len(o.ConfigFile) == 0 {
c.ComponentConfig = o.ComponentConfig
// only apply deprecated flags if no config file is loaded (this is the old behaviour).
if err := o.Deprecated.ApplyTo(&c.ComponentConfig); err != nil {
return err
}
if err := o.CombinedInsecureServing.ApplyTo(c, &c.ComponentConfig); err != nil {
return err
}
} else {
cfg, err := loadConfigFromFile(o.ConfigFile)
if err != nil {
return err
}
if err := validation.ValidateKubeSchedulerConfiguration(cfg).ToAggregate(); err != nil {
return err
}
// use the loaded config file only, with the exception of --address and --port. This means that
// none of the deprecated flags in o.Deprecated are taken into consideration. This is the old
// behaviour of the flags we have to keep.
c.ComponentConfig = *cfg
if err := o.CombinedInsecureServing.ApplyToFromLoadedConfig(c, &c.ComponentConfig); err != nil {
return err
}
}
if err := o.SecureServing.ApplyTo(&c.SecureServing, &c.LoopbackClientConfig); err != nil {
return err
}
if o.SecureServing != nil && (o.SecureServing.BindPort != 0 || o.SecureServing.Listener != nil) {
if err := o.Authentication.ApplyTo(&c.Authentication, c.SecureServing, nil); err != nil {
return err
}
if err := o.Authorization.ApplyTo(&c.Authorization); err != nil {
return err
}
}
if len(o.ShowHiddenMetricsForVersion) > 0 {
metrics.SetShowHidden()
}
return nil
}
ApplyTo函数的主要流程是:
3)调用WriteConfigFile函数将config参数以YAML的格式写入到指定的文件中
// WriteConfigFile writes the config into the given file name as YAML.
func WriteConfigFile(fileName string, cfg *kubeschedulerconfig.KubeSchedulerConfiguration) error {
const mediaType = runtime.ContentTypeYAML
info, ok := runtime.SerializerInfoForMediaType(kubeschedulerscheme.Codecs.SupportedMediaTypes(), mediaType)
if !ok {
return fmt.Errorf("unable to locate encoder -- %q is not a supported media type", mediaType)
}
encoder := kubeschedulerscheme.Codecs.EncoderForVersion(info.Serializer, kubeschedulerconfigv1alpha2.SchemeGroupVersion)
configFile, err := os.Create(fileName)
if err != nil {
return err
}
defer configFile.Close()
if err := encoder.Encode(cfg, configFile); err != nil {
return err
}
return nil
}
1)调用Config函数根据配置的Options来构造一个Config对象(Config结构体定义参见4.1)
// Config return a scheduler config object
func (o *Options) Config() (*schedulerappconfig.Config, error) {
if o.SecureServing != nil {
if err := o.SecureServing.MaybeDefaultWithSelfSignedCerts("localhost", nil, []net.IP{net.ParseIP("127.0.0.1")}); err != nil {
return nil, fmt.Errorf("error creating self-signed certificates: %v", err)
}
}
c := &schedulerappconfig.Config{}
if err := o.ApplyTo(c); err != nil {
return nil, err
}
// Prepare kube clients.
client, leaderElectionClient, eventClient, err := createClients(c.ComponentConfig.ClientConnection, o.Master, c.ComponentConfig.LeaderElection.RenewDeadline.Duration)
if err != nil {
return nil, err
}
coreBroadcaster := record.NewBroadcaster()
// Set up leader election if enabled.
var leaderElectionConfig *leaderelection.LeaderElectionConfig
if c.ComponentConfig.LeaderElection.LeaderElect {
// Use the scheduler name in the first profile to record leader election.
coreRecorder := coreBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: c.ComponentConfig.Profiles[0].SchedulerName})
leaderElectionConfig, err = makeLeaderElectionConfig(c.ComponentConfig.LeaderElection, leaderElectionClient, coreRecorder)
if err != nil {
return nil, err
}
}
c.Client = client
c.InformerFactory = informers.NewSharedInformerFactory(client, 0)
c.PodInformer = scheduler.NewPodInformer(client, 0)
c.EventClient = eventClient.EventsV1beta1()
c.CoreEventClient = eventClient.CoreV1()
c.CoreBroadcaster = coreBroadcaster
c.LeaderElection = leaderElectionConfig
return c, nil
}
2)根据上边生成的Config对象,调用Complete函数构造一个CompletedConfig对象
CompletedConfig结构体的定义如下:
// CompletedConfig same as Config, just to swap private object.
type CompletedConfig struct {
// Embed a private pointer that cannot be instantiated outside of this package.
*completedConfig
}
Complete函数定义如下:
// Complete fills in any fields not set that are required to have valid data. It's mutating the receiver.
func (c *Config) Complete() CompletedConfig {
cc := completedConfig{c}
if c.InsecureServing != nil {
c.InsecureServing.Name = "healthz"
}
if c.InsecureMetricsServing != nil {
c.InsecureMetricsServing.Name = "metrics"
}
apiserver.AuthorizeClientBearerToken(c.LoopbackClientConfig, &c.Authentication, &c.Authorization)
return CompletedConfig{&cc}
}
configz包主要是为kubernetes的组件提供/configz接口,可以调用此接口获取对应组件的配置参数。
1)调用New函数生成一个Config对象
Config对象定义如下:
// Config is a handle to a ComponentConfig object. Don't create these directly;
// use New() instead.
type Config struct {
val interface{}
}
New函数定义如下:
// New creates a Config object with the given name. Each Config is registered
// with this package's "/configz" handler.
func New(name string) (*Config, error) {
configsGuard.Lock()
defer configsGuard.Unlock()
if _, found := configs[name]; found {
return nil, fmt.Errorf("register config %q twice", name)
}
newConfig := Config{}
configs[name] = &newConfig
return &newConfig, nil
}
2)调用Set函数将ComponentConfig对象设置到Config对象上
// Set sets the ComponentConfig for this Config.
func (v *Config) Set(val interface{}) {
configsGuard.Lock()
defer configsGuard.Unlock()
v.val = val
}
Run函数我们将在下一篇文章中进行详细的分析,敬请期待。