kubebuilder 實(shí)戰(zhàn)之開發(fā)一個(gè)存儲(chǔ)用戶信息的 operator

本文介紹如何使用 kubebuilder 實(shí)現(xiàn)一個(gè)存儲(chǔ)用戶信息的 CRD,同時(shí)開發(fā) controller 綁定同名的 ServiceAccount。

不過多介紹 kubebuilder 的理論知識(shí),直接開干。

開發(fā)環(huán)境準(zhǔn)備

初始化 kubebuilder

mkdir lanyulei
cd lanyulei
kubebuilder init --domain lanyulei.com --repo lanyulei
  • init:初始化命令參數(shù)。
  • --domain:可以理解為接口組的分組。根據(jù)實(shí)際情況來定,我一般定義為項(xiàng)目名.com
  • --repo:項(xiàng)目名稱,若是當(dāng)前項(xiàng)目下已經(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

開啟支持多接口組

如果你本次項(xiàng)目開發(fā)的 operator多接口組的話,則需要執(zhí)行一下命令。

例如:同時(shí)需要用戶相關(guān)接口角色相關(guān)接口,則需要兩組 api group,因此需要執(zhí)行以下操作。

kubebuilder edit --multigroup=true

需要的工具

這三個(gè)工具是需要提前下載好的,放在項(xiàng)目的根目錄下的 bin 目錄下面的。

controller-gen (Version: v0.8.0)
kustomize (Version: v4.5.4)
setup-envtest (Version: latest)

Github 下載對(duì)應(yīng)系統(tǒng)的二進(jìn)制文件即可,版本的話,我測試的版本已標(biāo)注,根據(jù)實(shí)際情況自行調(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)建資源對(duì)象
y
Create Controller [y/n] # 是否創(chuàng)建 controller
y
  • --group:接口分組
  • --version:接口版本
  • --kind:對(duì)應(yīng) k8s 資源對(duì)象中的 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

代碼實(shí)現(xiàn)

定義用戶字段

通過完善 Spec 后綴的結(jié)構(gòu)體,來完善 k8s 中資源對(duì)象對(duì)應(yīng)的 spec 字段。

我們?cè)谶@里加上用戶相關(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 手機(jī)號(hào)
    Tel string `json:"tel,omitempty"`

    // IsAdmin 是否超級(jí)管理員
    IsAdmin string `json:"is_admin,omitempty"`

    // IsActive 是否可用
    IsActive string `json:"is_active,omitempty"`
}

controller 開發(fā)

此部分開發(fā),主要是綁定 ServiceAccount。

在創(chuàng)建 User 的時(shí)候,則創(chuàng)建對(duì)應(yīng)同名的 ServiceAccount,刪除亦同理。

為方便統(tǒng)一管理,將 ServiceAccount 統(tǒng)一存放在 lanyulei_users 的命名空間中。

kubebuilder 幫我們實(shí)現(xiàn)了大部分功能,因此我們只需要實(shí)現(xiàn) Reconcile 函數(shù)即可,req 會(huì)返回當(dāng)前變更的對(duì)象的 NamespaceName 信息,有這兩個(gè)信息,我們就可以獲取到這個(gè)對(duì)象了,進(jìn)行處理了。

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 # 添加此項(xiàng)注釋,表示為當(dāng)前 controller 可對(duì) ServiceAccount 進(jìn)行 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,
        },
    }

    // 綁定對(duì)應(yīng)的sa,刪除的時(shí)候連帶著刪除
    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 這種格式的注釋,此類注釋是為了實(shí)現(xiàn)代碼生成而添加的注釋,此類內(nèi)容較多,可通過搜索引擎,進(jìn)行了解即可。

部署

首先我們需要本地需要有 kubectl 命令,并且可以連接到 k8s 集群。如果滿足這個(gè)條件,那么我們就可以部署測試我們的 operator 了。

將 crd 部署到 k8s 集群上。

kubebuilder 幫我們寫好了 Makefile 如果沒有定制化的需求,例如指定 k8s 集群配置文件,則直接執(zhí)行下面的命令即可,若是有此類需求,還請(qǐng)自行調(diào)整 Makefile

部署 crd 到 k8s 集群

make install

本地啟動(dòng) controller

make run

controller 部署到 k8s 集群運(yùn)行

前面我們?cè)陂_發(fā)環(huán)境將 controller 運(yùn)行起來嘗試了所有功能,在實(shí)際生產(chǎn)環(huán)境中,controller 并非這樣獨(dú)立于 k8s 之外,而是以 pod 的狀態(tài)運(yùn)行在 k8s 之中,接下來我們嘗試將 controller 代碼編譯構(gòu)建成 docker 鏡像,再在k8s上運(yùn)行起來。

首先你需要有一個(gè) docker hub 的賬號(hào),然后使用 docker login 命令進(jìn)行登陸。

執(zhí)行下面的命令構(gòu)建鏡像并推送到 docker hub。

make docker-build docker-push IMG=lanyulei/lanyulei:v1.0.0

若推送網(wǎng)速過慢,可自行配置阿里云容器鏡像加速器。

鏡像準(zhǔn)備好之后,執(zhí)行以下命令即可在 k8s 集群中部署 controller。

make deploy IMG=lanyulei/lanyulei:v1.0.0

驗(yàn)證部署結(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

# 這個(gè)就是我們部署的 controller, 2/2 表示容器運(yùn)行中了。
lanyulei-system       lanyulei-controller-manager-7cb9cd6565-8wst8            2/2     Running   0          96m
[root@karmada-work-1 ~]# 

查看日志,確認(rèn)程序是否正常啟動(dòng)了。

kubectl logs -f \
lanyulei-controller-manager-7cb9cd6565-8wst8 \
-c manager \
-n lanyulei-system

沒有 Error 日志,則表示 controller 正常啟動(dòng)了,可以處理請(qǐng)求了。

自此我們開發(fā),存儲(chǔ)管理用戶信息的 operator 就開發(fā)完成,可以通過 postman, 測試接口的增刪改查。

本文為原創(chuàng)文章,未經(jīng)授權(quán)禁止轉(zhuǎn)載本站文章。
原文出處:蘭玉磊的個(gè)人博客
原文鏈接:https://www.fdevops.com/2022/04/10/kubebuilder-crd-31074
版權(quán):本文采用「署名-非商業(yè)性使用-相同方式共享 4.0 國際」知識(shí)共享許可協(xié)議進(jìn)行許可。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容