我是靠谱客的博主 调皮纸飞机,这篇文章主要介绍一个controller怎么调用另个controller 带参数_kubernetes kube-controller-manager 源码分析...,现在分享给大家,希望可以做个参考。

ff26ad449263d020379e64c208f1df2b.png
  • 1. 研究背景
  • 2. 源码分析
    • 2.1 目录结构
      • 2.1.1 controller-manager
      • 2.1.2 kube-controller-manager
      • 2.1.3 pkg/controler
    • 2.2 kube-controller-manage 如何启动各种controllers
      • 2.2.1 cobra.Command 分析
        • 2.2.1.1 main 分析
        • 2.2.1.2 NewControllerManagerCommand
        • 2.2.1.3 command.Execute()
      • 2.2.2 cobra.Command.Run 分析
      • 2.2.3 StartControllers 分析
      • 2.2.3 startJobController
    • 2.3 实现不同controllers差异
      • 2.3.1 参数差异
      • 2.3.2 参数校验
      • 2.3.3 controller 返回结果
  • 3. 架构分析
    • 3.1 独立kube-contrller
    • 3.2 config 和 option独立
    • 3.3 将informer作为参数传递给 NewControllerStruct
    • 3.4 使用Cobra

1. 研究背景

kubernetes版本:release-1.15

分析kube-controller-manager源码出于以下问题:

  • 如何start所有controllers
  • start controllers如何体现各自差异,比如传递参数,校验,返回结果处理等
  • 体会架构的特点
    • 为什么独立kube-controller结构
    • config 和option包含信息大致一样,为什么要分开
    • 为什么将informer 作为参数传入各自controller

2. 源码分析

在描述源码分析时,首先会列出重点目录结构及每块主要作用,然后分别对研究背景中提出的三个问题进行分析。

2.1 目录结构

在分析源码中,主要涉及的二级目录是:

  • cmd/controller-manager
  • cmd/kube-controller-manager
  • pkg/controler

2.1.1 controller-manager

controler-manager没有主干逻辑,主要是辅助kube-controller-manager模块,将Kubernetes controller 非核心模块抽象出来,从而精简了kube-controller-manager逻辑。当然controler-manager也被其它模块调用。

实现的核心功能有:

  • Profiling
  • Prometheus Exporter
  • 非核心模块的options,部分shared with kube-controller-manager
  • some help funcs

controller-manager 目录结构如下:

  • serve.go: Profiling,Prometheus Exporter
  • helper.go: some help funcs
  • options: 非核心模块的options
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
cmd/controller-manager/ ├── OWNERS └── app ├── BUILD ├── helper.go ├── helper_test.go ├── options │ ├── BUILD │ ├── cloudprovider.go │ ├── debugging.go │ ├── generic.go │ ├── kubecloudshared.go │ └── servicecontroller.go └── serve.go

2.1.2 kube-controller-manager

kube-controller-manager实现启动所有controllers的主干逻辑。

kube-controller-manager 目录结构如下:

  • controller-manager.go: main启动入口,使用了cobra.Command
  • app/config: 是controller manager 的 main context object
    • 各种controller关心的参数,和pkg/controlelr/apis/config对应
    • kubeconfig(kubeconfig 衍生出ClientSet,LeaderElectionClient)
    • ClientSet
    • LeaderElectionClient
    • 安全相关组件配置文件
    • EventRecorder
  • app/options: 各种controller关心的参数,通过命令行参数获取,然后赋值给app/config
    • 每个controller对应一个options文件
    • 每个controller option 文件实现func一致
      • AddFlags 获取cmd 参数
      • ApplyTo将options参数赋值给pkg/controlelr/apis/config 参数
      • Validate 对参数进行校验,目前除了GenericController,其它controller Validate都是空
        • GenericController 校验内容是:k8s controllers 采用白名单机制运行,校验判断运行的controller是否合法
    • options.go: 是各种controller options 集合
      • 实现AddFlags、ApplyTo、Validate
      • Config(): 将options 赋值给app/config
  • app/*.go
    • 每个文件包含具体的controller run 入口。文件命令式根据api路径来的,比如batch.go 包含startJobController、startCronJobController
    • plugins.go: 获取各种Cloud providers插件,主要是针对volume plugins。
    • controllermanager.go: 是new cobra.Command 入口,包含controller run运行逻辑,是最重要的部分。
      • NewControllerManagerCommand():new cobra.Command
      • Run():start controllers具体运行逻辑
      • ControllerContext:context object for controller。包含controller-manager运行的所有内容,一般作为参数
      • KnownControllers():获取白名单中所有的controllers
      • StartControllers: 被Run() 调用
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
cmd/kube-controller-manager/ ├── BUILD ├── OWNERS ├── app │ ├── BUILD │ ├── apps.go │ ├── autoscaling.go │ ├── batch.go │ ├── bootstrap.go │ ├── certificates.go │ ├── cloudproviders.go │ ├── config │ │ ├── BUILD │ │ └── config.go │ ├── controllermanager.go │ ├── core.go │ ├── core_test.go │ ├── import_known_versions.go │ ├── options │ │ ├── BUILD │ │ ├── attachdetachcontroller.go │ │ ├── csrsigningcontroller.go │ │ ├── daemonsetcontroller.go │ │ ├── deploymentcontroller.go │ │ ├── deprecatedcontroller.go │ │ ├── endpointcontroller.go │ │ ├── garbagecollectorcontroller.go │ │ ├── hpacontroller.go │ │ ├── jobcontroller.go │ │ ├── namespacecontroller.go │ │ ├── nodeipamcontroller.go │ │ ├── nodelifecyclecontroller.go │ │ ├── options.go │ │ ├── options_test.go │ │ ├── persistentvolumebindercontroller.go │ │ ├── podgccontroller.go │ │ ├── replicasetcontroller.go │ │ ├── replicationcontroller.go │ │ ├── resourcequotacontroller.go │ │ ├── serviceaccountcontroller.go │ │ └── ttlafterfinishedcontroller.go │ ├── plugins.go │ ├── policy.go │ ├── rbac.go │ └── testing │ ├── BUILD │ └── testserver.go └── controller-manager.go

2.1.3 pkg/controler

pkg/controller 是每个controller实现的具体逻辑。

pkg/controler目录结构如下:

  • pkg/controller/apis: 定义每个controller启动的参数
  • 其它每个文件夹实现各自controller 逻辑
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
wanlei$ tree pkg/controller/ -L 1 pkg/controller/ ├── BUILD ├── OWNERS ├── apis ├── bootstrap ├── certificates ├── client_builder.go ├── client_builder_dynamic.go ├── cloud ├── clusterroleaggregation ├── controller_ref_manager.go ├── controller_ref_manager_test.go ├── controller_utils.go ├── controller_utils_test.go ├── cronjob ├── daemon ├── deployment ├── disruption ├── doc.go ├── endpoint ├── garbagecollector ├── history ├── informer_factory.go ├── job ├── lookup_cache.go ├── namespace ├── nodeipam ├── nodelifecycle ├── podautoscaler ├── podgc ├── replicaset ├── replication ├── resourcequota ├── route ├── service ├── serviceaccount ├── statefulset ├── testutil ├── ttl ├── ttlafterfinished ├── util └── volume

2.2 kube-controller-manage 如何启动各种controllers

kube-controller-manager使用cobra.Command工具作为启动入口,cobra.Command可以自定义Run function(实现run controllers 逻辑),当执行command.Execute()时会调用前面的Run func。

在实现启动controllers时,kube-controller-manager使用白名单机制,所有的启动controllers都事先通过map[string]ControllerRunFunc 预先定义好。

2.2.1 cobra.Command 分析

kube-controller-manager 借助 cobra.Command工具,理解kube-controller-manager前提是清楚cobra.Command 运行逻辑。

2.2.1.1 main 分析

mian func 主要包含两部分:

  • NewControllerManagerCommand():new cobra.Command,其中cobra.Command.Run自定义实现启动controllers 逻辑
  • command.Execute(): 执行cobra.Command.Run
复制代码
1
2
3
4
5
6
7
8
9
10
func main() { ... command := app.NewControllerManagerCommand() ... if err := command.Execute(); err != nil { fmt.Fprintf(os.Stderr, "%vn", err) os.Exit(1) } }

2.2.1.2 NewControllerManagerCommand

NewControllerManagerCommand 主要是new cobra.Command。主干逻辑如下:

  • new options.NewKubeControllerManagerOptions
  • new cobra.Command
    • s.Config 将option 赋值给config
    • Run(c.Complete(), wait.NeverStop) 实现了Run controllers具体逻辑。将来被command.Execute()调用
  • s.Flags 解析cmd参数
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// NewControllerManagerCommand creates a *cobra.Command object with default parameters func NewControllerManagerCommand() *cobra.Command { s, err := options.NewKubeControllerManagerOptions() ... cmd := &cobra.Command{ Use: "kube-controller-manager", Long: `The Kubernetes controller manager ....`, Run: func(cmd *cobra.Command, args []string) { ... c, err := s.Config(KnownControllers(), ControllersDisabledByDefault.List()) if err != nil { fmt.Fprintf(os.Stderr, "%vn", err) os.Exit(1) } if err := Run(c.Complete(), wait.NeverStop); err != nil { fmt.Fprintf(os.Stderr, "%vn", err) os.Exit(1) } }, } ... namedFlagSets := s.Flags(KnownControllers(), ControllersDisabledByDefault.List()) ... return cmd }

2.2.1.3 command.Execute()

command.Execute() 是cobra.Command实现的,kube-controller-mananger只是调用,实现了cobra.Command逻辑。

  • ExecuteC() 总是先执行父cobra.Command,然后返回子cobra.Command
  • Execute() 并没有递归执行cobra.Command,因此kube-controller-manage必须是root cobra.Command
  • cmd.execute(flags) 是每个cobra.Command 执行逻辑
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
func (c *Command) Execute() error { _, err := c.ExecuteC() return err } func (c *Command) ExecuteC() (cmd *Command, err error) { // Regardless of what command execute is called on, run on Root only if c.HasParent() { return c.Root().ExecuteC() } ... if c.TraverseChildren { cmd, flags, err = c.Traverse(args) } else { cmd, flags, err = c.Find(args) } ... err = cmd.execute(flags) ... return cmd, err }

cmd.execute(flags):是当前cobra.Command 执行逻辑。执行逻辑有点像自动化测试执行过程,先PreRun,然后Run,最后PostRun。在执行PreRun和PostRun顺便执行Parents的PreRun和PostRun。

  • c.Run(c, argWoFlags) 就是我们NewControllerManagerCommand 定义的Run func
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
func (c *Command) execute(a []string) (err error) { ... for p := c; p != nil; p = p.Parent() { if p.PersistentPreRunE != nil { ... } else if p.PersistentPreRun != nil { ... } } if c.PreRunE != nil { ... } else if c.PreRun != nil { ... } .. if c.RunE != nil { ... } else { c.Run(c, argWoFlags) } if c.PostRunE != nil { ... } else if c.PostRun != nil { ... } for p := c; p != nil; p = p.Parent() { if p.PersistentPostRunE != nil { ... } else if p.PersistentPostRun != nil { ... } } return nil }

2.2.2 cobra.Command.Run 分析

cobra.Command.Run 主要做了两件事情

  • Start the controller manager HTTP server,启动Profiling和Prometheus Exporter
  • 单点或者多点执行定义的run fuc(具体实现启动run controllers)
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// Run runs the KubeControllerManagerOptions. This should never exit. func Run(c *config.CompletedConfig, stopCh <-chan struct{}) error { .... // Setup any healthz checks we will want to use. var checks []healthz.HealthzChecker var electionChecker *leaderelection.HealthzAdaptor if c.ComponentConfig.Generic.LeaderElection.LeaderElect { electionChecker = leaderelection.NewLeaderHealthzAdaptor(time.Second * 20) checks = append(checks, electionChecker) } // Start the controller manager HTTP server // unsecuredMux is the handler for these controller *after* authn/authz filters have been applied var unsecuredMux *mux.PathRecorderMux if c.SecureServing != nil { unsecuredMux = genericcontrollermanager.NewBaseHandler(&c.ComponentConfig.Generic.Debugging, checks...) handler := genericcontrollermanager.BuildHandlerChain(unsecuredMux, &c.Authorization, &c.Authentication) // TODO: handle stoppedCh returned by c.SecureServing.Serve if _, err := c.SecureServing.Serve(handler, 0, stopCh); err != nil { return err } } if c.InsecureServing != nil { unsecuredMux = genericcontrollermanager.NewBaseHandler(&c.ComponentConfig.Generic.Debugging, checks...) insecureSuperuserAuthn := server.AuthenticationInfo{Authenticator: &server.InsecureSuperuser{}} handler := genericcontrollermanager.BuildHandlerChain(unsecuredMux, nil, &insecureSuperuserAuthn) if err := c.InsecureServing.Serve(handler, 0, stopCh); err != nil { return err } } run := func(ctx context.Context) { ... } if !c.ComponentConfig.Generic.LeaderElection.LeaderElect { run(context.TODO()) panic("unreachable") } .... leaderelection.RunOrDie(context.TODO(), leaderelection.LeaderElectionConfig{ Lock: rl, LeaseDuration: c.ComponentConfig.Generic.LeaderElection.LeaseDuration.Duration, RenewDeadline: c.ComponentConfig.Generic.LeaderElection.RenewDeadline.Duration, RetryPeriod: c.ComponentConfig.Generic.LeaderElection.RetryPeriod.Duration, Callbacks: leaderelection.LeaderCallbacks{ OnStartedLeading: run, OnStoppedLeading: func() { klog.Fatalf("leaderelection lost") }, }, WatchDog: electionChecker, Name: "kube-controller-manager", }) panic("unreachable") }

run func:

  • 首先CreateControllerContext
  • 然后执行StartControllers
    • NewControllerInitializers() 获取白名单controllers map作为参数
  • 最后start InformerFactory和GenericInformerFactory
    • InformerFactory:用于常规 API 资源对象的控制器
    • GenericInformerFactory:用于 garbagecollector 和 resourcequota 控制器
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
run := func(ctx context.Context) { .... controllerContext, err := CreateControllerContext(c, rootClientBuilder, clientBuilder, ctx.Done()) ... if err := StartControllers(controllerContext, saTokenControllerInitFunc, NewControllerInitializers(controllerContext.LoopMode), unsecuredMux); err != nil { klog.Fatalf("error starting controllers: %v", err) } controllerContext.InformerFactory.Start(controllerContext.Stop) controllerContext.GenericInformerFactory.Start(controllerContext.Stop) close(controllerContext.InformersStarted) select {} }

2.2.3 StartControllers 分析

for循环启动Controller

  • controllers: 参数是startController map
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
// StartControllers starts a set of controllers with a specified ControllerContext func StartControllers(ctx ControllerContext, startSATokenController InitFunc, controllers map[string]InitFunc, unsecuredMux *mux.PathRecorderMux) error { ... for controllerName, initFn := range controllers { if !ctx.IsControllerEnabled(controllerName) { klog.Warningf("%q is disabled", controllerName) continue } time.Sleep(wait.Jitter(ctx.ComponentConfig.Generic.ControllerStartInterval.Duration, ControllerStartJitter)) klog.V(1).Infof("Starting %q", controllerName) debugHandler, started, err := initFn(ctx) if err != nil { klog.Errorf("Error starting %q", controllerName) return err } if !started { klog.Warningf("Skipping %q", controllerName) continue } if debugHandler != nil && unsecuredMux != nil { basePath := "/debug/controllers/" + controllerName unsecuredMux.UnlistedHandle(basePath, http.StripPrefix(basePath, debugHandler)) unsecuredMux.UnlistedHandlePrefix(basePath+"/", http.StripPrefix(basePath, debugHandler)) } klog.Infof("Started %q", controllerName) } return nil } // NewControllerInitializers is a public map of named controller groups (you can start more than one in an init func) // paired to their InitFunc. This allows for structured downstream composition and subdivision. func NewControllerInitializers(loopMode ControllerLoopMode) map[string]InitFunc { controllers := map[string]InitFunc{} controllers["endpoint"] = startEndpointController controllers["replicationcontroller"] = startReplicationController controllers["podgc"] = startPodGCController controllers["resourcequota"] = startResourceQuotaController controllers["namespace"] = startNamespaceController controllers["serviceaccount"] = startServiceAccountController controllers["garbagecollector"] = startGarbageCollectorController controllers["daemonset"] = startDaemonSetController controllers["job"] = startJobController controllers["deployment"] = startDeploymentController controllers["replicaset"] = startReplicaSetController controllers["horizontalpodautoscaling"] = startHPAController controllers["disruption"] = startDisruptionController controllers["statefulset"] = startStatefulSetController controllers["cronjob"] = startCronJobController controllers["csrsigning"] = startCSRSigningController controllers["csrapproving"] = startCSRApprovingController controllers["csrcleaner"] = startCSRCleanerController controllers["ttl"] = startTTLController controllers["bootstrapsigner"] = startBootstrapSignerController controllers["tokencleaner"] = startTokenCleanerController controllers["nodeipam"] = startNodeIpamController controllers["nodelifecycle"] = startNodeLifecycleController if loopMode == IncludeCloudLoops { controllers["service"] = startServiceController controllers["route"] = startRouteController controllers["cloud-node-lifecycle"] = startCloudNodeLifecycleController // TODO: volume controller into the IncludeCloudLoops only set. } controllers["persistentvolume-binder"] = startPersistentVolumeBinderController controllers["attachdetach"] = startAttachDetachController controllers["persistentvolume-expander"] = startVolumeExpandController controllers["clusterrole-aggregation"] = startClusterRoleAggregrationController controllers["pvc-protection"] = startPVCProtectionController controllers["pv-protection"] = startPVProtectionController controllers["ttl-after-finished"] = startTTLAfterFinishedController controllers["root-ca-cert-publisher"] = startRootCACertPublisher return controllers }

2.2.3 startJobController

所有startController func都会被StartControllers调用,并且函数实现基本一致。
我们以startJobController为例,先校验Job资源是否存在,然后NewJobController,最后执行JobController Run func.

  • NewJobController() 参数是jobcontroller 需要的informers和kubeClient
  • Run() 参数是之前Config定义的各种参数,从而体现不同Controller差异性
    后面的过程是我们熟悉的单个Controller逻辑,不做表述。至此,启动不同Controller 逻辑分析结束。
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
func startJobController(ctx ControllerContext) (http.Handler, bool, error) { if !ctx.AvailableResources[schema.GroupVersionResource{Group: "batch", Version: "v1", Resource: "jobs"}] { return nil, false, nil } go job.NewJobController( ctx.InformerFactory.Core().V1().Pods(), ctx.InformerFactory.Batch().V1().Jobs(), ctx.ClientBuilder.ClientOrDie("job-controller"), ).Run(int(ctx.ComponentConfig.JobController.ConcurrentJobSyncs), ctx.Stop) return nil, true, nil }

2.3 实现不同controllers差异

每个Controller实现的功能不同,必然要在实现过程中考虑到不同Controller的差异和共同点。

  • 共同点:
    • 本质上所有的控制器都是Controller,机制基本上是一致的,不同的是具体实现的策略。因此在定义Controller的Struct时,我们很多情况都能看到共通的地方。
    • Controller都需要的参数我们都可以抽象出来,比如ClientSet(或者kubeconfig)
  • 差异点:
    • 由于每个Controller实现的策略不同,需要的参数也不尽相同

2.3.1 参数差异

在实现不同Controller赋值不同参数时,kube-controller-manager 实现方式还是很讲究的。在分析代码时,起初对options和config 两部分的定位非常疑惑,因为两者有太多重复的地方,kube-controller-manage 使用了相当多的代码对option赋值给config,合并为一个struct是否更加合理?

分析之后,认为kube-controller-manage 这么做采用的是机制和策略分离原则。options参数主要是面向cmd.Flag的,而config参数只要是被controller调用。并且option和config参数并非是完全对应,不同的场景下,相同的option可能会有不同的config值。

  • option 赋值参考AddFlags逻辑
  • config 赋值参考ApplyTo逻辑

2.3.2 参数校验

kube-controller-manage主要是对options参数做校验,不同controller参数校验的规则可以不同。

  • 参考Validate逻辑

2.3.3 controller 返回结果

在controller-manage 中介绍了profiling和Prometheus Exporter被调用(http形式)。不同的controller暴露的http 路径和处理结果是不同的,如何实现这些差异。

  • 每个startController 返回结果都是固定的(http.Handler, bool, error),分别对应(controller-manager定义的hander,started,err)
  • StartControllers 根据每个startController返回结果进行处理
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// StartControllers starts a set of controllers with a specified ControllerContext func StartControllers(ctx ControllerContext, startSATokenController InitFunc, controllers map[string]InitFunc, unsecuredMux *mux.PathRecorderMux) error { ... for controllerName, initFn := range controllers { ... debugHandler, started, err := initFn(ctx) if err != nil { klog.Errorf("Error starting %q", controllerName) return err } if !started { klog.Warningf("Skipping %q", controllerName) continue } if debugHandler != nil && unsecuredMux != nil { basePath := "/debug/controllers/" + controllerName unsecuredMux.UnlistedHandle(basePath, http.StripPrefix(basePath, debugHandler)) unsecuredMux.UnlistedHandlePrefix(basePath+"/", http.StripPrefix(basePath, debugHandler)) } klog.Infof("Started %q", controllerName) } return nil } func startJobController(ctx ControllerContext) (http.Handler, bool, error) { ... }

3. 架构分析

3.1 独立kube-contrller

  • controller-manage 定位在help功能,通过打桩的方式在其它包中引用,并非仅仅被kube-controller-manager 引用
    • NewBaseHandler() 被kube-Controller-manage和cloud-controller-manage引用
    • controller-manage 目前发现的代码都是被kube-Controller-manage和cloud-controller-manage引用。取名controller-manage可能也是因为被各种controller-manage共享

3.2 config 和 option独立

  • config 内容比option内容稍微丰富一点,config 主要负责controller启动,option 主要是负责获取cmd参数
  • option 参数赋值给config并非是一一对应,赋值过程中还包含一定的逻辑。机制和策略分开,config 和option负责不同模块,赋值过程中包含一定的逻辑。参考2.3.1描述。

3.3 将informer作为参数传递给 NewControllerStruct

  • 每个controller都是一个独立组件,controller之间通过apiserver(http)交互。controller不需要通过内存共享数据。
  • 不同controller关注不同的informer事件,同时添加各自的handfunc回调函数
  • InformerFactory.Start()可以start 所有k8s自定义的informer,不需要在各自controller 内部start

3.4 使用Cobra

对kube-controller-manage引入Cobra.Command的目的我依旧是带有疑问的。目前并没有看到Cobra.Command本身强大的功能而简化代码,反而提高了理解代码复杂性。

  • Cobra.Command支持父子先后执行Cobra.Command,但是kube-controller-manage 调用的是Cobra.Command.Execute(),该函数理论上要求kube-controller-manage 必须是 root Cobra.Command
  • Cobra.Command 支持PreFun,PostFunc。Kube-controller-manage 在创建Cobra.Command均没有定义
  • Cobra.Command 支持参数解析,但是目前看来options中获取cmd参数依然使用是传统的"http://github.com/spf13/pflag" pflag.Parse()方法

kube-controller-manage(release-1.15)处于一个过渡阶段,未来版本会更好的利用Cobra特性。

最后

以上就是调皮纸飞机最近收集整理的关于一个controller怎么调用另个controller 带参数_kubernetes kube-controller-manager 源码分析...的全部内容,更多相关一个controller怎么调用另个controller内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(82)

评论列表共有 0 条评论

立即
投稿
返回
顶部