《 Kubebuilder v2 使用指南 》-P4-从零开始设计一个CRD

28 篇文章 0 订阅
8 篇文章 8 订阅

从零开始设计一个CRD

前言

经过前面的铺垫,相信现在对kubebuilder的工作模式已初具了解,那么从本篇开始,正式设计一个CRD。本文对于Unit的设计,是基于我的个人场景下的需求提炼出来的,不一定适合你,但着重点在于思路,希望能有帮助。

面临的现状

在我的场景下,应该也是大多数人的场景下,通常一个运行服务(姑且这么称呼),使用一系列build-in 类型资源进行组合,来保障运行和提供服务,例如,最常用的组合有:StatefulSet/Deployment/Ingress/Service/Ingress这几种资源的按需组合,如下图:

这些资源类型每一种都是可选项,根据使用需求的不同,来灵活(弱绑定?)进行组合。

例如:

  • 非web服务不需要Ingress资源
  • 自发现和注册的应用不需要Service
  • 无状态的应用选用Deployment,有状态的应用选用StatefulSet
  • 有的应用无需持久存储,有的应用需求PVC来实现持久存储

按需组合,不一而同

这样的弱绑定关系在个人认为管理上不足够的友好,每种资源的增删改查等逻辑,需要分别操作,没有整体的连贯性和协同性,需要分而治之。

想要实现什么

与市面上大多数专属于某个应用或者某类应用的CRD不同,我这里想要实现的CRD目标是:每一个运行服务,所用到的的各类资源,以强申明将它们绑定在一起,进行统一的生命周期管理

因此,我给这个CRD的命名是Unit,这意味着,StatefulSet/Deployment/Ingress/Service/Ingress这些可选的build-in资源,由原本松散组合构成一个运行服务的模式,变为集合在一个Unit单元里,形成统一的管理生命周期。如下图:

设计的目标已经设定了,下面就开始实际设计和填充Unit结构体。

结构设计

如同所有的build-in GVR一样,结构体的构成除了主要分为两个部分,一个是Spec,一个是Status,声明式的目标即是Status满足Spec的声明要求。因此,Unit的结构体设计也围绕Spec和Status两部分来。下面直接贴代码,代码中会有注释。

按照上面的描述,Unit总共可管理这5种资源:StatefulSet/Deployment/Ingress/Service/Ingress,为了结构清晰,将它们各自的结构体分别放在不同的文件中:

原本的build-in资源结构体,以Deployment举例,Deployment结构体字段非常多,不够精简,因此我不想每种资源的每个字段都完整声明,而是在前端以最精简地方式声明,只提供必要的字段,而最后填充出完整的Deployment的结构体的方法放在后端来进行,尽可能地保证前端使用用户的友好度。

Spec

直接贴代码

UnitSpec
// UnitSpec defines the desired state of Unit
type UnitSpec struct {
    // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
    // Important: Run "make" to regenerate code after modifying this file

    // Category 支持两种: Deployment / StatefulSet ,二者只能选其一,在admission validating webhook里会做校验.
  
    Category string `json:"category"`

    // Replicas和Selector这两个字段在mutate webhook里默认会有填充
    Replicas *int32                `json:"replicas,omitempty"`
    Selector *metav1.LabelSelector `json:"selector,omitempty"`

    // Template describes the pods that will be created.
    Template         corev1.PodTemplateSpec    `json:"template"`
    RelationResource UnitRelationResourceSpec `json:"relationResource,omitempty"`
}
UnitRelationResourceSpec
type UnitRelationResourceSpec struct {
    Service *OwnService `json:"serviceInfo,omitempty"`
    PVC     *OwnPVC     `json:"pvcInfo,omitempty"`
    Ingress *OwnIngress `json:"ingressInfo,omitempty"`
}

这里指定OwnService/OwnIngress/OwnPVC,至于具体的ownDeployment / ownStatefulSet,由于限制二种部署方式只能取其一,因此会结合UnitSpec.Category和UnitSpec.Template来动态生成,怎么生成放在后面说,先来看看结构体。

OwnStatefulSet
type OwnStatefulSet struct {
    Spec appsv1.StatefulSetSpec
}
OwnDeployment
type OwnDeployment struct {
   Spec appsv1.DeploymentSpec `json:"spec"`
}
OwnService
// svc的端口映射关系
type ServicePort struct {
   Name       string             `json:"name,omitempty" protobuf:"bytes,1,opt,name=name"`
   Protocol   string             `json:"protocol,omitempty" protobuf:"bytes,2,opt,name=protocol,casttype=Protocol"`
   Port       int32              `json:"port" protobuf:"varint,3,opt,name=port"`
   TargetPort intstr.IntOrString `json:"targetPort,omitempty" protobuf:"bytes,4,opt,name=targetPort"`
   NodePort   int32              `json:"nodePort,omitempty" protobuf:"varint,5,opt,name=nodePort"`
}

type OwnService struct {
   Ports     []v1.ServicePort `json:"ports,omitempty" patchStrategy:"merge" patchMergeKey:"port" protobuf:"bytes,1,rep,name=ports"`
   ClusterIP string           `json:"clusterIP,omitempty" protobuf:"bytes,3,opt,name=clusterIP"`
}
OwnIngress
// ingress信息
type OwnIngress struct {
    Domains []string `json:"domain"`
}
OwnPVC
// pvc声明信息
type OwnPVC struct {
    Spec v1.PersistentVolumeClaimSpec` json:"spec"`
}

大部分都是利用原resource.Spec字段,这样在精简的同时,生成resource object的时候也可以比较方便。

Status

Unit.Status设计就比较天马行空了,例如我希望在状态里面看到与Service和Pod自动关联的Endpoints逻辑资源,因此把部分Endpoint的信息也加了进来。

UnitStatus
// UnitStatus defines the observed state of Unit
type UnitStatus struct {
   // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
   // Important: Run "make" to regenerate code after modifying this file
   Replicas       *int32      `json:"replicas,omitempty"`
   Selector string `json:"selector"`
   LastUpdateTime metav1.Time `json:"lastUpdateTime,omitempty"`

   BaseDeployment         appsv1.DeploymentStatus     `json:"deployment,omitempty"`
   BaseStatefulSet        appsv1.StatefulSetStatus    `json:"statefulSet,omitempty"`
   RelationResourceStatus UnitRelationResourceStatus `json:"relationResourceStatus,omitempty"`
}

type UnitRelationResourceStatus struct {
    Service  UnitRelationServiceStatus         `json:"service,omitempty"`
    Ingress  []v1beta1.IngressRule              `json:"ingress,omitempty"`
    Endpoint []UnitRelationEndpointStatus      `json:"endpoint,omitempty"`
    PVC      corev1.PersistentVolumeClaimStatus `json:"pvc,omitempty"`
}
UnitRelationServiceStatus
type UnitRelationServiceStatus struct {
   Type            v1.ServiceType      `json:"type,omitempty"`
   ClusterIP       string              `json:"clusterIP,omitempty"`
   Ports           []ServicePortStatus `json:"ports,omitempty"`
   SessionAffinity v1.ServiceAffinity  `json:"sessionAffinity,omitempty"`
}
UnitRelationEndpointStatus
type UnitRelationEndpointStatus struct {
   PodName  string `json:"podName"`
   PodIP    string `json:"podIP"`
   NodeName string `json:"nodeName"`
}

原ServiceStatus信息量太少,就用了一个自定义的status填充了一下,其余的资源类型的Status,如DeploymentStatus/StatefulSetStatus/PersistentVolumeClaimStatus 信息量已经足够了,偷下懒直接利用。

总结

Unit的实现目标确定后,设计和填充了它的结构体,同时为了保持结构体的精简,简化出了一层ownResource,同样也设计和填充ownResources的Spec和Status结构。基础已经铺垫好,下一篇开始设计和实现Unit Reconciler的控制逻辑,以管理Unit和ownResources之间的生命周期的联动。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值