关于程序员:kubebuilder-实战之开发一个存储用户信息的-operator

31次阅读

共计 7320 个字符,预计需要花费 19 分钟才能阅读完成。

本文介绍如何应用 kubebuilder 实现一个存储用户信息的 CRD,同时开发 controller 绑定同名的 ServiceAccount。

不过多介绍 kubebuilder 的理论知识,间接开干。

开发环境筹备

初始化 kubebuilder

mkdir lanyulei
cd lanyulei
kubebuilder init --domain lanyulei.com --repo lanyulei
  • init:初始化命令参数。
  • –domain:能够了解为接口组的分组。依据理论状况来定,我个别定义为 我的项目名.com
  • –repo:项目名称,若是以后我的项目下曾经存在 go.mod,则无需此参数。

初始化胜利后的代码构造。

.
├── 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

执行以下命令创立咱们须要 api group

$ kubebuilder create api --group user --version v1 --kind User

Create Resource [y/n] # 是否创立资源对象
y
Create Controller [y/n] # 是否创立 controller
y
  • –group:接口分组
  • –version:接口版本
  • –kind:对应 k8s 资源对象中的 kind

创立接口组后的代码构造

.
├── 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"`
}

controller 开发

此局部开发,次要是绑定 ServiceAccount

在创立 User 的时候,则创立对应同名的 ServiceAccount,删除亦同理。

为不便对立治理,将 ServiceAccount 对立寄存在 lanyulei_users 的命名空间中。

kubebuilder 帮咱们实现了大部分性能,因而咱们只须要实现 Reconcile 函数即可,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}

// 创立 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

部署 crd 到 k8s 集群

make install

本地启动 controller

make run

controller 部署到 k8s 集群运行

后面咱们在开发环境将 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/…
版权:本文采纳「署名 - 非商业性应用 - 雷同形式共享 4.0 国内」常识共享许可协定进行许可。

正文完
 0