kube-scheduler源码解读(2)

在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 验证是否提供了version flag, 打印命令的flags

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)
	})
}

2 判断args参数

判断args参数是否为空,如果不为空,则打印错误,并退出执行

3 验证配置选项的设置是否有效

调用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 判断是否设置了“write-config-to”标志

如果设置了此标志,则执行以下代码:

   // 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函数的主要流程是:

  1. 根据o.ConfigFile参数的配置从文件加载或者从Options中加载配置参数
  2. 从Options中配置SecureServing,Authentication,Authorization参数
  3. 根据o.ShowHiddenMetricsForVersion参数来决定是否开启一些hidden的指标采集器

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
}

5 生成config

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}
}

6 注册configz

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
}

7 根据指定的配置开始执行scheduler组件

Run函数我们将在下一篇文章中进行详细的分析,敬请期待。

你可能感兴趣的:(Kubernetes)