每段旅程都需要一个起点,每个程序都需要一个主函数

emptymain.go
Apache License

版权所有 2022 年 Kubernetes 作者。

根据 Apache 许可,版本 2.0(“许可”)获得许可; 除非符合许可的规定,否则您不得使用此文件。 您可以在以下网址获取许可的副本:

http://www.apache.org/licenses/LICENSE-2.0

除非适用法律要求或书面同意,否则根据许可分发的软件将按“原样“分发, 不附带任何明示或暗示的担保或条件。 请参阅许可,了解特定语言下的权限和限制。

我们的包从一些基本的导入开始。特别是:

  • 核心的 controller-runtime
  • 默认的 controller-runtime 日志记录,Zap(稍后会详细介绍)
package main

import (
	"flag"
	"os"

	// 导入所有 Kubernetes 客户端认证插件(例如 Azure、GCP、OIDC 等)
	// 以确保 exec-entrypoint 和 run 可以利用它们。
	_ "k8s.io/client-go/plugin/pkg/client/auth"

	"k8s.io/apimachinery/pkg/runtime"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	clientgoscheme "k8s.io/client-go/kubernetes/scheme"
	_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
	ctrl "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/cache"
	"sigs.k8s.io/controller-runtime/pkg/healthz"
	"sigs.k8s.io/controller-runtime/pkg/log/zap"
	"sigs.k8s.io/controller-runtime/pkg/metrics/server"
	"sigs.k8s.io/controller-runtime/pkg/webhook"
	// +kubebuilder:scaffold:imports
)

每组控制器都需要一个 Scheme, 它提供了 Kinds 与它们对应的 Go 类型之间的映射。稍后在编写 API 定义时,我们将更详细地讨论 Kinds,所以稍后再谈。

var (
	scheme   = runtime.NewScheme()
	setupLog = ctrl.Log.WithName("setup")
)

func init() {
	utilruntime.Must(clientgoscheme.AddToScheme(scheme))

	//+kubebuilder:scaffold:scheme
}

此时,我们的主函数相当简单:

  • 我们为指标设置了一些基本的标志。

  • 我们实例化了一个 manager, 它负责运行我们所有的控制器,并设置了共享缓存和客户端到 API 服务器的连接(请注意我们告诉 manager 关于我们的 Scheme)。

  • 我们运行我们的 manager,它反过来运行我们所有的控制器和 Webhooks。 manager 被设置为在接收到优雅关闭信号之前一直运行。 这样,当我们在 Kubernetes 上运行时,我们会在 Pod 优雅终止时表现良好。

虽然目前我们没有任何东西要运行,但记住+kubebuilder:scaffold:builder注释的位置——很快那里会变得有趣起来。

func main() {
	var metricsAddr string
	var enableLeaderElection bool
	var probeAddr string
	flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.")
	flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
	flag.BoolVar(&enableLeaderElection, "leader-elect", false,
		"Enable leader election for controller manager. "+
			"Enabling this will ensure there is only one active controller manager.")
	opts := zap.Options{
		Development: true,
	}
	opts.BindFlags(flag.CommandLine)
	flag.Parse()

	ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))

	mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
		Scheme: scheme,
		Metrics: server.Options{
			BindAddress: metricsAddr,
		},
		WebhookServer:          webhook.NewServer(webhook.Options{Port: 9443}),
		HealthProbeBindAddress: probeAddr,
		LeaderElection:         enableLeaderElection,
		LeaderElectionID:       "80807133.tutorial.kubebuilder.io",
	})
	if err != nil {
		setupLog.Error(err, "unable to start manager")
		os.Exit(1)
	}

注意,Manager 可以通过以下方式限制所有控制器将监视资源的命名空间:

	mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
		Scheme: scheme,
		Cache: cache.Options{
			DefaultNamespaces: map[string]cache.Config{
				namespace: {},
			},
		},
		Metrics: server.Options{
			BindAddress: metricsAddr,
		},
		WebhookServer:          webhook.NewServer(webhook.Options{Port: 9443}),
		HealthProbeBindAddress: probeAddr,
		LeaderElection:         enableLeaderElection,
		LeaderElectionID:       "80807133.tutorial.kubebuilder.io",
	})

上面的示例将把项目的范围更改为单个Namespace。在这种情况下,建议将提供的授权限制为此命名空间, 方法是将默认的ClusterRoleClusterRoleBinding替换为RoleRoleBinding。 有关更多信息,请参阅 Kubernetes 关于使用 RBAC 授权 的文档。

此外,还可以使用 DefaultNamespacescache.Options{} 缓存特定一组命名空间中的对象:

	var namespaces []string // 名称空间列表
	defaultNamespaces := make(map[string]cache.Config)

	for _, ns := range namespaces {
		defaultNamespaces[ns] = cache.Config{}
	}

	mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
		Scheme: scheme,
		Cache: cache.Options{
			DefaultNamespaces: defaultNamespaces,
		},
		Metrics: server.Options{
			BindAddress: metricsAddr,
		},
		WebhookServer:          webhook.NewServer(webhook.Options{Port: 9443}),
		HealthProbeBindAddress: probeAddr,
		LeaderElection:         enableLeaderElection,
		LeaderElectionID:       "80807133.tutorial.kubebuilder.io",
	})

有关更多信息,请参阅 cache.Options{}

	// +kubebuilder:scaffold:builder

	if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
		setupLog.Error(err, "unable to set up health check")
		os.Exit(1)
	}
	if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
		setupLog.Error(err, "unable to set up ready check")
		os.Exit(1)
	}

	setupLog.Info("starting manager")
	if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
		setupLog.Error(err, "problem running manager")
		os.Exit(1)
	}
}

有了这个,我们可以开始构建我们的 API 了!