kubebuilder 實戰(zhàn)之開發(fā)一個存儲用戶信息的 operator

本文介紹如何使用 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 會返回當前變更的對象的 NamespaceName 信息学赛,有這兩個信息,我們就可以獲取到這個對象了痒蓬,進行處理了鲁捏。

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é)議進行許可话告。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末病线,一起剝皮案震驚了整個濱河市纺裁,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,997評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件怨愤,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評論 3 392
  • 文/潘曉璐 我一進店門外冀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來西轩,“玉大人捅伤,你說我怎么就攤上這事熄诡⊥嗉耄” “怎么了裳凸?”我有些...
    開封第一講書人閱讀 163,359評論 0 353
  • 文/不壞的土叔 我叫張陵瞎颗,是天一觀的道長嚼蚀。 經(jīng)常有香客問我僻孝,道長斋荞,這世上最難降的妖魔是什么蜈彼? 我笑而不...
    開封第一講書人閱讀 58,309評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上菇怀,老公的妹妹穿的比我還像新娘呼伸。我一直安慰自己珍促,他們只是感情好仁卷,可當我...
    茶點故事閱讀 67,346評論 6 390
  • 文/花漫 我一把揭開白布背蟆。 她就那樣靜靜地躺著,像睡著了一般钢悲。 火紅的嫁衣襯著肌膚如雪惭等。 梳的紋絲不亂的頭發(fā)上琳要,一...
    開封第一講書人閱讀 51,258評論 1 300
  • 那天,我揣著相機與錄音杜秸,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的巡李。 我是一名探鬼主播辐宾,決...
    沈念sama閱讀 40,122評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了酌畜?” 一聲冷哼從身側(cè)響起埠戳,我...
    開封第一講書人閱讀 38,970評論 0 275
  • 序言:老撾萬榮一對情侶失蹤在岂,失蹤者是張志新(化名)和其女友劉穎及老,沒想到半個月后匕垫,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,403評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡洲鸠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,596評論 3 334
  • 正文 我和宋清朗相戀三年瘾腰,在試婚紗的時候發(fā)現(xiàn)自己被綠了费薄。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片召廷。...
    茶點故事閱讀 39,769評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡筹煮,死狀恐怖仑鸥,靈堂內(nèi)的尸體忽然破棺而出意狠,到底是詐尸還是另有隱情闷板,我是刑警寧澤拦止,帶...
    沈念sama閱讀 35,464評論 5 344
  • 正文 年R本政府宣布顶瞒,位于F島的核電站守问,受9級特大地震影響朋譬,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鲤氢,卻給世界環(huán)境...
    茶點故事閱讀 41,075評論 3 327
  • 文/蒙蒙 一寝并、第九天 我趴在偏房一處隱蔽的房頂上張望弦牡。 院中可真熱鬧稻据,春花似錦算柳、人聲如沸何荚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽乃摹。三九已至叭莫,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間悼院,已是汗流浹背颖医。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評論 1 269
  • 我被黑心中介騙來泰國打工俺榆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 47,831評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像线椰,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子配紫,可洞房花燭夜當晚...
    茶點故事閱讀 44,678評論 2 354

推薦閱讀更多精彩內(nèi)容