常见问题(FAQ)
在初始化项目时通过 domain 参数传入的值(例如 kubebuilder init --domain example.com)有什么作用?
创建项目后,通常你会希望扩展 Kubernetes API,并定义由你的项目拥有的新 API。因此,该 domain 值会被记录在定义项目配置的 PROJECT 文件中,并作为域名用于创建 API 端点。请确保你理解Groups、Versions 与 Kinds,哇哦! 中的概念。
domain 用于作为 group 的后缀,用来直观地表示资源组的类别。例如,如果设置了 --domain=example.com:
kubebuilder init --domain example.com --repo xxx --plugins=go/v4
kubebuilder create api --group mygroup --version v1beta1 --kind Mykind
那么最终的资源组将是 mygroup.example.com。
如果没有设置 domain 字段,默认值为
my.domain。
我想自定义项目使用 klog,而不是 controller-runtime 提供的 zap。如何将 klog 或其他 logger 用作项目的日志器?
在 main.go 中你可以把:
opts := zap.Options{
Development: true,
}
opts.BindFlags(flag.CommandLine)
flag.Parse()
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
替换为:
flag.Parse()
ctrl.SetLogger(klog.NewKlogr())
执行 make run 后,我看到类似 “unable to find leader election namespace: not running in-cluster…” 的错误
你可以启用 leader election。不过,如果你在本地使用 make run 目标测试项目(该命令会让 manager 在集群外运行),那么你可能还需要设置创建 leader election 资源的命名空间,如下所示:
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
Scheme: scheme,
MetricsBindAddress: metricsAddr,
Port: 9443,
HealthProbeBindAddress: probeAddr,
LeaderElection: enableLeaderElection,
LeaderElectionID: "14be1926.testproject.org",
LeaderElectionNamespace: "<project-name>-system",
如果你在集群中通过 make deploy 目标运行项目,则可能不希望添加此选项。因此,你可以使用环境变量自定义该行为,仅在开发时添加此选项,例如:
leaderElectionNS := ""
if os.Getenv("ENABLE_LEADER_ELECTION_NAMESPACE") != "false" {
leaderElectionNS = "<project-name>-system"
}
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
Scheme: scheme,
MetricsBindAddress: metricsAddr,
Port: 9443,
HealthProbeBindAddress: probeAddr,
LeaderElection: enableLeaderElection,
LeaderElectionNamespace: leaderElectionNS,
LeaderElectionID: "14be1926.testproject.org",
...
在旧版本 Kubernetes 上部署项目时遇到错误 “open /var/run/secrets/kubernetes.io/serviceaccount/token: permission denied”,如何解决?
如果你遇到如下错误:
1.6656687258729894e+09 ERROR controller-runtime.client.config unable to get kubeconfig {"error": "open /var/run/secrets/kubernetes.io/serviceaccount/token: permission denied"}
sigs.k8s.io/controller-runtime/pkg/client/config.GetConfigOrDie
/go/pkg/mod/sigs.k8s.io/controller-runtime@v0.13.0/pkg/client/config/config.go:153
main.main
/workspace/main.go:68
runtime.main
/usr/local/go/src/runtime/proc.go:250
当你在 Kubernetes 较旧版本(可能 <= 1.21)上运行项目时,这可能由该问题导致,原因是挂载的 token 文件权限为 0600,解决方案见此 PR。临时解决办法是:
在 manager.yaml 中添加 fsGroup:
securityContext:
runAsNonRoot: true
fsGroup: 65532 # 添加该 fsGroup 以使 token 文件可读
不过请注意,该问题已被修复;若你将项目部署在更高版本(可能 >= 1.22),则不会出现此问题。
运行 make install 应用 CRD 清单时出现 Too long: must have at most 262144 bytes 错误。如何解决?为什么会出现该错误?
尝试运行 make install 应用 CRD 清单时,可能会遇到 Too long: must have at most 262144 bytes 错误。该错误源于 Kubernetes API 实施的大小限制。注意:make install 目标会使用 kubectl apply -f - 应用 config/crd 下的 CRD 清单。因此,当使用 apply 命令时,API 会为对象添加包含完整先前配置的 last-applied-configuration 注解。如果该配置过大,就会超出允许的字节大小。(更多信息)
理想情况下,使用 client-side apply 看似完美,因为不需要把完整对象配置作为注解(last-applied-configuration)存储在服务端。然而,需要注意的是,目前 controller-gen 与 kubebuilder 尚不支持该特性。更多内容参见:Controller-tool 讨论。
因此,你可以使用以下方式之一来规避该问题:
移除 CRD 中的描述(description):
你的 CRD 是由 controller-gen 生成的。通过使用 maxDescLen=0 选项来移除描述,可以减小大小,从而可能解决该问题。为此,你可以按以下示例修改 Makefile,然后调用 make manifest 目标以重新生成不包含描述的 CRD,如下所示:
.PHONY: manifests
manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects.
# 注意:在默认脚手架中加入了 maxDescLen=0 选项以解决 “Too long: must have at most 262144 bytes” 问题。
# 使用 kubectl apply 创建/更新资源时,K8s API 会创建注解以存储资源的最新版本(kubectl.kubernetes.io/last-applied-configuration)。
# 该注解有大小限制,如果 CRD 过大且描述很多,就会导致失败。
$(CONTROLLER_GEN) rbac:roleName=manager-role crd:maxDescLen=0 webhook paths="./..." output:crd:artifacts:config=config/crd/bases
重新设计你的 API:
你可以审视 API 的设计,看看是否违反了例如单一职责原则而导致规格过多,从而考虑对其进行重构。
如何高效地校验和解析 CRD 中的字段?
为提升用户体验,编写 CRD 时建议使用 OpenAPI v3 schema 进行校验。不过,这种方式有时需要额外的解析步骤。 例如,考虑如下代码:
type StructName struct {
// +kubebuilder:validation:Format=date-time
TimeField string `json:"timeField,omitempty"`
}
这种情况下会发生什么?
- 如果用户尝试以非法的 timeField 值创建 CRD,Kubernetes API 会返回错误提示。
- 对于开发者,字符串值在使用前需要手动解析。
有更好的方式吗?
为了同时提供更好的用户体验与更顺畅的开发体验,建议使用诸如 metav1.Time 这样的预定义类型。例如:
type StructName struct {
TimeField metav1.Time `json:"timeField,omitempty"`
}
这种情况下会发生什么?
- 对非法的
timeField值,用户仍会从 Kubernetes API 获得错误提示。 - 开发者可以直接使用已解析的 TimeField,而无需额外解析,从而降低错误并提升效率。
