本文介绍如何使用 kubebuilder 实现一个存储用户信息的 CRD,同时开发 controller 绑定同名的 ServiceAccount。
不过多介绍 kubebuilder 的理论知识,直接开干。
mkdir lanyulei
cd lanyulei
kubebuilder init --domain lanyulei.com --repo lanyulei
项目名.com
。初始化成功后的代码结构。
.
├── Dockerfile
├── Makefile
├── PROJECT
├── config
│ ├── default
│ │ ├── kustomization.yaml
│ │ ├── manager_auth_proxy_patch.yaml
│ │ └── manager_config_patch.yaml
│ ├── manager
│ │ ├── controller_manager_config.yaml
│ │ ├── kustomization.yaml
│ │ └── manager.yaml
│ ├── prometheus
│ │ ├── kustomization.yaml
│ │ └── monitor.yaml
│ └── rbac
│ ├── auth_proxy_client_clusterrole.yaml
│ ├── 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
│ └── service_account.yaml
├── go.mod
├── go.sum
├── hack
│ └── boilerplate.go.txt
└── main.go
如果你本次项目开发的 operator
是多接口组
的话,则需要执行一下命令。
例如:同时需要用户相关接口
和角色相关接口
,则需要两组 api group
,因此需要执行以下操作。
kubebuilder edit --multigroup=true
这三个工具是需要提前下载好的,放在项目的根目录下的 bin
目录下面的。
controller-gen (Version: v0.8.0)
kustomize (Version: v4.5.4)
setup-envtest (Version: latest)
Github 下载对应系统的二进制文件即可,版本的话,我测试的版本已标注,根据实际情况自行调整版本即可。
注意:工具下载完后,放到 bin
目录后,后面操作才可正常执行。
执行以下命令创建我们需要 api group
。
$ kubebuilder create api --group user --version v1 --kind User
Create Resource [y/n] # 是否创建资源对象
y
Create Controller [y/n] # 是否创建 controller
y
创建接口组后的代码结构
.
├── Dockerfile
├── Makefile
├── PROJECT
├── apis
│ └── user # 用户接口组
│ └── v1
│ ├── groupversion_info.go
│ ├── user_types.go
│ └── zz_generated.deepcopy.go
├── bin # 常用工具目录
│ ├── controller-gen
│ ├── kustomize
│ └── setup-envtest
├── config
│ ├── crd
│ │ ├── kustomization.yaml
│ │ ├── kustomizeconfig.yaml
│ │ └── patches
│ │ ├── cainjection_in_users.yaml
│ │ └── webhook_in_users.yaml
│ ├── default
│ │ ├── kustomization.yaml
│ │ ├── manager_auth_proxy_patch.yaml
│ │ └── manager_config_patch.yaml
│ ├── manager
│ │ ├── controller_manager_config.yaml
│ │ ├── kustomization.yaml
│ │ └── manager.yaml
│ ├── prometheus
│ │ ├── kustomization.yaml
│ │ └── monitor.yaml
│ ├── rbac
│ │ ├── auth_proxy_client_clusterrole.yaml
│ │ ├── 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
│ │ ├── service_account.yaml
│ │ ├── user_editor_role.yaml
│ │ └── user_viewer_role.yaml
│ └── samples
│ └── user_v1_user.yaml
├── controllers # controller 管理
│ └── user
│ ├── suite_test.go
│ └── user_controller.go
├── go.mod
├── go.sum
├── hack
│ └── boilerplate.go.txt
└── main.go
通过完善 Spec
后缀的结构体,来完善 k8s 中资源对象对应的 spec
字段。
我们在这里加上用户相关的字段描述。
apis/user/v1/user_types.go
// UserSpec defines the desired state of User
type UserSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "make" to regenerate code after modifying this file
// Nickname 用户名
Nickname string `json:"nickname,omitempty"`
// Password 密码
Password string `json:"password,omitempty"`
// Email 邮箱地址
Email string `json:"email,omitempty"`
// Tel 手机号
Tel string `json:"tel,omitempty"`
// IsAdmin 是否超级管理员
IsAdmin string `json:"is_admin,omitempty"`
// IsActive 是否可用
IsActive string `json:"is_active,omitempty"`
}
此部分开发,主要是绑定 ServiceAccount
。
在创建 User
的时候,则创建对应同名的 ServiceAccount
,删除亦同理。
为方便统一管理,将 ServiceAccount
统一存放在 lanyulei_users
的命名空间中。
kubebuilder 帮我们实现了大部分功能,因此我们只需要实现 Reconcile
函数即可,req
会返回当前变更的对象的 Namespace
和 Name
信息,有这两个信息,我们就可以获取到这个对象了,进行处理了。
controllers/user/user_controller.go
//+kubebuilder:rbac:groups=user.lanyulei.com,resources=users,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=user.lanyulei.com,resources=users/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=user.lanyulei.com,resources=users/finalizers,verbs=update
//+kubebuilder:rbac:groups=core,resources=serviceaccounts,verbs=get;list;create;delete # 添加此项注释,表示为当前 controller 可对 ServiceAccount 进行 get、list、create、delete 操作。
// Reconcile is part of the main k8s reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
// TODO(user): Modify the Reconcile function to compare the state specified by
// the User object against the actual cluster state, and then
// perform operations to make the cluster state reflect the state specified by
// the user.
//
// For more details, check Reconcile and its Result here:
// - https://pkg.go.dev/sigs.k8s.io/[email protected]/pkg/reconcile
func (r *UserReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
// 判断同名 ServiceAccount 是否存在
sa := &corev1.ServiceAccount{}
saReq := ctrl.Request{
NamespacedName: types.NamespacedName{
Namespace: UserNamespace,
Name: req.Name,
},
}
err := r.Get(ctx, saReq.NamespacedName, sa)
if errors.IsNotFound(err) {
err := r.createServiceAccountIfNotExists(ctx, req)
if err != nil {
return ctrl.Result{}, err
}
}
return ctrl.Result{}, nil
}
// 创建 ServiceAccount
func (r *UserReconciler) createServiceAccountIfNotExists(ctx context.Context, req ctrl.Request) (err error) {
logger := log.Log.WithValues("func", "createServiceAccountIfNotExists")
logger.Info("start create service account.")
user := &userv1.User{}
err = r.Get(ctx, req.NamespacedName, user)
if err != nil {
logger.Error(err, "Failed to get user.")
return
}
sa := &corev1.ServiceAccount{
ObjectMeta: metav1.ObjectMeta{
Name: req.Name,
Namespace: UserNamespace,
},
}
// 绑定对应的sa,删除的时候连带着删除
err = controllerutil.SetControllerReference(user, sa, r.Scheme)
if err != nil {
logger.Error(err, "SetControllerReference error")
return
}
err = r.Create(ctx, sa)
if err != nil {
logger.Error(err, "create service account error")
return
}
return
}
上面的代码中,我们看到了好多 //+kubebuilder
这种格式的注释,此类注释是为了实现代码生成
而添加的注释,此类内容较多,可通过搜索引擎,进行了解即可。
首先我们需要本地需要有 kubectl
命令,并且可以连接到 k8s
集群。如果满足这个条件,那么我们就可以部署测试我们的 operator
了。
将 crd 部署到 k8s 集群上。
kubebuilder 帮我们写好了 Makefile
如果没有定制化的需求,例如指定 k8s 集群配置文件,则直接执行下面的命令即可,若是有此类需求,还请自行调整 Makefile
。
make install
make run
前面我们在开发环境将 controller 运行起来尝试了所有功能,在实际生产环境中,controller 并非这样独立于 k8s 之外,而是以 pod 的状态运行在 k8s 之中,接下来我们尝试将 controller 代码编译构建成 docker 镜像,再在k8s上运行起来。
首先你需要有一个 docker hub 的账号,然后使用 docker login
命令进行登陆。
执行下面的命令构建镜像并推送到 docker hub。
make docker-build docker-push IMG=lanyulei/lanyulei:v1.0.0
若推送网速过慢,可自行配置阿里云容器镜像加速器
。
镜像准备好之后,执行以下命令即可在 k8s 集群中部署 controller。
make deploy IMG=lanyulei/lanyulei:v1.0.0
验证部署结果。
[root@karmada-work-1 ~]# kubectl get po -A
NAMESPACE NAME READY STATUS RESTARTS AGE
cert-manager cert-manager-64d9bc8b74-ptl4n 1/1 Running 0 149m
cert-manager cert-manager-cainjector-6db6b64d5f-xcw2d 1/1 Running 0 149m
cert-manager cert-manager-webhook-6c9dd55dc8-wk8lw 1/1 Running 0 149m
kube-system coredns-64897985d-wtcqq 1/1 Running 0 15h
kube-system coredns-64897985d-x8g7s 1/1 Running 0 15h
kube-system etcd-karmada-work-2-control-plane 1/1 Running 0 15h
kube-system kindnet-8wcr6 1/1 Running 0 15h
kube-system kube-apiserver-karmada-work-2-control-plane 1/1 Running 0 15h
kube-system kube-controller-manager-karmada-work-2-control-plane 1/1 Running 0 15h
kube-system kube-proxy-5w2ln 1/1 Running 0 15h
kube-system kube-scheduler-karmada-work-2-control-plane 1/1 Running 0 15h
local-path-storage local-path-provisioner-5ddd94ff66-fkw28 1/1 Running 0 15h
# 这个就是我们部署的 controller, 2/2 表示容器运行中了。
lanyulei-system lanyulei-controller-manager-7cb9cd6565-8wst8 2/2 Running 0 96m
[root@karmada-work-1 ~]#
查看日志,确认程序是否正常启动了。
kubectl logs -f \
lanyulei-controller-manager-7cb9cd6565-8wst8 \
-c manager \
-n lanyulei-system
没有 Error 日志,则表示 controller 正常启动了,可以处理请求了。
自此我们开发,存储管理用户信息的 operator
就开发完成,可以通过 postman
, 测试接口的增删改查。
本文为原创文章,未经授权禁止转载本站文章。 原文出处:兰玉磊的个人博客 原文链接:https://www.fdevops.com/2022/04/10/kubebuilder-crd-31074 版权:本文采用「署名-非商业性使用-相同方式共享 4.0 国际」知识共享许可协议进行许可。