前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >图解K8s源码 - k8s核心数据结构

图解K8s源码 - k8s核心数据结构

作者头像
才浅Coding攻略
发布2022-12-12 18:17:54
8200
发布2022-12-12 18:17:54
举报
文章被收录于专栏:才浅coding攻略才浅coding攻略

阿巩

期待同大家一起学习和交流~

在上一章中阿巩和大家分享了k8s组件之一kube-apiserver,在我自己阅读代码时发现k8s整体结构复杂,而且由于参与的开发者众多代码结构不免有些混乱,我往往容易陷入到某个细节而无法从整体视角梳理流程。在查阅官网文档及相关书籍后,我决定换个思路,先理解k8s核心数据结构设计,这样能够在阅读源码时做到事半功倍。好的,日拱一卒,我们开始吧!

K8s系统虽然功能众多且复杂,但它本质上是一个资源控制系统,即资源是k8s最重要的概念,它包括注册、管理、调度资源并维护资源状态。k8s将资源细分为三种数据结构,分别是:Group(资源组)、Version(资源版本)、Resource(资源)。Kind(资源种类)与Resource同级,用来描述资源的种类。

k8s系统支持多个 Group,每个 Group 支持多个 Version,每个 Version 支持多个 Resource,其中部分资源同时会拥有自己的子资源 SubResource。一个资源对象的表现形式为:

<group>/<version>, Kind=<kind>

例如 apps/v1,Kind=Deployment。

对每个资源都可以进行一系列操作即 Verbs,Verbs 对 Etcd 集群存储中的资源对象做增、删、改、查等操作。k8s资源分为两种:Kubernetes Resource 内置资源和Custom Resource 自定义资源,自定义资源可以通过CRD实现。

每个资源至少有两个版本,External Version 外部版本用于对外暴露给用户请求的接口所使用的资源对象。Internal Version 内部版本不对外暴露,仅在 Kubernetes API Server 内部使用。

资源列表:ResourceList

代码语言:javascript
复制
type APIResourceList struct {
 TypeMeta `json:",inline"`
 // groupVersion is the group and version this APIResourceList is for.
 GroupVersion string `json:"groupVersion" protobuf:"bytes,1,opt,name=groupVersion"`
 // resources contains the name of the resources and if they are namespaced.
 APIResources []APIResource `json:"resources" protobuf:"bytes,2,rep,name=resources"`
}

我们可以通过APIResourceList数据结构描述所有Group、Version、Resource的结构,以最常用的Pod、Service、Deployment资源为例,APIResourceList Example代码示例如下:

pkg/controller/namespace/deletion/namespaced_resources_deleter_test.go 367

代码语言:javascript
复制
func testResources() []*metav1.APIResourceList {
    results := []*metav1.APIResourceList{
      {
          GroupVersion: "v1",
          APIResources: []metav1.APIResource{
            {
                Name:       "pods",
                Namespaced: true,
                Kind:       "Pod",
                Verbs:      []string{"get", "list", "delete", "deletecollection", "create", "update"},
            },
            {
                Name:       "services",
                Namespaced: true,
                Kind:       "Service",
                Verbs:      []string{"get", "list", "delete", "deletecollection", "create", "update"},
            },
          },
        },
        {
          GroupVersion: "apps/v1",
          APIResources: []metav1.APIResource{
            {
                Name:       "deployments",
                Namespaced: true,
                Kind:       "Deployment",
                Verbs:      []string{"get", "list", "delete", "deletecollection", "create", "update"},
            },
          },
        },
    }
    return results
}

每个资源都可以使用metav1.APIResource结构进行描述,它描述资源的基本信息包括:资源名称(Name)、资源所属命名空间(NameSpace)、资源种类(Kind)、资源可操作方法列表(Verbs)。每个资源都属于一个或多个版本,通过metav1.APIVersion结构描述,并使用Versions []string字符串数组进行存储。

示例中的GroupVersion是一个字符串,在资源同时资源版本和资源组时,被设置为<group>/<version>;资源不存在资源组时,被设置为/<version>。

对于一个资源,用来明确标识它的资源组名称、资源版本和资源名称的结构称为GVR,即GroupVersionResource结构体,代码路径在:

vendor/k8s.io/apimachinery/pkg/runtime/schema/group_version.go 96

代码语言:javascript
复制
type GroupVersionResource struct {
    Group    string
    Version  string
    Resource string
}

//以deployment为例:
type GroupVersionResource{
    Group:"apps",
    Version:"v1",
    Resource:"Deployment"
}

Group、Version、Resource核心数据结构详情如下图:

资源组Group

代码语言:javascript
复制
type APIGroup struct {
    TypeMeta `json:",inline"`
    // name is the name of the group.
    Name string `json:"name" protobuf:"bytes,1,opt,name=name"`
    // versions are the versions supported in this group.
    Versions []GroupVersionForDiscovery `json:"versions" protobuf:"bytes,2,rep,name=versions"`
    // 首选版本
    PreferredVersion GroupVersionForDiscovery `json:"preferredVersion,omitempty" protobuf:"bytes,3,opt,name=preferredVersion"`
    ServerAddressByClientCIDRs []ServerAddressByClientCIDR `json:"serverAddressByClientCIDRs,omitempty" protobuf:"bytes,4,rep,name=serverAddressByClientCIDRs"`
  • 将资源按照功能划分成不同的资源组,并允许单独启用/禁用资源组或者资源组中的资源。
  • 为方便版本迭代升级,资源组中支持拥有不同资源版本。
  • 支持同名的资源种类(kind)存在于不同资源组内。
  • 资源组与资源版本通过Kubernetes API Server对外暴露,允许开发者通过HTTP协议进行交互并通过动态客户端(即DynamicClient)进行资源发现。
  • 支持CRD自定义资源扩展。

资源版本Version

代码语言:javascript
复制
type APIVersions struct {
    TypeMeta `json:",inline"`
    Versions []string `json:"versions" protobuf:"bytes,1,rep,name=versions"`
    ServerAddressByClientCIDRs []ServerAddressByClientCIDR `json:"serverAddressByClientCIDRs" protobuf:"bytes,2,rep,name=serverAddressByClientCIDRs"`
}

Kubernetes的资源版本控制可分为3种,分别是Alpha、Beta、Stable,它们之间的迭代顺序为Alpha→Beta→Stable,其通常用来表示软件测试过程中的3个阶段。

资源Resource

一个资源被实例化后会表达为一个资源对象(Resource Object),所有资源对象都是实体(Entity),k8s使用这些Entity来表示当前状态。通过kube-apiserver进行查询和更新每一个资源对象。k8s支持两种Entity:

  • 持久性实体(Persistent Entity):资源对象被创建后,Kubernetes会持久确保该资源对象存在。大部分资源对象属于持久性实体,例如Deployment资源对象。
  • 短暂性实体(Ephemeral Entity): 资源对象被创建后,如果出现故障或调度失败,不会重新创建该资源对象,例如Pod资源对象。
代码语言:javascript
复制
type APIResource struct {
    // 资源名称
    Name string `json:"name" protobuf:"bytes,1,opt,name=name"`
    // 资源的单数名称,例如pods的单数是pod
    SingularName string `json:"singularName" protobuf:"bytes,6,opt,name=singularName"`
    // 是否有命名空间
    Namespaced bool `json:"namespaced" protobuf:"varint,2,opt,name=namespaced"`
    // 所在资源组名称
    Group string `json:"group,omitempty" protobuf:"bytes,8,opt,name=group"`
    // 所在资源版本
    Version string `json:"version,omitempty" protobuf:"bytes,9,opt,name=version"`
    // 资源种类
    Kind string `json:"kind" protobuf:"bytes,3,opt,name=kind"`
    // 资源操作方法
    Verbs Verbs `json:"verbs" protobuf:"bytes,4,opt,name=verbs"`
    // 资源简称
    ShortNames []string `json:"shortNames,omitempty" protobuf:"bytes,5,rep,name=shortNames"`
    // 该资源所属的分组资源列表
    Categories []string `json:"categories,omitempty" protobuf:"bytes,7,rep,name=categories"`
    StorageVersionHash string `json:"storageVersionHash,omitempty" protobuf:"bytes,10,opt,name=storageVersionHash"`
}

资源版本(如v1beta1、v1等)与外部版本/内部版本概念不同。外部版本用于对外暴露给用户请求的接口所使用的资源对象,例如,用户在通过YAML或JSON格式的描述文件创建资源对象时,所使用的是外部版本的资源对象;内部版本用于多资源版本的转换,例如将v1beta1版本转换为v1版本。

即拥有资源版本的资源属于外部版本,拥有runtime.APIVersionInternal标识的资源属于内部版本。以资源pod为例:

  • 外部版本 (由于外部版本的资源需要对外暴露给用户请求的接口,所以有版本号,有json tags和proto tags):
代码语言:javascript
复制
type Pod struct {
    metav1.TypeMeta `json:",inline"`
    metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
    Spec PodSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`
    Status PodStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
}
  • 内部版本
代码语言:javascript
复制
type Pod struct {
    metav1.TypeMeta
    metav1.ObjectMeta
    Spec PodSpec
    Status PodStatus
}

通过register.go代码文件定义所属的资源组和资源版本时两者的区别:

代码语言:javascript
复制
// 内部版本
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal}
// 外部版本
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1"}

在每个k8s资源组目录中都有install/install.go代码文件,它负责将资源信息注册到资源注册表(Scheme)中,以core核心资源组为例:

代码语言:javascript
复制
func init() {
   Install(legacyscheme.Scheme)
}

// Install registers the API group and adds types to a scheme
func Install(scheme *runtime.Scheme) {
    utilruntime.Must(core.AddToScheme(scheme))
    utilruntime.Must(v1.AddToScheme(scheme))
    utilruntime.Must(scheme.SetVersionPriority(v1.SchemeGroupVersion))
}
  • legacyscheme.Scheme是kube-apiserver组件的全局资源注册表,Kubernetes的所有资源信息都交给资源注册表统一管理。
  • core.AddToScheme函数注册core资源组内部版本的资源。
  • v1.AddToScheme函数注册core资源组外部版本的资源。
  • scheme.SetVersionPriority函数注册资源组的版本顺序,如有多个资源版本,排在最前面的为资源首选版本。

资源操作方法及接口说明

代码语言:javascript
复制
kubectl delete pods --all
kubectl delete pod -l $key=$value

通用资源类型runtime.Object

runtime.Object为Interface接口类型,作为资源对象的通用资源对象,这里我们可以把资源对象理解为Go中的Struct类型,它们都实现了runtime.Object中提供的两个方法,分别是GetObjectKind和DeepCopyObject。

  • GetObjectKind:用于设置并返回GroupVersionKind。
  • DeepCopyObject:用于深拷贝当前资源对象并返回。(深拷贝:另外开辟内存空间,不与原对象共享内容,可在不修改原对象基础上修改对象属性)

Scheme资源注册表

我们都听说过Windows上的注册表,k8s中的资源注册表和它类似,只不过注册的是资源类型,包括内部和外部版本。

Scheme资源注册表通过Go语言的map结构实现映射关系,这些映射关系可以实现高效的正向和反向检索,从Scheme资源注册表中检索某个GVK的Type,它的时间复杂度为O(1)。

Scheme资源注册表数据结构主要由4个map结构组成:

  • gvkToType: 存储GVK与Type的映射关系。
  • typeToGVK: 存储Type与GVK的映射关系,一个Type会对应一个或多个GVK。
  • unversionedTypes: 存储UnversionedType与GVK的映射关系。
  • unversionedKinds: 存储Kind名称与UnversionedType的映射关系。

(UnversionedType早期Kubernetes系统中的概念,目前几乎所有的资源对象都拥有版本,即KnownType。但在metav1元数据中还有部分类型如:metav1.Status、 metav1.APIVersions、metav1.APIGroupList、metav1.APIGroup、metav1.APIResourceList)

对于不同的资源类型注册方式也不同,如 AddKnownTypes 注册有版本资源;AddKnownTypeWithName 带着名字注册有版本资源;AddUnversionedTypes 注册无版本资源。以AddKnownTypes,在注册资源类型时,无须指定Kind名称,而是通过reflect机制获取资源类型的名称作为资源种类名称,代码示例如下:

代码语言:javascript
复制
func (s *Scheme) AddKnownTypes(gv schema.GroupVersion, types ...Object) {
    s.addObservedVersion(gv)
    for _, obj := range types {
        t := reflect.TypeOf(obj)
        if t.Kind() != reflect.Ptr {
            panic("All types must be pointers to structs.")
        }
        t = t.Elem()
        s.AddKnownTypeWithName(gv.WithKind(t.Name()), obj)
    }
}

对于查询注册表可以使用kube-apiserver组件下提供的”scheme.资源类型“的方法查询。

文章最后附上k8s Project Layout结构图:

参考:

《kubernetes源码剖析》

https://blog.51cto.com/daixuan/4976182

END

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-11-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 才浅coding攻略 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档