本文介紹如何使用 kubebuilder 實現(xiàn)一個存儲用戶信息的 CRD啼器,同時開發(fā) controller 綁定同名的 ServiceAccount腥寇。
不過多介紹 kubebuilder 的理論知識术羔,直接開干。
開發(fā)環(huán)境準備
初始化 kubebuilder
mkdir lanyulei
cd lanyulei
kubebuilder init --domain lanyulei.com --repo lanyulei
- init:初始化命令參數(shù)嚼贡。
- --domain:可以理解為接口組的分組。根據(jù)實際情況來定,我一般定義為
項目名.com
。 - --repo:項目名稱北苟,若是當前項目下已經(jīng)存在 go.mod彩扔,則無需此參數(shù)。
初始化成功后的代碼結(jié)構(gòu)习瑰。
.
├── 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
開啟支持多接口組
如果你本次項目開發(fā)的 operator
是多接口組
的話第喳,則需要執(zhí)行一下命令。
例如:同時需要用戶相關(guān)接口
和角色相關(guān)接口
,則需要兩組 api group
伙判,因此需要執(zhí)行以下操作菇曲。
kubebuilder edit --multigroup=true
需要的工具
這三個工具是需要提前下載好的弥雹,放在項目的根目錄下的 bin
目錄下面的。
controller-gen (Version: v0.8.0)
kustomize (Version: v4.5.4)
setup-envtest (Version: latest)
Github 下載對應(yīng)系統(tǒng)的二進制文件即可项钮,版本的話亚隙,我測試的版本已標注亦歉,根據(jù)實際情況自行調(diào)整版本即可。
注意:工具下載完后,放到 bin
目錄后鞠值,后面操作才可正常執(zhí)行。
創(chuàng)建 API
執(zhí)行以下命令創(chuàng)建我們需要 api group
。
$ kubebuilder create api --group user --version v1 --kind User
Create Resource [y/n] # 是否創(chuàng)建資源對象
y
Create Controller [y/n] # 是否創(chuàng)建 controller
y
- --group:接口分組
- --version:接口版本
- --kind:對應(yīng) k8s 資源對象中的 kind
創(chuàng)建接口組后的代碼結(jié)構(gòu)
.
├── 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
代碼實現(xiàn)
定義用戶字段
通過完善 Spec
后綴的結(jié)構(gòu)體斜友,來完善 k8s 中資源對象對應(yīng)的 spec
字段晒衩。
我們在這里加上用戶相關(guān)的字段描述靠胜。
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"`
}
controller 開發(fā)
此部分開發(fā)该镣,主要是綁定 ServiceAccount
。
在創(chuàng)建 User
的時候棉圈,則創(chuàng)建對應(yīng)同名的 ServiceAccount
分瘾,刪除亦同理。
為方便統(tǒng)一管理背传,將 ServiceAccount
統(tǒng)一存放在 lanyulei_users
的命名空間中径玖。
kubebuilder 幫我們實現(xiàn)了大部分功能,因此我們只需要實現(xiàn) Reconcile
函數(shù)即可前域,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/controller-runtime@v0.11.0/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
}
// 創(chuàng)建 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,
},
}
// 綁定對應(yīng)的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
這種格式的注釋,此類注釋是為了實現(xiàn)代碼生成
而添加的注釋斗忌,此類內(nèi)容較多,可通過搜索引擎唧躲,進行了解即可。
部署
首先我們需要本地需要有 kubectl
命令嘴秸,并且可以連接到 k8s
集群凭疮。如果滿足這個條件,那么我們就可以部署測試我們的 operator
了衰腌。
將 crd 部署到 k8s 集群上吮螺。
kubebuilder 幫我們寫好了 Makefile
如果沒有定制化的需求紫岩,例如指定 k8s 集群配置文件被因,則直接執(zhí)行下面的命令即可堕花,若是有此類需求壕曼,還請自行調(diào)整 Makefile
摹蘑。
部署 crd 到 k8s 集群
make install
本地啟動 controller
make run
controller 部署到 k8s 集群運行
前面我們在開發(fā)環(huán)境將 controller 運行起來嘗試了所有功能过咬,在實際生產(chǎn)環(huán)境中,controller 并非這樣獨立于 k8s 之外烫幕,而是以 pod 的狀態(tài)運行在 k8s 之中驱显,接下來我們嘗試將 controller 代碼編譯構(gòu)建成 docker 鏡像瞳抓,再在k8s上運行起來。
首先你需要有一個 docker hub 的賬號胳蛮,然后使用 docker login
命令進行登陸抚垄。
執(zhí)行下面的命令構(gòu)建鏡像并推送到 docker hub桐经。
make docker-build docker-push IMG=lanyulei/lanyulei:v1.0.0
若推送網(wǎng)速過慢,可自行配置阿里云容器鏡像加速器
摹菠。
鏡像準備好之后蔽介,執(zhí)行以下命令即可在 k8s 集群中部署 controller。
make deploy IMG=lanyulei/lanyulei:v1.0.0
驗證部署結(jié)果。
[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 正常啟動了,可以處理請求了辆琅。
自此我們開發(fā)娩井,存儲管理用戶信息的 operator
就開發(fā)完成洞辣,可以通過 postman
昙衅, 測試接口的增刪改查。
本文為原創(chuàng)文章啼县,未經(jīng)授權(quán)禁止轉(zhuǎn)載本站文章麦向。
原文出處:蘭玉磊的個人博客
原文鏈接:https://www.fdevops.com/2022/04/10/kubebuilder-crd-31074
版權(quán):本文采用「署名-非商業(yè)性使用-相同方式共享 4.0 國際」知識共享許可協(xié)議進行許可话告。