kubebuilder学习参考文章:
kubebuilder github
使用 kubebuilder 创建 operator 示例
kubebuilder云原生开发
一、项目创建
- 创建项目文件目录
mkdir kubebuilder-example
cd kubebuilder-example
- 初始化module
通过mod管理,不需要将go项目放在GOPATH目录下,但是在通过kubebuilder创建架子之前,需要初始化一下go.mod
~/kubebuilder-example go mod init example.io
go: creating new go.mod: module example.io
内容如下
~//kubebuilder-example cat go.mod
module example.io
go 1.14
- kubebuilder脚手架搭建项目
~/kubebuilder-example kubebuilder init --domain example.io
Writing scaffold for you to edit...
Get controller runtime:
$ go get sigs.k8s.io/controller-runtime@v0.5.0
go: downloading sigs.k8s.io/controller-runtime v0.5.0
go: downloading k8s.io/apimachinery v0.17.2
go: downloading k8s.io/client-go v0.17.2
......
Next: define a resource with:
$ kubebuilder create api
创建完成后,内容如下
~/kubebuilder-example tree
├── Dockerfile
├── Makefile
├── PROJECT
├── bin
│ └── manager
├── config
│ ├── certmanager
│ ├── crd
│ ├── default
│ ├── manager
│ ├── prometheus
│ ├── rbac
│ ├── samples
│ └── webhook
├── go.mod
├── go.sum
├── hack
│ └── boilerplate.go.txt
└── main.go
创建示例api如下
~/GolandProjects/kubebuilder-example kubebuilder create api --group webapp --version v1 --kind Guestbook
Create Resource [y/n]
y
Create Controller [y/n]
y
Writing scaffold for you to edit...
api/v1/guestbook_types.go
controllers/guestbook_controller.go
......
go build -o bin/manager main.go
创建完成后
~/kubebuilder-example tree
├── Dockerfile
├── Makefile
├── PROJECT
├── api
│ └── v1
│ ├── groupversion_info.go
│ ├── guestbook_types.go
│ └── zz_generated.deepcopy.go
├── bin
│ └── manager
├── config
│ ├── certmanager
│ ├── crd
│ ├── default
│ ├── manager
│ ├── prometheus
│ ├── rbac
│ ├── samples
│ └── webhook
├── controllers
│ ├── guestbook_controller.go
│ └── suite_test.go
├── go.mod
├── go.sum
├── hack
│ └── boilerplate.go.txt
└── main.go
- 安装crd
$ make install
本地部署 controller,便于调试
$ make run
将在本地看到调试和输出
将 controller 部署到 Kubernetes
执行下面的命令部署 controller 到 Kubernetes 上,这一步将会在本地构建 controller 的镜像,并推送到 DockerHub 上,然后在 Kubernetes 上部署 Deployment 资源。
make docker-build docker-push IMG=woshiwoniu/kubebuilder-example:latest
make deploy IMG=woshiwoniu/kubebuilder-example:latest
在初始化项目时,kubebuilder 会自动根据项目名称创建一个 Namespace,如本文中的 kubebuilder-example-system,查看 Deployment 对象和 Pod 资源。
$ kubectl get deployment -n kubebuilder-example-system
NAME READY UP-TO-DATE AVAILABLE AGE
kubebuilder-example-controller-manager 1/1 1 1 3h26m
$ kubectl get pod -n kubebuilder-example-system
NAME READY STATUS RESTARTS AGE
kubebuilder-example-controller-manager-77b4c685f9-2npz8 2/2 Running 0 3h16m
创建 CR
Kubebuilder 在初始化项目的时候已生成了示例 CR,执行下面的命令部署 CR。
kubectl apply -f config/samples/webapp_v1_guestbook.yaml
执行下面的命令查看新创建的 CR。
$ kubectl get guestbooks.webapp.example.io guestbook-sample -o yaml
至此一个基本的 Operator 框架已经创建完成,但这个 Operator 只是修改了 etcd 中的数据而已,实际上什么事情也没做,因为我们没有在 Operator 中的增加业务逻辑。
增加业务逻辑
下面我们将修改 CRD 的数据结构并在 controller 中增加一些日志输出。
修改 CRD
我们将修改上文中使用 kubebuilder 命令生成的默认 CRD 配置,在 CRD 中增加 FirstName、LastName 和 Status 字段。
下面是修改后的 api/v1/guestbook_types.go 文件的内容,对应修改的地方已在代码中注释说明。
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
// GuestbookSpec defines the desired state of Guestbook
type GuestbookSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "make" to regenerate code after modifying this file
// Foo is an example field of Guestbook. Edit Guestbook_types.go to remove/update
// 添加两个新的字段
FirstName string `json:"firstname"`
LastName string `json:"lastname"`
}
// GuestbookStatus defines the observed state of Guestbook
type GuestbookStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "make" to regenerate code after modifying this file
Status string `json:"Status"`
}
// +kubebuilder:object:root=true
// 在这里增加 status 的说明
// +kubebuilder:subresource:status
// Guestbook is the Schema for the guestbooks API
type Guestbook struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec GuestbookSpec `json:"spec,omitempty"`
Status GuestbookStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
// GuestbookList contains a list of Guestbook
type GuestbookList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Guestbook `json:"items"`
}
func init() {
SchemeBuilder.Register(&Guestbook{}, &GuestbookList{})
}
上面的代码比原先使用 kubebuilder 生成的默认代码增加了以下内容:
FirstName string `json:"firstname"`
LastName string `json:"lastname"`
Status string `json:"Status"`
// +kubebuilder:subresource:status
修改 Reconcile 函数
Reconcile 函数是 Operator 的核心逻辑,Operator 的业务逻辑都位于 controllers/guestbook_controller.go 文件的 func (r *GuestbookReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) 函数中。
// +kubebuilder:rbac:groups=webapp.example.io,resources=guestbooks,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=webapp.example.io,resources=guestbooks/status,verbs=get;update;patch
func (r *GuestbookReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
_ = context.Background()
_ = r.Log.WithValues("guestbook", req.NamespacedName)
// your logic here
ctx := context.Background()
_ = r.Log.WithValues("apiexamplea", req.NamespacedName)
// 获取当前的 CR,并打印
obj := &webappv1.Guestbook{}
if err := r.Get(ctx, req.NamespacedName, obj); err != nil {
log.Println(err, "Unable to fetch object")
} else {
log.Println("Geeting from Kubebuilder to", obj.Spec.FirstName, obj.Spec.LastName)
}
// 初始化 CR 的 Status 为 Running
obj.Status.Status = "Running"
if err := r.Status().Update(ctx, obj); err != nil {
log.Println(err, "unable to update status")
}
return ctrl.Result{}, nil
}
这段代码的业务逻辑是当发现有 guestbooks.webapp.example.io 的 CR 变更时,在控制台中输出日志。
运行测试
修改好 Operator 的业务逻辑后,再测试一下新的逻辑是否可以正常运行。
部署 CRD
跟上文的做法一样,执行下面的命令部署 CRD。
make install
运行 controller
跟上文的做法一样,执行下面的命令运行 controller。为了方便起见,我们将在本地运行 controller,当然您也可以将其部署到 Kubernetes 上运行。
make run
保持该窗口在前台运行。
部署 CR
修改 config/samples/webapp_v1_guestbook.yaml 文件中的配置,将之前默认的foo字段删除,添加firstname和lastname。
apiVersion: webapp.example.io/v1
kind: Guestbook
metadata:
name: guestbook-sample
spec:
# Add fields here
firstname: Jimmy
lastname: Song
将其应用到 Kubernetes。
kubectl apply -f config/samples/webapp_v1_guestbook.yaml
此时转到上文中运行 controller 的窗口,将在命令行前台中看到如下输出。
go fmt ./...
go vet ./...
/Users/test/Workspace/go/bin/controller-gen "crd:trivialVersions=true" rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases
go run ./main.go
2020-06-07T16:48:29.966+0800 INFO controller-runtime.metrics metrics server is starting to listen {"addr": ":8080"}
2020-06-07T16:48:29.967+0800 INFO setup starting manager
2020-06-07T16:48:29.967+0800 INFO controller-runtime.manager starting metrics server {"path": "/metrics"}
2020-06-07T16:48:29.967+0800 INFO controller-runtime.controller Starting EventSource {"controller": "guestbook", "source": "kind source: /, Kind="}
2020-06-07T16:48:30.068+0800 INFO controller-runtime.controller Starting Controller {"controller": "guestbook"}
2020-06-07T16:48:30.068+0800 INFO controller-runtime.controller Starting workers {"controller": "guestbook", "worker count": 1}
2020/06/07 16:48:30 Geeting from Kubebuilder to Jimmy Song
2020-06-07T16:48:30.080+0800 DEBUG controller-runtime.controller Successfully Reconciled {"controller": "guestbook", "request": "kubebuilder-example-system/guestbook-sample"}
从上面的日志中,可以看到这条输出。
2020/06/07 16:48:30 Geeting from Kubebuilder to Jimmy Song
这正是在 Reconcile 函数中的输出。
删除 CR
使用下面的命令删除 CR,这里的资源类型以Kind的小写加上apiVersion拼接。
kubectl delete guestbooks.webapp.example.io guestbook-sample