导读: 作为云原生的从业者,多多少少也会听说CRD(Custom Resource Definition)和operator,但是说实话开发起来挺繁琐的,而且对初学者也不友好。还好社区目前有好使的脚手架:kubebuilder 和operator-sdk ,目前个人感觉大家会往前者站队,毕竟k8s自家的,索性就学着用它来尝试开发operator了。考虑到篇幅和观感问题,这里就只介绍最基本的使用,像finalizer 和webhook 建议直接查官方文档,code逻辑另外写一篇心得。
1.安装 安装参考官方指南 , 由于github下载太慢我就用码云fork了源文件来安装的:
1 2 3 4 git clone https://gitee.com/henrywangx/kubebuilder.git cd kubebuildermake build cp bin/kubebuilder $GOPATH /bin
2.使用 kubebuilder依赖go module所以要打开go module环境变量:export GO111MODULE=on
, 另外proxy或者墙的原因,先设一下go mod的proxy:export GOPROXY=https://goproxy.io
, 然后就可以开始使用了。总结就是要:
1 2 export GO111MODULE=onexport GOPROXY=https://goproxy.io
2.1创建project 1 2 3 mkdir $GOPATH /src/demo cd $GOPATH /src/demokubebuilder init --domain demo.com --license apache2 --owner "xiong"
然后呢check一下当前文件夹:
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 tree . . ├── Dockerfile ├── Makefile ├── PROJECT ├── bin │ └── manager ├── config │ ├── certmanager │ │ ├── certificate.yaml │ │ ├── kustomization.yaml │ │ └── kustomizeconfig.yaml │ ├── default │ │ ├── kustomization.yaml │ │ ├── manager_auth_proxy_patch.yaml │ │ ├── manager_webhook_patch.yaml │ │ └── webhookcainjection_patch.yaml │ ├── manager │ │ ├── kustomization.yaml │ │ └── manager.yaml │ ├── prometheus │ │ ├── kustomization.yaml │ │ └── monitor.yaml │ ├── rbac │ │ ├── auth_proxy_role.yaml │ │ ├── auth_proxy_role_binding.yaml │ │ ├── auth_proxy_service.yaml │ │ ├── kustomization.yaml │ │ ├── leader_election_role.yaml │ │ ├── leader_election_role_binding.yaml │ │ └── role_binding.yaml │ └── webhook │ ├── kustomization.yaml │ ├── kustomizeconfig.yaml │ └── service.yaml ├── go.mod ├── go.sum ├── hack │ └── boilerplate.go.txt └── main.go
这里kubebuilder帮我们生成了一下模板文件夹,包括解决crd的rbac, cert, webhook的文件。暂时不用管他们,这时需要保证你的终端能访问k8s的测试集群,简单就是用kubectl cluster-info
看看是否出错,如果不出错,就可以run起来main.go了
1 2 kubectl cluster-info go run main.go
可以看到终端输出的log:
1 2 3 2019-12-28T00:22:09.789+0800 INFO controller-runtime.metrics metrics server is starting to listen {"addr" : ":8080" } 2019-12-28T00:22:09.789+0800 INFO setup starting manager 2019-12-28T00:22:09.790+0800 INFO controller-runtime.manager starting metrics server {"path" : "/metrics" }
从main.go
里面可以看出其实kubebuilder帮我们生成一个管理controller的manager的代码,但是还没添加controller(controller是指管理crd的控制器):
1 2 3 4 5 6 7 8 9 10 11 12 13 func main () {... mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ Scheme: scheme, MetricsBindAddress: metricsAddr, LeaderElection: enableLeaderElection, Port: 9443 , }) ... if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { ... } }
ok, 接下来我们就可以用kubebuilder帮我们创建一个我们想要的crd,我就叫这个crd为Object 吧:
1 kubebuilder create api --group infra --version v1 --kind Object
这里简单注意一下, group
, version
, kind
这三个属性组合起来来标识一个k8s的crd。另外就是kind
要首字母大写而且不能有特殊符号。
执行上面的命令之后,kubebuilder就帮我们创建了两个文件api/v1/object_types.go
和controllers/object_controller.go
, 前者是这个crd需要定义哪些属性,而后者是对crd的reconsile的处理逻辑(也就是增删改crd的逻辑), 我们后面再讲这两个文件。最后呢,在main.go里面,我们定义的Object 对应的controller会注册到之前生成的manager里:
1 2 3 4 5 6 7 8 9 10 11 function main(){ ... if err = (&controllers.ObjectReconciler{ Client: mgr.GetClient(), Log: ctrl.Log.WithName("controllers" ).WithName("Object" ), Scheme: mgr.GetScheme(), }).SetupWithManager(mgr); err != nil { ... } ...
聪明的你一定能猜到,我们反复执行kubebuilder create api xxx
这条命令就会帮我们创建和注册不同的controller到manager里面。
回过头我们再看一下api/v1/object_types.go
,这里我在spec里面加一个Detail
,在status里面加一个Created
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 type ObjectSpec struct { Foo string `json:"foo,omitempty"` Detail string `json:"detail,omitempty"` } type ObjectStatus struct { Created bool `json:"created,omitempy"` }
实际上kubebuilder就是帮我们生成Object的spec和status的模板,从注释就也可以看出来spec是我们期望的crd状态,而status就是观测到的状态,具体也可以参见k8s对一个对象的定义 。可以看到默认定义下面,kubebuilder会为我们生成对应的yaml文件在config/samples/infra_v1_object.yaml
:
1 2 3 4 5 6 7 8 9 --- apiVersion: infra.demo.com/v1 kind: Object metadata: name: object-sample spec: foo: bar detail: "detail for demo"
在controllers/object_controller.go
,也就是kubebuilder帮我们生成的Reconcile代码里面,我添加了打印Detail
的信息,并且把Created
改成true:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 func (r *ObjectReconciler) Reconcile (req ctrl.Request) (ctrl.Result, error) { ctx := context.Background() _ = r.Log.WithValues("object" , req.NamespacedName) obj := &infrav1.Object{} if err := r.Get(ctx, req.NamespacedName, obj); err != nil { fmt.Errorf("couldn't find object:%s" , req.String()) } else { r.Log.V(1 ).Info("Successfully get detail" , "Detail" , obj.Spec.Detail) r.Log.V(1 ).Info("" , "Created" , obj.Status.Created) } if !obj.Status.Created { obj.Status.Created = true r.Update(ctx, obj) } return ctrl.Result{}, nil }
由于kubebuilder要用到kustomize ,所以先确保先装好了, mac的安装方式:
然后就install生成的crd并且运行修改代码后的manager:
1 2 make install go run main.go
现在我们就可以用config/samples/infra_v1_object.yaml
创建一个Object:
1 kubectl create -f config/samples/infra_v1_object.yaml
在运行manager的终端里面可以看到我们刚才添加的代码打印出来的log:
1 2 3 4 5 2019-12-28T23:39:34.963+0800 DEBUG controllers.Object Successfully get detail {"Detail" : "detail for demo" } 2019-12-28T23:39:34.963+0800 DEBUG controllers.Object {"Created" : false } 2019-12-28T23:39:35.019+0800 DEBUG controller-runtime.controller Successfully Reconciled {"controller" : "object" , "request" : "default/object-sample" } 2019-12-28T23:39:35.019+0800 DEBUG controllers.Object Successfully get detail {"Detail" : "detail for demo" } 2019-12-28T23:39:35.019+0800 DEBUG controllers.Object {"Created" : true }
再看看从k8s里面看到的这个Object的状态:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 kubectl get object object-sample -o yaml apiVersion: infra.demo.com/v1 kind: Object metadata: creationTimestamp: "2019-12-28T15:39:34Z" generation: 2 name: object-sample namespace: default resourceVersion: "433782" selfLink: /apis/infra.demo.com/v1/namespaces/default/objects/object-sample uid: 3f4b368d-2988-11ea-a544-080027d6a71e spec: detail: detail for demo foo: bar status: Created: true
可以看到这个object-sample
的status.Created
已经被修改为true了
3.其他 关于没有介绍的crt,webhook和finalizer,官网手册有比较详细的用法介绍。我就简单谈一下我的理解,如果有错误请纠正:
webhook ,其实就是当我们添加和修改一个Object 时,我们需要对Object的合法性进行判断,所以可以通过webhook的framework来进行合法性的判定,所以kubebuilder可以生成对应的webhook代码;
crt ,用于解决webhook访问k8s时所需要的证书问题,官网也建议使用crt-manager 解决证书问题;
finalizer ,就是在删除Object时,由于这个Object 可能创建一些其他的resource比如pod之类的,又或者在删除之前,我们需要做一些清理工作,finalizer 就是实现这个清理的framework代码;
另外kubebuilder也是支持把这个manager部署为deployment,但是调试起来比较麻烦,所以就只用go run
的形式演示了。
小结 这篇blog简单的介绍了一下kubebuilder开发crd的基本过程,没有深入过多的代码原理,可能也有不少错误地方,麻烦帮忙纠正。另外大家可能跟我刚学习kubebuilder的时候一样,只能照着官网教程敲命令,kubebuilder生成的代码就像一个黑盒一样,接下来目标就是专门整理一下kubebuilder生成crd代码流程和结构。