启动篇-01配置加载与校验
NewKebeletCommand
// NewKubeletCommand 创建一个带有默认参数的 *cobra.Command 对象
func NewKubeletCommand() *cobra.Command {
// 解析命令行标识
cleanFlagSet := pflag.NewFlagSet(componentKubelet, pflag.ContinueOnError)
cleanFlagSet.SetNormalizeFunc(cliflag.WordSepNormalizeFunc)
// 创建一个带默认值的 KubeletFlags 对象
kubeletFlags := options.NewKubeletFlags()
// 创建一个带默认值的 KubeletConfiguration 对象
kubeletConfig, err := options.NewKubeletConfiguration()
// programmer error
if err != nil {
klog.ErrorS(err, "Failed to create a new kubelet configuration")
os.Exit(1)
}
cmd := &cobra.Command{
...
// kubelet 有特殊的标识解析要求来强制执行标识优先级规则,因此我们在下面的 Run 中手动执行所有解析
// DisableFlagParsing=true 提供了传递给 kubelet 的完整标识集合,而不受 Cobra 干扰
DisableFlagParsing: true,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
// 解析标识
if err := cleanFlagSet.Parse(args); err != nil {
return fmt.Errorf("failed to parse kubelet flag: %w", err)
}
// 检查是否有非法标识
cmds := cleanFlagSet.Args()
if len(cmds) > 0 {
return fmt.Errorf("unknown command %+s", cmds[0])
}
// 遇到 help 直接打印并返回
help, err := cleanFlagSet.GetBool("help")
if err != nil {
return errors.New(`"help" flag is non-bool, programmer error, please correct`)
}
if help {
return cmd.Help()
}
// 遇到 version 直接打印并返回
verflag.PrintAndExitIfRequested()
// 根据配置文件设置 feature gates
if err := utilfeature.DefaultMutableFeatureGate.SetFromMap(kubeletConfig.FeatureGates); err != nil {
return fmt.Errorf("failed to set feature gates from initial flags-based config: %w", err)
}
// 验证 kubeletFlags 是否合法
if err := options.ValidateKubeletFlags(kubeletFlags); err != nil {
return fmt.Errorf("failed to validate kubelet flags: %w", err)
}
// 检查 Infra 容器是否修改,已弃用,将在 1.27 删除,需要通过 CRI 设置
if cleanFlagSet.Changed("pod-infra-container-image") {
klog.InfoS("--pod-infra-container-image will not be pruned by the image garbage collector in kubelet and should also be set in the remote runtime")
}
// 如果有 kubelet 配置文件,则加载
if configFile := kubeletFlags.KubeletConfigFile; len(configFile) > 0 {
kubeletConfig, err = loadConfigFile(configFile)
if err != nil {
return fmt.Errorf("failed to load kubelet config file, error: %w, path: %s", err, configFile)
}
// 重新解析标识以设置标识优先级,详细信息查看 issue # 56171
if err := kubeletConfigFlagPrecedence(kubeletConfig, args); err != nil {
return fmt.Errorf("failed to precedence kubeletConfigFlag: %w", err)
}
// 根据新配置更新 feature gates
if err := utilfeature.DefaultMutableFeatureGate.SetFromMap(kubeletConfig.FeatureGates); err != nil {
return fmt.Errorf("failed to set feature gates from initial flags-based config: %w", err)
}
}
// 配置和标识解析完成后,现在我们可以初始化 log 了
logs.InitLogs()
if err := logsapi.ValidateAndApplyAsField(&kubeletConfig.Logging, utilfeature.DefaultFeatureGate, field.NewPath("logging")); err != nil {
return fmt.Errorf("initialize logging: %v", err)
}
cliflag.PrintFlags(cleanFlagSet)
// 验证本地配置,也是最终配置,动态配置已在 1.22 弃用且在 1.24 中删除
if err := kubeletconfigvalidation.ValidateKubeletConfiguration(kubeletConfig, utilfeature.DefaultFeatureGate); err != nil {
return fmt.Errorf("failed to validate kubelet configuration, error: %w, path: %s", err, kubeletConfig)
}
if (kubeletConfig.KubeletCgroups != "" && kubeletConfig.KubeReservedCgroup != "") && (strings.Index(kubeletConfig.KubeletCgroups, kubeletConfig.KubeReservedCgroup) != 0) {
klog.InfoS("unsupported configuration:KubeletCgroups is not within KubeReservedCgroup")
}
// 构建 kubeletServer,包括 KubeletFlags 和 KubeletConfiguration
kubeletServer := &options.KubeletServer{
KubeletFlags: *kubeletFlags,
KubeletConfiguration: *kubeletConfig,
}
// 构建默认 KubeletDeps
kubeletDeps, err := UnsecuredDependencies(kubeletServer, utilfeature.DefaultFeatureGate)
if err != nil {
return fmt.Errorf("failed to construct kubelet dependencies: %w", err)
}
if err := checkPermissions(); err != nil {
klog.ErrorS(err, "kubelet running with insufficient permissions")
}
// 生成 log 打印需要的 kubelet 配置
config := kubeletServer.KubeletConfiguration.DeepCopy()
for k := range config.StaticPodURLHeader {
config.StaticPodURLHeader[k] = []string{"<masked>"}
}
// 打印 kubelet 配置用于验证
klog.V(5).InfoS("KubeletConfiguration", "configuration", config)
// 设置 kubelet 的 ctx,用于 kubelet 终止
ctx := genericapiserver.SetupSignalContext()
utilfeature.DefaultMutableFeatureGate.AddMetrics()
// 运行 kubelet,执行 Run 方法,进入下一阶段
return Run(ctx, kubeletServer, kubeletDeps, utilfeature.DefaultFeatureGate)
},
}
// 隔离 cleanFlagSet 使其不会被全局标识污染
kubeletFlags.AddFlags(cleanFlagSet)
options.AddKubeletConfigFlags(cleanFlagSet, kubeletConfig)
options.AddGlobalFlags(cleanFlagSet)
cleanFlagSet.BoolP("help", "h", false, fmt.Sprintf("help for %s", cmd.Name()))
// ugly,但是必须这样,因为 Cobra 的默认 UsageFunc 和 HelpFunc 会污染全局标识
const usageFmt = "Usage:\n %s\n\nFlags:\n%s"
cmd.SetUsageFunc(func(cmd *cobra.Command) error {
fmt.Fprintf(cmd.OutOrStderr(), usageFmt, cmd.UseLine(), cleanFlagSet.FlagUsagesWrapped(2))
return nil
})
cmd.SetHelpFunc(func(cmd *cobra.Command, args []string) {
fmt.Fprintf(cmd.OutOrStdout(), "%s\n\n"+usageFmt, cmd.Long, cmd.UseLine(), cleanFlagSet.FlagUsagesWrapped(2))
})
return cmd
}NewKubeletFlags
// NewKubeletFlags 创建一个带默认值的 KubeletFlags
func NewKubeletFlags() *KubeletFlags {
return &KubeletFlags{
ContainerRuntimeOptions: *NewContainerRuntimeOptions(),
CertDirectory: "/var/lib/kubelet/pki",
RootDirectory: defaultRootDir,
MasterServiceNamespace: metav1.NamespaceDefault,
MaxContainerCount: -1,
MaxPerPodContainerCount: 1,
MinimumGCAge: metav1.Duration{Duration: 0},
RegisterSchedulable: true,
NodeLabels: make(map[string]string),
}
}NewKubeletConfiguration
// 创建带有默认值的 kubelet 配置
func NewKubeletConfiguration() (*kubeletconfig.KubeletConfiguration, error) {
// 创建 kubelet 的 schema
// schema 定义了序列化和反序列化 kubernetes API 对象的方法
// 用于将 GVK 与 Go 类型进行转换,以及不同版本的 Go 类型之间的映射
scheme, _, err := kubeletscheme.NewSchemeAndCodecs()
if err != nil {
return nil, err
}
// v1beta1 版本的 kubelet 配置
versioned := &v1beta1.KubeletConfiguration{}
// 设置各字段的默认值
scheme.Default(versioned)
// 默认 kubelet 配置
config := &kubeletconfig.KubeletConfiguration{}
// 用 v1beta1 版本的配置去覆盖默认 kubelet 配置
if err := scheme.Convert(versioned, config, nil); err != nil {
return nil, err
}
// 设置 legacy 默认值
applyLegacyDefaults(config)
return config, nil
}NewSchemeAndCodecs
// NewSchemeAndCodecs 是一个实用程序函数,它返回一个 Scheme 和 CodecFactory
// 这些 Scheme 和 CodecFactory 可以理解 kubeletconfig API 组中的类型
// 传递 mutator 可以调整 CodecFactory 的行为,例如启用严格解码
func NewSchemeAndCodecs(mutators ...serializer.CodecFactoryOptionsMutator) (*runtime.Scheme, *serializer.CodecFactory, error) {
scheme := runtime.NewScheme()
// 调用了register.go中的注册函数,对应资源:
// GroupVersion{Group: "kubelet.config.k8s.io", Version: runtime.APIVersionInternal}
// 注册了:KubeletConfiguration,SerializedNodeConfigSource 和 CredentialProviderConfig
if err := kubeletconfig.AddToScheme(scheme); err != nil {
return nil, nil, err
}
// 调用 kubeletconfigv1beta1 下的注册函数,对应资源:
// GroupVersion{Group: "kubelet.config.k8s.io", Version: "v1beta1"}
// 使用 v1beta1 下的 defaults.go 文件中 SetDefaults_KubeletConfiguration 为 scheme 设置了默认值
if err := kubeletconfigv1beta1.AddToScheme(scheme); err != nil {
return nil, nil, err
}
// 调用 kubeletconfigv1 下的注册函数,对应资源:
// GroupVersion{Group: "kubelet.config.k8s.io", Version: "v1"}
// 使用 v1 下的 defaults.go 文件中 SetDefaults_KubeletConfiguration 为 scheme 设置了默认值
if err := kubeletconfigv1.AddToScheme(scheme); err != nil {
return nil, nil, err
}
codecs := serializer.NewCodecFactory(scheme, mutators...)
return scheme, &codecs, nil
}最后更新于
这有帮助吗?