启动篇-01配置加载与校验

NewKebeletCommand

cmd/kubelet/app/server.go
// 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

cmd/kubelet/app/options/options.go
// 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

cmd/kubelet/app/options/options.go
// 创建带有默认值的 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

pkg/kubelet/apis/config/scheme/scheme.go
// 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  
}

最后更新于

这有帮助吗?