FAQ
初始化项目时通过 domain 标志传入的值(例如 kubebuilder init --domain example.com)有何作用?
创建项目后,通常需要扩展 Kubernetes API 并定义项目自有的新 API。因此 Kubebuilder 会在定义项目配置的 PROJECT 文件中记录 domain,并用它作为域名创建 API 端点。请务必理解组、版本和种类,真是让人头大!。
该域名用于组后缀,以明确显示资源组类别。例如,如果设置为 --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。
如果域字段未设置,默认值为
my.domain。
我希望自定义我的项目,使用 klog 替代 controller-runtime 提供的 zap。我该如何使用 klog 或其他日志记录器作为项目的日志记录器?
在 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 后,我看到类似“无法找到领导者选举命名空间:未在集群内运行…“的错误。
可以启用领导者选举。但若用 make run 在集群外本地测试,可能还需要设置 manager 创建领导者选举资源所用的命名空间,如下:
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)时,可能由该问题引起。原因是 Kubernetes 将挂载的 token 文件权限设为 0600,参见此处的解决方案。变通方法是:
在 manager.yaml 中添加 fsGroup。
安全上下文:
以非root用户身份运行: true
fsGroup: 65532 # 添加此 fsGroup 以使令牌文件可读
不过,请注意,此问题已被修复,如果您在高版本(可能大于或等于1.22)中部署项目,则不会发生该问题。
当我运行 make install 来应用 CRD 清单时,出现错误 过长:必须最多为 262144 字节。如何解决这个问题?为什么会出现这个错误?
在尝试运行 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 注解该对象,该注解包含整个之前的配置。如果这个配置太大,就会超过允许的字节大小。(更多信息)
可通过以下几种方式规避该场景,例如:
通过从 CRD 中移除描述:
Controller-gen 用于生成 CRD。使用 maxDescLen=0 去掉描述可减小体积,从而可能解决问题。可按如下示例更新 Makefile,再执行 make manifest 重新生成无描述的 CRD,参见:
.PHONY: manifests
manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects.
# Note that the option maxDescLen=0 was added in the default scaffold in order to sort out the issue
# Too long: must have at most 262144 bytes. By using kubectl apply to create / update resources an annotation
# that the K8s API creates to store the latest version of the resource ( kubectl.kubernetes.io/last-applied-configuration).
# However, it has a size limit and if the CRD is too big with so many long descriptions as this one it will cause the failure.
$(CONTROLLER_GEN) rbac:roleName=manager-role crd:maxDescLen=0 webhook paths="./..." output:crd:artifacts:config=config/crd/bases
通过重新设计您的 API:
您可以审查您的 API 设计,看看它是否有超过应有的规格,从而违反单一职责原则。例如,这样您可能需要重新设计它们。
使用 Server-Side Apply 安装 CRD:
该问题出现在用客户端 apply 安装 CRD 时,也可改用 Server-Side Apply 安装或更新 CRD,例如 kubectl apply --server-side -f ...。这样不会把完整清单存入 kubectl.kubernetes.io/last-applied-configuration 注解,从而可避免大小限制失败。
若更新已有 CRD,根据工作流,kubectl replace -f ... 也可能有帮助。
如何有效地校验与解析 CRD 中的字段?
为了提高用户体验,建议在编写 CRD 时使用 OpenAPI v3 schema 验证。然而,这种方法有时可能需要额外的解析步骤。例如,请考虑以下代码:
type StructName struct {
// +kubebuilder:validation:Format=日期-时间
TimeField string `json:"timeField,omitempty"`
}
在这种情况下会发生什么?
- 如果用户尝试创建具有无效 timeField 值的 CRD,Kubernetes API 会向其发送错误通知。
- 在开发者端,字符串值需要在使用之前手动解析。
有没有更好的方法?
为了提供更好的用户体验和简化开发人员体验,建议使用预定义类型,如 metav1.Time。例如,考虑以下代码:
type StructName struct {
TimeField metav1.Time `json:"timeField,omitempty"`
}
在这种情况下会发生什么?
- 用户仍然会收到来自 Kubernetes API 的错误通知,提示无效的
timeField值。 - 开发人员可以直接在代码中使用解析后的 TimeField,而无需额外的解析,从而减少错误并提高效率。
用 Podman 或 Buildah 构建镜像时失败,提示 cmd/main.go: no such file or directory。如何解决?
尽管我们尽量让脚手架兼容其他容器工具,但 Kubebuilder 官方测试与支持的是 Docker。
脚手架生成的 .dockerignore 用 !**/*.go 重新包含所有 Go 源文件。Docker 会遵守该模式;Podman 与 Buildah 不会:它们会跳过被忽略的目录,导致 Go 源码进不了构建上下文。上游跟踪见 buildah#6417,报告见 kubebuilder#5181。
若使用 Podman,请在 .dockerignore 中按目录名重新包含源码目录:
!cmd
!api
!internal
再把项目中其他存放 Go 源文件的目录一并加上。
用 Podman 或 Buildah 构建镜像时出现 short-name "golang:1.26" did not resolve to an alias and no unqualified-search registries are defined。如何解决?
尽管我们尽量让脚手架兼容其他容器工具,但 Kubebuilder 官方测试与支持的是 Docker。
Docker 会把短镜像名解析到 Docker Hub。Podman 与 Buildah 则在主机上解析:使用 registries.conf.d 中的短名别名,或 registries.conf 中的 unqualified-search-registries。主机两者皆无时就会报错。例如 Debian 12 默认都不提供。
使用下列任一方案。
覆盖基础镜像。 脚手架 Dockerfile 将其作为构建参数暴露:
ARG BASE_IMAGE=golang:1.26
FROM ${BASE_IMAGE} AS builder
# 来自 Docker Hub
make docker-build IMG=<some-registry>/<project>:tag BASE_IMAGE=docker.io/library/golang:1.26
# 来自自有仓库或镜像站
make docker-build IMG=<some-registry>/<project>:tag BASE_IMAGE=<myregistry>/golang:1.26
make docker-buildx 同样接受 BASE_IMAGE。
或在主机上为 golang 短名添加别名:
# /etc/containers/registries.conf.d/golang.conf
[aliases]
"golang" = "docker.io/library/golang"
匹配的别名无需查阅 unqualified-search-registries,因此该列表为空时也可用。别名还忽略 tag,一条即可覆盖所有 golang 标签。相比把 docker.io 加进搜索列表(会改变主机上所有非限定镜像名的解析方式),更推荐用别名。无根 Podman 请使用 $HOME/.config/containers/registries.conf.d/golang.conf。