【k8s源码阅读】核心数据结构

k8s的本质是一个资源控制系统,即围绕着资源做注册、管理、调度等操作来维护资源状态。但是因为资源系统的复杂性,k8s把资源进行分组和版本化,产生了Group(资源组)、Version(资源版本)、Resource(资源)的数据结构。同时,为了描述资源种类,也引入了Kind,这和Resource是同一级别的。

资源的管理是层级递进的。资源组、资源版本、资源和子资源的完成形式是目录形式来进行定位的,即:

sql 复制代码
Group/Version/Resource/SubResource

比如说常用的Deployment资源,它的完整表现形式就是apps/v1/deployments/status

另外,资源对象也是一个常见的概念,这里指的是经过实例化之后的资源 ,它通过资源组+资源版本+资源种类(Group/Version, Kind) 进行限制。比如Deployment资源,在实例化之后,表现形式就是 apps/v1,Kind=Deployments

每一种资源都有一定数量的操作方法,资源操作方法会用于Etcd集群中对资源进行增删改查。常见的8种操作方法是create、delete、deletecollection、get、list、patch、update、watch

每一种资源都有一定的版本,至少有两个,一个是外部版本,一个是内部版本。外部版本用于对外暴露请求的接口所用的资源对象,内部版本只在api server内部使用。

资源从定义上也可以分为两个,一个是内置资源,一个是Custom Resources(用户自定义资源)。开发者可以通过CRD实现自定义资源,并且把资源添加到系统中使用。

一、资源大图

获取内置资源的主要方式有两种:

  • kubectl api-versions:列出当前支持的资源组和资源版本
  • kubectl api-resources:列出当前支持的Resource资源列表

下表列举了一些常用的资源类型,完整请看官方文档:

kubernetes.io/zh-cn/docs/...

二、资源元信息

k8s的核心数据结构放在 vendor/k8s.io/apimachinery/pkg/apis/meta/v1/types.go 里面,首先先看一下描述:

golang 复制代码
// Package v1 contains API types that are common to all versions.
// 包 v1 包含所有版本通用的 API 类型。
// The package contains two categories of types:
//   - external (serialized) types that lack their own version (e.g TypeMeta)
//   - internal (never-serialized) types that are needed by several different
//     api groups, and so live here, to avoid duplication and/or import loops
//     (e.g. LabelSelector).
//
// In the future, we will probably move these categories of objects into
// separate packages.
package v1

API版本控制

REST API 是 Kubernetes 的基础架构。组件之间的所有操作和通信,以及外部用户命令都是 API Server 处理的 REST API 调用。因此,Kubernetes 平台中的所有资源被视为 API 对象,并且在 API 中都有对应的定义项。

TypeMeta用来表示API请求和响应当中的元信息,包含了资源类型和资源版本。

当然,每一个资源都可能属于一个或者多个资源版本,资源所属的版本会通过APIVersions来描述,通过Versions []string字段来进行存储,它列举了资源所有支持的版本,并且暴露给clients。

我们也可以通过GroupVersion字段来描述资源组和版本,这个是一个字符串,被设置为Group/Version。当资源不存在资源组的时候,会被设置为/Version

另外,也可以用GroupVersionResource(GVR)字段来没有歧义地明确表示一个资源的资源组、版本号以及名称。

golang 复制代码
type GroupVersionResource struct {
    Group    string
    Version  string
    Resource string
}

vendor/k8s.io/apimachinery/pkg/runtime/schema/ 中定义了常用的资源数据结构

ObjectMeta

ObjectMeta 是所有持久化资源必须具有的元数据,其中包括用户必须创建的所有对象。

存放位置:vendor/k8s.io/apimachinery/pkg/apis/meta/v1/types.go:111

golang 复制代码
// ObjectMeta is metadata that all persisted resources must have, which includes all objects
// users must create.
type ObjectMeta struct {
    //name 在命名空间内必须是唯一的。创建资源时需要,尽管某些资源可能允许客户端请求自动地生成适当的名称。 名称主要用于创建幂等性和配置定义。无法更新。
    Name string 

    //generateName 是一个可选前缀,由服务器使用,仅在未提供 name 字段时生成唯一名称。 
    GenerateName string 

    //namespace 定义了一个值空间,其中每个名称必须唯一。空命名空间相当于 "default" 命名空间,但 "default" 是规范表示。
    Namespace string 

    // 可用于组织和分类(确定范围和选择)对象的字符串键和值的映射。 可以匹配 ReplicationController 和 Service 的选择算符。
    Labels map[string]string 

    // annotations 是一个非结构化的键值映射,存储在资源中,可以由外部工具设置以存储和检索任意元数据。 它们不可查询,在修改对象时应保留。
    Annotations map[string]string 

    //ResourceVersion是同名、同类型对象,同时间下唯一
    //因为同名对象在不同时间可能会更新、删除再添加
    //用于比较两个对象谁比较新的情况
    ResourceVersion string
    ........
}

参考文章:blog.csdn.net/qq_24433609...

三、Resource

在整个体系架构中,资源是k8s最重要的概念。一个资源被实例化之后会表达为一个资源对象。在k8s系统中定义了许多资源对象,所有的资源对象都是Entity。可以通过api-server来进行查询和更新每一个资源对象。

  • 持久性实体(Persistent Entity):在资源对象被创建后,Kubernetes会持久确保该资源对象存在。大部分资源对象属于持久性实体,例如deployment资源对象。
  • 短暂性实体(Ephemeral Entity):也可称其为非持久性实体(Non-Peprsistent Entity)。在资源对象被创建后,如果出现故障或调度失败,不会重新创建该资源对象,例如Pod资源对象。

存放位置:vendor/k8s.io/apimachinery/pkg/apis/meta/v1/types.go

在k8s中,每个资源都可以用metav1.APIResource来进行描述,它描述了资源的基本信息:

  • Name:资源名称
  • Namespaced:bool值,是否被分配到了某个ns中
  • Group:group 是资源的首选组。 空表示包含资源列表的组。
  • Version:version 是资源的首选版本。
  • Kind:资源种类
  • Verbs:[]string类型,定义资源的可操作方法

资源定义

k8s的资源定义代码在pkg/apis的目录下面,同一个资源对应着内部版本和外部版本。

资源的内部版本定义了所支持的资源类型(types.go)、资源验证方法(validation.go)、资源注册至资源注册表的方法(install/install.go)等。而资源的外部版本定义了资源的转换方法(conversion.go)、资源的默认值(defaults.go)等。

以Pod资源为例,它的版本定义在pkg/apis/core/下面。

内部版本的资源代码结构说明如下

  • doc.go:GoDoc文件,定义了当前包的注释信息。在Kubernetes资源包中,它还担当了代码生成器的全局Tags描述文件。
  • register.go:定义了资源组、资源版本及资源的注册信息。
  • types.go:定义了在当前资源组、资源版本下所支持的资源类型。
  • v1、vlbeta1、v1beta2:定义了资源组下拥有的资源版本的资源(即外部版本)。
  • install:把当前资源组下的所有资源注册到资源注册表中。
  • validation:定义了资源的验证方法。
  • zz_generated.deepcopy.go:定义了资源的深复制操作,该文件P由代码生成器自动生成。

每一个资源目录都会通过registry定义所属的资源组和资源版本,内部版本资源对象通过runtime.APIVersionInternal标识。

go 复制代码
// GroupName is the group name use in this package
const GroupName = ""

// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal}

在每一个资源目录下面,都通过type代码文件定义当前的资源组/资源版本下面支持的资源类型。

以Pod资源为例,它的外部资源定义在pkg/apis/core/v1/下面,项目结构如下:

  • conversion.go:定义了资源的转换函数(默认转换函数),并将默认转换函数注册到资源注册表中。
  • zz_generated.conversion.go:定义了资源的转换函数(自动生成的转换函数),并将生成的转换函数注册到资源注册表中。该文件由代码生成器自动生成。
  • defaults.go:定义了资源的默认值函数,并将默认值函数注册到资源注册表中。
  • zz_generated.defaults.go:定义了资源的默认值函数(自动生成的默认值函数),并将生成的默认值函数注册到资源注册表中。该文件由代码生成器自动生成。

外部版本和内部版本资源类型是相通的,外部版本会通过资源版本来进行标识

go 复制代码
// GroupName is the group name use in this package
const GroupName = ""

// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1"}

// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
    return SchemeGroupVersion.WithResource(resource).GroupResource()
}

在这里我们也可以看到Pod的GroupName = "" ,这个是因为k8s支持有组名的资源组和没有组名的资源组。没有组名的资源组,被称为CoreGroups(即核心资源组)或LegacyGroups,也可被称为GroupLess(即无组)。其表现形式为/<version>/<ressource>,例如/v1/pods

资源注册

注册指的是把资源注册到资源注册表中。在每一个资源组目录下面,都拥有一个install/install.go代码文件,负责注册资源信息到注册表 (scheme)

github.com/kubernetes/...

go 复制代码
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函数注册资源组的版本顺序,如有多个资源版本,排在最前面的为资源首选版本。

目前,k8s所有资源类型都已经注册到scheme资源注册表中,这是一个内存型的注册表:

  • 支持注册多种资源类型,包括内部版本和外部版本。
  • 支持多种版本转换机制。
  • 支持不同资源的序列化/反序列化机制。

scheme支持两种资源类型的注册,分别是有版本资源和无版本资源。其中,无版本资源的资源对象并不需要进行转换(但是这个类型已经被弱化了)

在Scheme资源注册表中,UnversionedType资源类型的为对象通过scheme.AddUnversionedTypes方法进行注册,KnownType资源类型的对象通过scheme.AddKnownTypes方法进行注册。

在运行过程中,kube-apiserver组件常对Scheme资源注册表进行行查询,它提供了如下方法。

  • scheme.KnownTypes:查询注册表中指定GV下的资源类型。
  • scheme.AllKnownTypes:查询注册表中所有GVK下的资源类型。
  • scheme.ObjectKinds:查询资源对象所对应的GVK,一个资源对象可能存在多个GVK。
  • scheme.New:查询GVK所对应的资源对象。
  • scheme.IsGroupRegistered:判断指定的资源组是否已经注册。
  • scheme.IsVersionRegistered:判断指定的GV是否已经注册。
  • scheme.Recognizes:判断指定的GVK是否已经注册。
  • scheme.IsUnversioned:判断指定的资源对象是否属于UnversionedType类型。

scheme在基于 k8s 做二次开发的项目中还是一个非常常用的组件❗️

首选版本

metav1.APIResource引入了一个首选版本的概念,现在做一下解答。一个资源组可能会有多个版本,比如我们可以看到apps资源组下面就有三个版本。那当我们使用apps资源组下的某个资源,比如Deployment的时候,在一些场景下不指定资源版本,就使用首选版本。

版本的定义我们可以在install里面看到,首选版本的存在也说明scheme.SetVersionPriority的版本注册顺序是很重要的。

github.com/kubernetes/...

go 复制代码
func init() {
    Install(legacyscheme.Scheme)
}

// Install registers the API group and adds types to a scheme
func Install(scheme *runtime.Scheme) {
    utilruntime.Must(apps.AddToScheme(scheme))
    utilruntime.Must(v1beta1.AddToScheme(scheme))
    utilruntime.Must(v1beta2.AddToScheme(scheme))
    utilruntime.Must(v1.AddToScheme(scheme))
    utilruntime.Must(scheme.SetVersionPriority(v1.SchemeGroupVersion, v1beta2.SchemeGroupVersion, v1beta1.SchemeGroupVersion))
}

再通过注册表scheme.PreferredVersionAllGroups函数获取所有资源组下面的首选版本的时候,会把位于最前面的资源版本作为首选版本

go 复制代码
func (s *Scheme) PreferredVersionAllGroups() []schema.GroupVersion {
    ret := []schema.GroupVersion{}
    for group, versions := range s.versionPriority {
       for _, version := range versions {
          ret = append(ret, schema.GroupVersion{Group: group, Version: version})
          break
       }
    }
    ....
  }

资源操作方法

通过metav1.Verbs可以实现对每一个资源支持的操作方法来进行定义。比如对于Pod资源对象,可以通过kubectl命令行工具对其执行creatte、delete、get等操作。Kubernetes系统所支持的操作方法目前有8种操作,分别是create、delete、deletecollection、get、list、patch、update、watch。这些操作方法可分为四大类,分别属于增、删、改、查,对资源进行创建、删除、更新和子查询。

如何了解一个资源对象拥有哪些可操作的方法呢?需要查看与存储相关联的源码包registry,其定义在vendor/k8s.io/apiserver/pkg/registry/目录下。每种操作方法对应一个操作方法接口。

以get、create操作方法为例,rest.Getter接口定义了Get方法,rest.Creater接口定义了New和Create方法。如果某个资源对象在存储(Storage)上实现了Get、New及Create方法,就可以认为该资源对象同时拥有了get和create操作方法。

vendor/k8s.io/apiserver/pkg/registry/rest/rest.go

go 复制代码
// Getter is an object that can retrieve a named RESTful resource.
type Getter interface {
    // Get finds a resource in the storage by name and returns it.
    // Although it can return an arbitrary error value, IsNotFound(err) is true for the
    // returned error value err when the specified resource is not found.
    Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error)
}

// Creater is an object that can create an instance of a RESTful object.
type Creater interface {
    // New returns an empty object that can be used with Create after request data has been put into it.
    // This object must be a pointer type for use with Codec.DecodeInto([]byte, runtime.Object)
    New() runtime.Object

    // Create creates a new version of a resource.
    Create(ctx context.Context, obj runtime.Object, createValidation ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error)
}

以Pod资源对象为例,Pod资源对象的存储(Storage)实现了以上接口的方法,Pod资源对象继承了genericregistry.Store,该对象可以管理存储(Sttorage)的增、删、改、查操作,代码示例如下:

github.com/kubernetes/...

golang 复制代码
// PodStorage includes storage for pods and all sub resources
type PodStorage struct {
    Pod                 *REST
    Binding             *BindingREST
    LegacyBinding       *LegacyBindingREST
    Eviction            *EvictionREST
    Status              *StatusREST
    EphemeralContainers *EphemeralContainersREST
    Log                 *podrest.LogREST
    Proxy               *podrest.ProxyREST
    Exec                *podrest.ExecREST
    Attach              *podrest.AttachREST
    PortForward         *podrest.PortForwardREST
}

// Create ensures a pod is bound to a specific host.
func (r *LegacyBindingREST) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (out runtime.Object, err error) {
    metadata, err := meta.Accessor(obj)
    if err != nil {
       return nil, errors.NewBadRequest(fmt.Sprintf("not a Binding object: %T", obj))
    }
    return r.bindingRest.Create(ctx, metadata.GetName(), obj, createValidation, options)
}

// REST implements a RESTStorage for pods
type REST struct {
    *genericregistry.Store
    proxyTransport http.RoundTripper
}

在这里我们还需要明确一个概念,资源对象的操作方法与存储相关联,增删改查实际上都是针对于存储相关的操作。所以具体去看资源支持的操作类型,可以重点关注pkg/registry

以pod/logs子资源对象为例,该资源对象只实现了get操作方法,代码示例如下:

github.com/kubernetes/...

golang 复制代码
// LogREST implements the log endpoint for a Pod
type LogREST struct {
    KubeletConn client.ConnectionInfoGetter
    Store       *genericregistry.Store
}

// Get retrieves a runtime.Object that will stream the contents of the pod log
func (r *LogREST) Get(ctx context.Context, name string, opts runtime.Object) (runtime.Object, error) {
    ...
}

命名空间

每个命名空间相当于一个虚拟集群了,不同命名空间之间可以进行隔离,当然也可以通过某种方式跨命名空间通信。将Kubernetes系统划分为3个环境,分别是pro生产环境、test测试环境及dev开发环境,它们之间相互隔离,admin管理员用户对3个环境竟都拥有权限,而dev作为开发者只对dev开发环境拥有权限。

Kubernetes系统中默认内置了4个命名空间

  • default:所有未指定命名空间的资源对象都会被分配给该命名空间。
  • kube-system:所有由Kubernetes系统创建的资源对象都会被分配纪给该命名空间。
  • kube-public:此命名空间下的资源对象可以被所有人访问(包括未认证用户)。
  • kube-node-lease:此命名空间下存放来自节点的心跳记录(节点租约信息)。

描述某个资源对象属于某个命名空间,就要用到之前说到的metav1.ObjectMeta,比如pod资源

github.com/kubernetes/...

golang 复制代码
// Pod is a collection of containers, used as either input (create, update) or as output (list, get).
type Pod struct {
    metav1.TypeMeta
    // +optional 其中有ns信息
    metav1.ObjectMeta

    // Spec defines the behavior of a pod.
    // +optional
    Spec PodSpec

    // Status represents the current information about a pod. This data may not be up
    // to date.
    // +optional
    Status PodStatus
}

查看哪些k8s资源对象属于命名空间可以用以下命令

ini 复制代码
kubectl api-resources --namespaced=true|false

自定义资源&资源描述

Custom Resource支持把自定义资源加入到系统里面。目前使用的是CRD的方式。

一个资源对象需要用5个字段来描述它,分别是Group/Version、Kind、MetaData、Spec、Status。这些字段定义在YAML或JSON文件中。Kubeernetes系统中的所有的资源对象都可以采用YAML或JSON格式的描述文件来定义,下面是某个Pod文件的资源对象描述文件。

yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: frontend
spec:
  containers:
  - name: app
    image: images.my-company.example/app:v4
    resources:
      requests:
        memory: "64Mi"
        cpu: "250m"
      limits:
        memory: "128Mi"
        cpu: "500m"
  - name: log-aggregator
    image: images.my-company.example/log-aggregator:v6
    resources:
      requests:
        memory: "64Mi"
        cpu: "250m"
      limits:
        memory: "128Mi"
        cpu: "500m"
  • apiVersion:指定创建资源对象的资源组和资源版本,其表现现形式为<group>/<version>,若是core资源组(即核心资源组)下的资油原对象,其表现形式为<version>

  • kind:指定创建资源对象的种类。

  • metadata:描述创建资源对象的元数据信息,例如名称、命名空间等。(对应ObjectMeta

  • spec:对应PodSpec,包含有关Deployment资源对象的核心信息,告诉Kuberneetes期望的资源状态、副本数量、环境变量、卷等信息。

    • 地址:vendor/k8s.io/api/core/v1/types.go:3516
  • status:对应PodStatus,包含有关正在运行的Deployment资源对象的信息。

    • 地址:vendor/k8s.io/api/core/v1/types.go:4451

四、运行时

参考资料:blog.csdn.net/qq_24433609...

以资源对象Pod为例,该资源对象可以转换成runtime.Object通用资源对象,也可以从runtime.Object通用资源对象转换成Pod资源对象。runtime.Object结构如下:

go 复制代码
type Object interface {
    GetObjectKind() schema.ObjectKind
    DeepCopyObject() Object
}

需要注意的是,一般DeepCopyObject由代码生成,在生成文件当中。

所以,Kubernetes的任意资源对象都可以通过runtime.Object存储它的类型并允许深复制操作。通过runtime.Object Example代码示例,可以将资源对象转换成通用资源对象并再次转换回资源对象。runtime.Object Example代码示例如下:

go 复制代码
pod := &corev1.Pod{
    TypeMeta :.......
}
obj := runtime.Object(pod)
pod2, ok := obj.(*core.Pod)
if !ok {
    panic()
}
相关推荐
Johny_Zhao8 小时前
Docker + CentOS 部署 Zookeeper 集群 + Kubernetes Operator 自动化运维方案
linux·网络安全·docker·信息安全·zookeeper·kubernetes·云计算·系统运维
木鱼时刻2 天前
容器与 Kubernetes 基本概念与架构
容器·架构·kubernetes
chuanauc2 天前
Kubernets K8s 学习
java·学习·kubernetes
庸子3 天前
基于Jenkins和Kubernetes构建DevOps自动化运维管理平台
运维·kubernetes·jenkins
李白你好3 天前
高级运维!Kubernetes(K8S)常用命令的整理集合
运维·容器·kubernetes
Connie14513 天前
k8s多集群管理中的联邦和舰队如何理解?
云原生·容器·kubernetes
伤不起bb3 天前
Kubernetes 服务发布基础
云原生·容器·kubernetes
别骂我h3 天前
Kubernetes服务发布基础
云原生·容器·kubernetes
weixin_399380693 天前
k8s一键部署tongweb企业版7049m6(by why+lqw)
java·linux·运维·服务器·云原生·容器·kubernetes
斯普信专业组4 天前
K8s环境下基于Nginx WebDAV与TLS/SSL的文件上传下载部署指南
nginx·kubernetes·ssl