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资源列表
下表列举了一些常用的资源类型,完整请看官方文档:
二、资源元信息
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)
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
的版本注册顺序是很重要的。
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)的增、删、改、查操作,代码示例如下:
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操作方法,代码示例如下:
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资源
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()
}