
对于业务应用,需要对其进行先k8s内置资源进行一系列运维操作,因此编写业务的operator必不可少,在此了解到kubebuilder 是社区认可度很高的一种官方、标准化 Operator 框架,可以利用其非常方便的编写业务operator,以此来扩展 Kubernetes API
Kubebuilder 是一个使用 CRDs 构建 K8s API 的 SDK,主要是:
方便用户从零开始开发 CRDs,Controllers 和 Admission Webhooks 来扩展 K8s。
自定义资源 CRD(Custom Resource Definition)可以扩展 Kubernetes API,掌握 CRD 是成为 Kubernetes 高级玩家的必备技能,本文将介绍 CRD 和 Controller 的概念,并对 CRD 编写框架 Kubebuilder 进行深入分析,让您真正理解并能快速开发 CRD。

按照处理类型的不同,一般可以将其分为两类:一类可能会修改传入对象,称为 mutating webhook;一类则会只读传入对象,称为 validating webhook。

GVK = GroupVersionKind,GVR = GroupVersionResource。
每一个 GVK 都关联着一个 package 中给定的 root Go type,比如 apps/v1/Deployment 就关联着 K8s 源码里面 k8s.io/api/apps/v1 package 中的 Deployment struct,我们提交的各类资源定义 YAML 文件都需要写:
apiVersion:这个就是 GV 。 kind:这个就是 K。
根据 GVK K8s 就能找到你到底要创建什么类型的资源,根据你定义的 Spec 创建好资源之后就成为了 Resource,也就是 GVR。GVK/GVR 就是 K8s 资源的坐标,是我们创建/删除/修改/读取资源的基础。
每一组 Controllers 都需要一个 Scheme,提供了 Kinds 与对应 Go types 的映射,也就是说给定 Go type 就知道他的 GVK,给定 GVK 就知道他的 Go type,比如说我们给定一个 Scheme: "tutotial.kubebuilder.io/api/v1".CronJob{} 这个 Go type 映射到 batch.tutotial.kubebuilder.io/v1 的 CronJob GVK,那么从 Api Server 获取到下面的 JSON:
{
"kind": "CronJob",
"apiVersion": "batch.tutorial.kubebuilder.io/v1",
...
}就能构造出对应的 Go type了,通过这个 Go type 也能正确地获取 GVR 的一些信息,控制器可以通过该 Go type 获取到期望状态以及其他辅助信息进行调谐逻辑。
Kubebuilder 的核心组件,具有 3 个职责:
Kubebuilder 的核心组件,负责在 Controller 进程里面根据 Scheme 同步 Api Server 中所有该 Controller 关心 GVKs 的 GVRs,其核心是 GVK -> Informer 的映射,Informer 会负责监听对应 GVK 的 GVRs 的创建/删除/更新操作,以触发 Controller 的 Reconcile 逻辑。
Kubebuidler 为我们生成的脚手架文件,我们只需要实现 Reconcile 方法即可。
在实现 Controller 的时候不可避免地需要对某些资源类型进行创建/删除/更新,就是通过该 Clients 实现的,其中查询功能实际查询是本地的 Cache,写操作直接访问 Api Server。
由于 Controller 经常要对 Cache 进行查询,Kubebuilder 提供 Index utility 给 Cache 加索引提升查询效率。
在一般情况下,如果资源被删除之后,我们虽然能够被触发删除事件,但是这个时候从 Cache 里面无法读取任何被删除对象的信息,这样一来,导致很多垃圾清理工作因为信息不足无法进行,K8s 的 Finalizer 字段用于处理这种情况。在 K8s 中,只要对象 ObjectMeta 里面的 Finalizers 不为空,对该对象的 delete 操作就会转变为 update 操作,具体说就是 update deletionTimestamp 字段,其意义就是告诉 K8s 的 GC“在deletionTimestamp 这个时刻之后,只要 Finalizers 为空,就立马删除掉该对象”。
所以一般的使用姿势就是在创建对象时把 Finalizers 设置好(任意 string),然后处理 DeletionTimestamp 不为空的 update 操作(实际是 delete),根据 Finalizers 的值执行完所有的 pre-delete hook(此时可以在 Cache 里面读取到被删除对象的任何信息)之后将 Finalizers 置为空即可。
K8s GC 在删除一个对象时,任何 ownerReference 是该对象的对象都会被清除,与此同时,Kubebuidler 支持所有对象的变更都会触发 Owner 对象 controller 的 Reconcile 方法。
能够访问 Kubernetes v1.11.3+ 集群
$ kubebuilder init --domain imoc-operator
运行下面的命令,创建一个新的 API(组/版本)为 “webapp/v1”,并在上面创建新的 Kind(CRD) “Guestbook”。
kubebuilder create api --group webapp --version v1 --kind Guestbook
$GOPATH/src/helloworld/controllers/guestbook_controller.go , 内容如下: 
make install
make run
apiVersion: webapp.com.bolingcavalry/v1
kind: Guestbook
metadata:
name: guestbook-sample
spec:
# Add fields here
foo: bar$ kubectl apply -f config/samples/
$ kubectl get Guestbook

make docker-build docker-push IMG=bolingcavalry/guestbook:002[root@kubebuilder helloworld]# make docker-build docker-push IMG=bolingcavalry/guestbook:002
$ make docker-build docker-push IMG=127.0.0.1:5000/guesstbook:v1
/Users/xuel/workspace/goworkspace/src/github.com/kaliarch/imoc-operator/bin/controller-gen object:headerFile="hack/boilerplate.go.txt" paths="./..."
go fmt ./...
go vet ./...
/Users/xuel/workspace/goworkspace/src/github.com/kaliarch/imoc-operator/bin/controller-gen "crd:trivialVersions=true,preserveUnknownFields=false" rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases
mkdir -p /Users/xuel/workspace/goworkspace/src/github.com/kaliarch/imoc-operator/testbin
test -f /Users/xuel/workspace/goworkspace/src/github.com/kaliarch/imoc-operator/testbin/setup-envtest.sh || curl -sSLo /Users/xuel/workspace/goworkspace/src/github.com/kaliarch/imoc-operator/testbin/setup-envtest.sh https://raw.githubusercontent.com/kubernetes-sigs/controller-runtime/v0.7.0/hack/setup-envtest.sh
source /Users/xuel/workspace/goworkspace/src/github.com/kaliarch/imoc-operator/testbin/setup-envtest.sh; fetch_envtest_tools /Users/xuel/workspace/goworkspace/src/github.com/kaliarch/imoc-operator/testbin; setup_envtest_env /Users/xuel/workspace/goworkspace/src/github.com/kaliarch/imoc-operator/testbin; go test ./... -coverprofile cover.out
fetching envtest tools@1.19.2 (into '/Users/xuel/workspace/goworkspace/src/github.com/kaliarch/imoc-operator/testbin')
x bin/
x bin/etcd
x bin/kubectl
x bin/kube-apiserver
setting up env vars
? github.com/kaliarch/imoc-operator [no test files]
? github.com/kaliarch/imoc-operator/api/v1 [no test files]
ok github.com/kaliarch/imoc-operator/controllers 12.959s coverage: 0.0% of statements
docker build -t 127.0.0.1:5000/guesstbook:v1 .
Sending build context to Docker daemon 335.5MB
Step 1/14 : FROM golang:1.15 as builder
1.15: Pulling from library/golang
b9a857cbf04d: Pull complete
d557ee20540b: Pull complete
3b9ca4f00c2e: Pull complete
667fd949ed93: Pull complete
547cc43be03d: Pull complete
0977886e8147: Pull complete
cceccf7c7738: Pull complete
Digest: sha256:de97bab9325c4c3904f8f7fec8eb469169a1d247bdc97dcab38c2c75cf4b4c5d
Status: Downloaded newer image for golang:1.15
---> 5f46b413e8f5
Step 2/14 : WORKDIR /workspace
---> Running in 597efa584096
Removing intermediate container 597efa584096
---> a21979056316
Step 3/14 : COPY go.mod go.mod
---> b6c4b03d5126
Step 4/14 : COPY go.sum go.sum
---> f1af7c95cdc8
Step 5/14 : RUN go mod download
---> Running in baf57375b805
Removing intermediate container baf57375b805
---> 62e488ee06f5
Step 6/14 : COPY main.go main.go
---> 72c3d023e770
Step 7/14 : COPY api/ api/
---> b164eb864a85
Step 8/14 : COPY controllers/ controllers/
---> 843af6a782ec
Step 9/14 : RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -a -o manager main.go
---> Running in af2881daee7c
Removing intermediate container af2881daee7c
---> cf6ef1542da6
Step 10/14 : FROM gcr.io/distroless/static:nonroot
nonroot: Pulling from distroless/static
9e4425256ce4: Pull complete
Digest: sha256:b89b98ea1f5bc6e0b48c8be6803a155b2a3532ac6f1e9508a8bcbf99885a9152
Status: Downloaded newer image for gcr.io/distroless/static:nonroot
---> 88055b6758df
Step 11/14 : WORKDIR /
---> Running in 35900ca6d19f
Removing intermediate container 35900ca6d19f
---> 902a3991fa3b
Step 12/14 : COPY --from=builder /workspace/manager .
---> 5af066bf1214
Step 13/14 : USER 65532:65532
---> Running in b44fbfb3c52b
Removing intermediate container b44fbfb3c52b
---> 6ca11554d8fa
Step 14/14 : ENTRYPOINT ["/manager"]
---> Running in 716538bf799a
Removing intermediate container 716538bf799a
---> a98e090c1e68
Successfully built a98e090c1e68
Successfully tagged 127.0.0.1:5000/guesstbook:v1
docker push 127.0.0.1:5000/guesstbook:v1
The push refers to repository [127.0.0.1:5000/guesstbook]
babc932481e7: Pushed
8651333b21e7: Pushed
v1: digest: sha256:5dbb4e549c0dff1a4edba19ea5a35f9e21deeabe2fcefbc6b6358fb849dd61e2 size: 739
$ curl -XGET http://127.0.0.1:5000/v2/_catalog
{"repositories":["guesstbook"]}$ make deploy IMG=127.0.0.1:5000/guesstbook:v1
/Users/xuel/workspace/goworkspace/src/github.com/kaliarch/imoc-operator/bin/controller-gen "crd:trivialVersions=true,preserveUnknownFields=false" rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases
cd config/manager && /Users/xuel/workspace/goworkspace/src/github.com/kaliarch/imoc-operator/bin/kustomize edit set image controller=127.0.0.1:5000/guesstbook:v1
/Users/xuel/workspace/goworkspace/src/github.com/kaliarch/imoc-operator/bin/kustomize build config/default | kubectl apply -f -
namespace/imoc-operator-system created
customresourcedefinition.apiextensions.k8s.io/guestbooks.webapp.com.bolingcavalry configured
role.rbac.authorization.k8s.io/imoc-operator-leader-election-role created
clusterrole.rbac.authorization.k8s.io/imoc-operator-manager-role created
clusterrole.rbac.authorization.k8s.io/imoc-operator-metrics-reader created
clusterrole.rbac.authorization.k8s.io/imoc-operator-proxy-role created
rolebinding.rbac.authorization.k8s.io/imoc-operator-leader-election-rolebinding created
clusterrolebinding.rbac.authorization.k8s.io/imoc-operator-manager-rolebinding created
clusterrolebinding.rbac.authorization.k8s.io/imoc-operator-proxy-rolebinding created
configmap/imoc-operator-manager-config created
service/imoc-operator-controller-manager-metrics-service created
deployment.apps/imoc-operator-controller-manager created
$ sudo docker login --username=1123845260@qq.com registry.cn-shanghai.aliyuncs.com
$ sudo docker pull registry.cn-shanghai.aliyuncs.com/kaliarch/slate:[镜像版本号]
$ sudo docker login --username=1123845260@qq.com registry.cn-shanghai.aliyuncs.com
$ sudo docker tag [ImageId] registry.cn-shanghai.aliyuncs.com/kaliarch/slate:[镜像版本号]
$ sudo docker push registry.cn-shanghai.aliyuncs.com/kaliarch/slate:[镜像版本号]
1. 登陆阿里云镜像仓库
密码:阿里控制台账号
2. 修改tag
docker tag 127.0.0.1:5000/guesstbook:v1 registry.cn-shanghai.aliyuncs.com/kaliarch/guesstbook:v1
3. 推送镜像
docker push registry.cn-shanghai.aliyuncs.com/kaliarch/guesstbook:v1
重新部署
make deploy IMG=registry.cn-shanghai.aliyuncs.com/kaliarch/guesstbook:v1

kubectl logs -f imoc-operator-controller-manager-648b4877c6-4bpp9 -n imoc-operator-system -c managermake uninstallkind delete cluster通过了解,我们可以看到 Kubebuilder 提供的功能对于快速编写 CRD 和 Controller 是十分有帮助的,无论是云原生中知名的开源项目例如 Istio、Knative 等知名项目还是各种自定义 Operators,都大量使用了 CRD,将各种组件抽象为 CRD,据此可以将为自己业务编写对应的CRD,使得业务更加生于云长于云。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。