当前位置:网站首页>Kubernetes源码分析(二)----资源Resource
Kubernetes源码分析(二)----资源Resource
2022-07-06 19:15:00 【而鱼儿and-fish】
一、简介
· 资源作为Kubernetes的核心名词,可以说在Kubernetes的体系中,将所有的实例都当作的资源;
· 理解Kubernetes的数据结构可以对理解Kubernetes起到启发性的帮助
二、Group、Version、Resource
· 在整个Kubernetes的体系架构中,资源是Kubernetes最重要的概念,所有的生态系统都是在围绕着资源运作,
Kubernetes在本质上就是一个资源控制系统(注册、管理、调度资源并维护资源的状态);
· 在庞大而复杂的Kubernetes系统中,只有单一的"资源"概念是不够的,
就像学校中校长直接管理所有学生是不现实的,会将学生分成不同的年级,一个年级分成不同的班,而在信息中心中会记录每个班中所有学生的信息和班级的基本信息(比如 人数、平均成绩等等),在年级层面会记录每个年级会记录有多少班、一共多少人等等,
这样学校在安排活动的时候就可以直接以班级或年级为单位快速的检索信息,层级制度的管理极大的提高了管理的效率;
Kubernetes也一样,将资源分组和版本化,形成Group(资源组)、Version(资源版本)、Resource(资源):
1. Group: 被称为资源组,在API Server中也可以被称为APIGroup;
2. Version: 被称为资源版本,在API Server中也可以被称为APIVersions;
3. Resource: 被称为资源,在API Server中也可以被称为APIResource;
4. Kind: 被称为资源组种类,描述Resource的种类、与Resource;
Kubernetes支持多个Group,每个Group支持多个Version,每个Version支持多个Resource,其中部分Resource同时会拥有多个子资源(SubResource),例如Deployment资源引用Status资源;
以常用的Depolyment资源为例,其完整的表现形式为 apps/v1/depolyment/status;
· 资源对象也是一个常用的概念,例如Depolyment资源实例化后拥有资源组、资源版本、资源种类;
表现为 aaps/v1,Kind=Depolyment;
· 每个资源都有一定数量资源操作方法(Verbs),资源操作方法用于Etcd集群存储中对资源对象的增删改查;
目前Kubernetes支持8中资源操作方法:create、delete、update、get、deletecollection、list、patch、watch;
· 每个资源都至少有两个版本(各自前的Version是两个概念) :外部版本(External Version) 和内部版本(Internal Version);
外部版本用于对外暴露给用户请求的接口所使用的资源对象;
内部版本用于在API Server内部中使用;
注意:这里的版本和Version是两个概念;
· Resource也可以分为两种:Kubernetes Resource(内置资源) 和 Custom Resource(自定义资源);
开发者通过CRD(Custom Resource Definitions) 可以实现自定义资源,它允许用户将自己定义的资源添加到Kubernetes系统中;
并像内置资源一样使用它们 (Ukwon,大部分使用的时候起始都是Custom Resource)
三、ResourceList
· Group、Version、Resource等核心的数据结构存放在k8s.io/aoimachinery/pkg/apis/meta/v1(准确一点,在types.go中)中;
其中包含了Kubernetes集群中所有组件使用的通用核心数据结构;
其中可以使用APIResource数据机构描述所有Group、Version、Resource的结构,以最常见的Pod、Service、Deployment资源为例;
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
}
Kubernetes的每个资源可使用meta1.APIResource结构进行描述,它描述资源的基本信息,
例如资源名称(Name)、资源所属的命名空间(Namespaced)、资源种类(Kind)、资源可操作的方法列表(Verbs)...;
· 每一个资源都属于一个或多个Version,资源版本通过metav1.APIVersions结构描述,一个或多个资源版本通过名为Versions的string数组进行存储;
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"`
}
在ResourceList的示例代码中,通过了GroupVersion字段来描述了资源组和资源版本;
当资源组同时存在Group和Version是,表示为GroupVersion: "<GroupName>/<VersionNname>";
当资源不存在Group时,表示为GroupVersion: "<VersionName>"
所以上面代码中,pods和services资源属于v1版本,而deployments资源属于apps Group下面的v1版本;
· 另外还可以通过Group、Version、Resource结构来明确标识一个资源的GVR名称;
在K8S中被封装为一个数据结构以便于快速调用(k8s.io/aoimachinery/pkg/runtime/schema中,具体在group_version.go文件中);
type GroupVersionResource struct {
Group string `json:"group" protobuf:"bytes,1,opt,name=group"`
Version string `json:"version" protobuf:"bytes,2,opt,name=version"`
Resource string `json:"resource" protobuf:"bytes,3,opt,name=resource"`
}
以Deployment资源为例,资源描述为:
schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"}
除此之外,还有GroupVersionKind、GroupVersion等其他的数据结构(也非常容易懂);
· APIVersions、APIGroup、APIResource的描述和APIResouceList一起都放在metav1中;
四、Group
· Group在API Server中也可以被称为APIGroup;
Kubernetes中定义很多Group,这些Group按照不同的功能将资源进行了划分;
· Group的特点为:
1.将众多resource按照功能划分成不同的group,并允许单独启用/禁用group;
当然也支持单独启用/禁用group中的resource;
2.支持group中的resource拥有不同的version;
这样方便了group内的resource依据version来进行迭代升级;
3.支持同名Kind存在于不同的group中;
4.group与version通过API Server对外暴露,允许开发者通过HTTP协议进行交互并通过动态客户端进行资源发现;
5.支持CRD自定义资源扩展;
6. 用户交互简单,例如在使用kubectl时可以不填写group的名称;
· 具体在代码中的表现为:(k8s.io/aoimachinery/pkg/apis/meta/v1)
type APIGroup struct {
TypeMeta `json:",inline"`
Name string `json:"name" protobuf:"bytes,1,opt,name=name"`
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"`
}
Name为Group名称;
Versions为资源组下所支持的资源版本;
PreferredVersion为首选版本,当一个group中存在多个Version时,API Server会在使用资源时选择PreferredVersion作为当前使用的版本;
· Kubernetes支持两种Group:
一种是由Name的Group: 如 apps/v1/deployment;
另一种是没有Name的Group: 被称为Core Groups(核心Group) 或者Legacy Groups,也可以被称为GroupLess(无组);
如 /v1/pods,表示为核心Group下的v1版本的pods的;
两种group的表现形式不同,所以形成的HTTP PATH路径也不同;
一般情况下Legacy Groups的前缀为api,有Name的Group的前缀为apis;
如 http://localhost:8080/api/v1/pods,和 http://localhost:8080/apis/apps/v1/deployments;
五、Version
· Kubernetes的资源版本控制类似于语义版本控制,在该基础上的资源版本定义允许版本好以v开头,例如v1beta1;
每当发布新的resource都需要对其设置版本号,这是为了在兼容旧版本的同时不断升级新版本,这有助于用户了解当前使用的resource是什么阶段,以及实现当前程序的迭代;
语义版本控制应用得非常广泛,也是目前开源界常用的一种版本控制规范;
· APIVersions在代码中表现为:
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"`
}
Versions为所支持的资源版本列表
· Kubernetes的资源版本控制可以封为三种,分别为Alpha、Beta、Stable,迭代顺序为从前往后;
Alpha用于表示通过了内部测试;
Beta表示已经修复了大部分不完善部分,仍然可能存在漏洞,需要由特定人群来测试;
Stable表示基本形成了产品并达到了一定的成熟度,可以稳定运行;
· Alpha:
Alpha为内部测试版本,用于Kubernetes开发者内部测试,该版本是不稳定的;
默认情况下Alpha的功能是被禁用的;Version名称一般为v1alpha1、v1alpha2、v2alpha1...
· Beta:
Beta为相对稳定的版本,Beta版本结果官方和社区多次测试,当功能迭代时,该版本会有较小的改变,但是不会删除;
默认情况下,Beta版本的功能是开启的;Version名称一般为v1beta1、v1beta2、v2beta1...
· Stable:
Stable为正式发布版本,Stable版本基本形成了产品,该版本不会被删除;
默认情况下,处于Stable版本的功能全部处于开启;Version名称一般为v1、v2、v3....
六、Resource
· 在整个Kubernetes体系架构中,资源是Lubernetes最重要的概念,可以说Kubernetes的生态系统都围绕着资源运作;
Kubernetes系统虽然有着相当复杂和众多的功能,但是它的本质就是一个资源控制系统(管理、调度资源并维护资源的状态);
· 一个Resource被实例化后会表达为一个资源对象(Resource Object);
在Kubernetes系统中定义并运行着各种各样的资源对象;
所有的对象都是Entity(实体),Kubernetes用Entity来表示当前的状态;
通过API Server进行查询和更新每一个资源对象;
Kubernetes支持两种Entity:
1. 持久性实体(Persistent Entity):
在资源对象被创建后,Kubernetes会吃就确保该资源对象的存在;
大部分资源对象属于持久性;
2. 短暂性实体(Ephemeral Entity):
也可称为Non-Persistent Entity非持久性实体;
在资源对象被创建后,如果出现故障或调度失败;
不会重新创建该资源的对象;
比如 Pod资源对象;
· Resource的代码如下:
type APIResource struct {
Name string `json:"name" protobuf:"bytes,1,opt,name=name"`
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"`
}
Name:Resource的名称
SingularName:资源的单数名称,必须由小写字母组成,默认使用资源种类(Kind)的小写形式命名;
Namespaced:资源是否拥有所属的命名空间;
Group:资源所属的Group;
Version:资源所在的资源版本;
Kind:资源的种类;
Verbs:资源的可操作方法列表;如 get、list、delete、create、update等;
ShortNames:资源的简称;如 Pod简称po;
· 资源的外部版本和内部版本
· Kubernetes资源拥有外部版本(External Version) 和内部版本(Internal Version);
在Kubernetes系统中,同一资源对应着两个版本,分别是外部版本和内部版本;
例如 Deployment资源,它所属的外部版本表现形式为apps/v1,内部版本表现为apps/_internal;
· External Object:
外部版本资源对象也称Versioned Object(拥有资源版本的资源对象);
外部版本用于对外暴露给用户请求的接口所使用的资源对象;
例如 用户使用yaml或者Json文件创建资源对象时,所使用的是外部版本的资源对象;
外部版本的资源对象通过资源版本(Alpha、Beta、Stable)进行标识;
· Internal Object:
内部版本资源对象不会对外暴露,仅在API Server内部使用;
内部版本用于多资源版本的转换,例如将v1beta1版本转换为v1版本,会向从v1beta1转换为Internal再转换为v1;
内部版本资源对象通过runtime.APIVersionInternal(即 _internal) 进行标识;
· 在Kubernetes源码中,外部版本的资源类型定义在k8s.io/api/<group>/<version>/<resource file>中;
例如Pod被定义在k8s.io/api/core/v1(具体在types.go文件中)
· 资源定义外部版本与内部版本的代码定义也不一样,
外部版本的资源需要对外暴露给用户请求的接口,所以资源代码定义了JSON Tags和Proto Tags;
内部版本的资源不对外暴露,所以没有如何的Json Tags和Proto Tags;
· 外部版本的Pod定义:位于staging/src/k8s.io/api/core/v1/types.go
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"`
}
内部版本的Pod定义:位于pkg/apis/core/types.go
type Pod struct {
metav1.TypeMeta
metav1.ObjectMeta
Spec PodSpec
Status PodStatus
}
· 资源代码定义
· Kubernetes资源代码被定义在pkg/apis下;
同一资源对应着内部版本和外部版本,内部版本和外部版本的资源代码结构并不相同;
· 资源的内部版本定义了所支持的
资源类型(types.go)、资源验证方法(validation.go)、
资源注册至资源注册表的方法(install/install.go)、资源对默认值(defaults.go)等;
· 以Deployment为例,它的内部版本定义在/pkg/apis/apps/下,其资源代码结构为:
1. doc.go:GoDoc文件,定义了当前包的注释信息;
在Kubernetes资源包中,他还担任了代码生成器的全局Tags描述文件
2. register.go:定义了Group、Version、Resource的注册信息;
3. types.go:定义了在当前资源组、资源版本下所支持的资源类型;
4. v1、v1beta1、v1bata2:定义了资源组下拥有的资源版本的资源
(外部版本在 staging/src/k8s.io/api/apps/ 下面);
5. install:把当前资源组下的所有资源都注册到资源注册表中;
6. validation:定义了资源的验证方法;
7. zz_generated.deepcopy.go:定义了资源对深复制操作,该文件由代码生成器自动生成;
· 每一个Kubernetes资源目录,都通过register.go代码文件定义所属的Group和Version;
内部版本资源对象通过runtime.APIVersionInternal(_internal)标识;
const GroupName = "apps"
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal}
这样就定义了一个Group == "apps",Version == runtime.APIVersionInternal的资源;
· 每一个Kubernetes资源目录,都通过types.go代码文件定义当前Group/Version下所支持的资源类型;
如pkg/apis/apps/types.go中定义的类型:
type DaemonSet struct{...}
type StatefulSetCondition struct {...}
type Deployment struct {...}
...
· 以Deployment为例,它的外部版本定义在staging/src/k8s.io/api/apps/下面的v1、v1beta1、v1beta2中;
1. constversion.go:定义了资源的默认转换函数,并将默认的转换函数注册到资源注册表中;
2. zz_generated.constants.go:定义了资源自动生成的转换函数,并将生成的转换函数注册到资源注册表中;
3. defaults.go:定义了资源的默认值函数,并将默认值函数注册到注册表中
4. zz_generated.defaults.go:定义了资源自动生成的默认值函数,并将默认值函数注册到注册表中;
除此之外还有register.go和doc.go这两个的与内部版本资源代码结构中相似;
· 外部和内部版本资源类型相同,都通过register.go代码文件定义所属的资源和资源版本;
和内部版本不同,外部版本使用Alpha、Beta、Stable标识:
const GroupName = "apiserver.config.k8s.io"
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1"}
· 将资源注册到资源注册表中
· 每一个Kubernetes资源组目录中,都有一个install/install.go文件,它负责将资源信息注册到资源注册表(Scheme) 中;
· 以core核心资源组为例:
func init() {
Install(legacyscheme.Scheme)
}
func Install(scheme *runtime.Scheme) {
utilruntime.Must(core.AddToScheme(scheme))
utilruntime.Must(v1.AddToScheme(scheme))
utilruntime.Must(scheme.SetVersionPriority(v1.SchemeGroupVersion))
}
legacyscheme.Scheme是API Server组件的全局资源注册表,
Kubernetes的所有资源信息都交给资源注册表同一管理;
core.AddToScheme函数注册core资源组内部版本的资源;
v1.AddToScheme函数注册core资源组外部版本的资源;
schema.SetVersionPriority函数注册资源组的版本顺序,如果由多个Version,排在最前面的是资源首选版本;
· 资源首选版本
· 首选版本(Preferred Version) 也称为优选版本(Priority Version);
一个Group下面拥有多个Version,它们全部存放在Group的Versions字段中,在一些场景中,如果不指定版本,会使用该资源的优先版本;
· 例如apps Group,注册资源是会注册多个资源版本,分别为v1、v1beta1、v1beta2:
utilruntime.Must(scheme.SetVersionPriority(v1.SchemeGroupVersion, v1beta2.SchemeGroupVersion, v1beta1.SchemeGroupVersion))
scheme.SetVersionPriority中参数的顺序很重要,在注册表Scheme的versionPrioity结构为一个map[string][]staging:
VersionPriority: "apps" [{"v1"},{"v1beta1"},{"v1beta2"}];
· 通过资源注册表schema.preferredVersion函数获取所有Group下的首选版本时,会将位于最前面的Version作为首选的版本;
在staging\src\k8s.io\apimachinery\pkg\runtime\scheme.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
}
}
...
return ret
}
可以发现PreferredVersionAllGroups通过一个循环直接遍历了Scheme中的versionPriority;
注意:versionPriority中不会存储内部版本;
· 除了PreferredVersionAllGroups函数,还有另外两个函数可以获取首选版本:
1. func (s *Scheme) PrioritizedVersionsAllGroups() []schema.GroupVersion {}
获取所有Group的所有Version,Version按照顺序放回
2. func (s *Scheme) PrioritizedVersionsForGroup(group string) []schema.GroupVersion {}
获取指定Group的所有Version,Version按照顺序放回
· 资源操作方法
· 在Kubernetes系统中,针对每个资源都有一定的操作方法Verbs,
例如对于Pod资源对象,可以使用kubectl对其指向create、delete、get等操作;
kuberletes系统所支持的操作方法目前有8种create、delete、deletecollection、get、list、patch、update、watch;
这些操作可以分为四大类
增:create;
删:delete、deletecollection;
改:patch、update;
查:get、list、watch;
· 资源操作的方法在staging\src\k8s.io\apimachinery\pkg\apis\meta\v1\types.go中:
type Verbs []string
func (vs Verbs) String() string {
return fmt.Sprintf("%v", []string(vs))
}
· 不同的资源拥有不同的操作方法,记录在APIRsource的Verbs字段中;
例如针对Pod资源对象和Pod/logs子资源对象,
Pod资源对象拥有create、delete、deletecollection、get、list、patch、watch、update等操作方法;
Pod/logs只需要拥有get操作方法,因为logs只需要执行查看操作;
操作方法通过metav1.Verbs()进行描述:
verbs := metav1.Verbs([]string{"delete", "deletecollection", "get", "list", "patch", "create", "update", "watch"})
· 资源对象的操作方法与存储(Storage) 相关,增删改查实际上都是针对存储的操作;
而查看Verbs与之对应的接口位于 staging\src\k8s.io\apiserver\pkg\registry\rest\rest.go中:
create:资源对象创建接口,rest.Creater;
delete:资源对象删除接口(单个资源对象),rest.deleter;
deletecollection:资源对象删除接口(多个资源对象),rest.Collectiondeleter;
get:资源对象获取接口(单个资源对象),rest.Getter;
list:资源对象获取接口(多个资源对象),rest.Lister;
patch:资源对象更新接口(局部资源对象的更新),rest.Patcher;
watch:资源对象监控接口,rest.Watcher;
update:资源对象更新接口(完整资源对象的更新),rest,Updater;
· 以get、create操作方法为例,rest.Getter接口定义了Get方法,rest.Create接口定义了New和Create方法;
如果某个资源对象在存储(Storage) 上实现了Get、New及Create方法,就可以认为该资源对象同时拥有了get和create操作方法;
type Getter interface {
Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error)
}
type Creater interface {
New() runtime.Object
Create(ctx context.Context, obj runtime.Object, createValidation ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error)
}
· 以Pod资源为例,Pod资源对象的Storage实现了以上接口的方法,Pod资源对象疯转了REST,REST中封装了,该对象可以管理存储(Storage) 的增、删、改、查操作:
(pkg\registry\core\pod\storage\storage.go)
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
}
type REST struct {
*genericregistry.Store
proxyTransport http.RoundTripper
}
· genericregistry.Store的方法实现在k8s.io/apiserver/pkg/registry/generic/registr/stroe.go中:
func (e *Store) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
pod/logs的子对象的方法在pkg/registry/core/pod/rest/log.go中:
func (r *LogREST) Get(ctx context.Context, name string, opts runtime.Object) (runtime.Object, error) {
LogREST中实际上也封装了 *genericregistry.Store
· 资源的命名空间
· Kubernetes系统支持namespace命名空间,其用于解决Kubernetes集群中资源对象过多导致管理复杂的问题;
每个NameSpace相当于一个逻辑上的虚拟机群,不同的namespace之间进行逻辑上的隔离,只能使用某种方法跨namespace通信;
Namespace用于划分不同的环境,例如 生产环境、开发环境等使用不同的namespace进行划分;
· Kubernetes系统之间内置个4个namespce,分别为:
1. default:所有未指定namespace的资源对象都会被分配给该namespce,在不指定namespace时会默认使用这个;
2. kube-system:所有由kubernetes系统创建的资源对象都会被分配给该namespace;
3. kube-public:此命名空间下的资源对象可以被所有人访问(包括未认证用户);
4. kube-node-lease:用于存放来自节点的心跳学习(节点的租约信息);
资源对象的命名空间信息存在结构体的 metav1.ObjectMeta 中,ObjectMeta中的Namespace字段记录着对应资源对象的namespace;
type Deployment struct {
metav1.TypeMeta
metav1.ObjectMeta
Spec DeploymentSpec
Status DeploymentStatus
}
type ObjectMeta struct {
Name string `json:"name,omitempty" protobuf:"bytes,1,opt,name=name"`
GenerateName string `json:"generateName,omitempty" protobuf:"bytes,2,opt,name=generateName"`
Namespace string `json:"namespace,omitempty" protobuf:"bytes,3,opt,name=namespace"`
...
}
而在APIRsource中的Namespaced bool字段记录着resource是否有namespce;
· 自定义资源
· Kubernetes支持自定义资源展示其拥有强大的高扩展功能;
即 可以将自定义的资源添加到Kubernetes系统中;
· 自定义的资源可以像内置资源一样使用,在YAML/JSON文件中带有Spec的资源定义都是对Kubernetes中资源对象的定义;
所有的自定义资源都可以使用kubectl进行操作;
· 资源对象描述文件定义
· Kubernetes资源可分为内置资源(Kubernetes Resource) 和自定义资源(Custom Resource);
它们都通过资源对象描述文件进行定义(Manifest File);
· 一个资源对象需要五个字段来描述,分别为 Group/Version、Kind、MetaData、Spec、Status;
这些字段用YAML/JSON文件描述
· 根据资源对象的源码分析可以大致直到需要哪些参数:
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"`
}
其中拆解开:
TypeMeta:用于在API response或request中描述一个单独的都西昂,用字符串表示该对象的类型及其API模式版本;
所以在其中定义了 Kind string 字段和 APIVersion string 用于记录Resource的kind和Group/version;
ObjectMeta:用于记录持久化资源的元数据,会记录Name、Namespace、UID、ResourceVersion、Labels、OwnerReference等等元数据;
主要是描述创建资源对象的一些名称、Namespace等;
PodSpec:用于记录对Pod的描述,同理Deployment的Spec是DeploymentSpec;
告诉Kubernetes期望的资源状态、环境变量、挂载卷等
PodStatus:表示Pod的状态,Staus可以跟踪系统的实际状态,同理Deployment的Status是DeploymentStatus;
记录包含正在运行的资源的状态,将Status和Spec做对比就可以知道资源是否偏离期望状态;
· 其中Status由Kubernetes系统提供和更新,表示资源的Actual state实际状态;
任何时刻,Kubernetes一致尽力管理着对象与期望状态相匹配;
七、Kubernetes内置资源全图
· 常见资源种类及其对应的资源组:
· apps:
1. DaemonSet:在Pod资源对象的基础上提供守护进程的资源类型;
2. RepliceSet:在Pod资源对象的基础上提供一组Pod副本的资源类型;
3. Deployment:在Pod资源对象的基础上提供支持无状态服务的资源类型;
4. Statefulment:在Pod资源对象的基础上提供支持有状态服务的资源类型;
· batch:
1. Job:提供一次性任务的资源类型;
2. CronJob:提供定时任务的资源类型;
· core:
1. Event:提供Kubernetes集群事件管理的资源类型;
2. LimitRange:为命名空间的每种资源对象设置资源(硬件资源)的使用限制;
3. Namespace:提供资源对象所在命名空间的资源类型;
4. Node:提供Kubernetes集群中管理工作节点的资源类型,每个节点都有一个唯一标识;
5. PersistentVolume:提供PV存储的资源类型;
6. PersistentVolumeClaim:提供PVC存储的资源类型;
7. Pod:提供容器集合管理的资源类型;
8. RepilcationController:在Pod资源对象的基础上提供副本数的保持不变的资源类型;
9. Secret:提供存储密码、Token、密钥等敏感数据的资源类型;
10.Service:提供负载均衡器为Pod资源对象的代理服务的资源类型;
· event.k8s.io:
Event:提供Kubernetes集群事件管理的资源类型;
· networking.k8s.io:
1. RuntimeClass:提供容器运行时功能的资源状态;
2. Ingress:提供Kubernetes集群外部访问集群内部服务管理的资源类型;
· node.k8s.io:
RuntimeClass:提供容器运行时功能的资源状态;
· policy:
1. Evictions:在Pod资源对象的基础上提供驱逐策略的资源类型;
2. PodDisruptionBudget:提供限制同时Pod中断的数量,以保证集群的高可用性;
· scheduling.k8s.io:
PriorityClass:提供Pod资源对象优先级管理的资源类型;
· seething.k8s.io:
PodPeset:在创建Pod资源对象时,可以将特定信息注入Pod资源对象中;
八、runtime.Object类型基石
· Runtime也就是"运行时",一般是指在程序或语言核心库的实现代码(关键就完事了) ;
Kubernetes Runtime在 k8s.io\apimachinery\pkg\runtime 中;
他提供了一个通用的资源类型 runtime.Object interface;
· Object是Kubernetes的基石;
Kubernetes的所有资源对象,作为一个struct数据结构,都要实现这个接口;
Object定义了两个方法:
type Object interface {
GetObjectKind() schema.ObjectKind
DeepCopyObject() Object
}
而schema.ObjectKind也是一个接口,所有从Scheme encode序列化的对象都对它们的类型信息进行编码;
type ObjectKind interface {
SetGroupVersionKind(kind GroupVersionKind)
GroupVersionKind() GroupVersionKind
}
GetObjectKind:用于设置并返回一个 GroupVersionKind;
DeepCopyObject:用于深复制当前资源对象并返回;
deepcopy相当于将数据结构克隆一份,因此他不会与原始对象共享任何内容,只是数据相同;
它可以让代码在不修改原对象的情况下,可以改变克隆对象的任何属性;
· 而只要资源对象同时拥有 GetObjectKind() schema.ObjectKind 方法和 DeepCopyObject() Object 方法就能作为Object使用;
Kubernetes的每一个资源对象都嵌入了metav1.TypeMeta类型,而在metav1.TypeMeta实现了 GetObjectKind();
而 DeepCopyObject() Object 被定义在了每个资源对象的 zz_generated.deepcopy.go中;
所以Kubernetes中所有的资源对象都支持使用Object的方法存储它的类型并允许深复制操作;
九、Unstructured数据
· 数据可以分为结构化数据(structured data) 和非结构化数据(unstructured data);
Kubernetes内部会经常处理这两种数据:
1. 结构化数据:
预先知道数据结构的数据类型就是结构化数据,例如JSON数据;
要使用这种数据,需要创建一个struct,通过Go的JSON库进行Unmarshall反序列化操作,将JSON结构中的数据按照字段名映射到struct中;
2. 非结构化数据:
无法预知数据结构的数据类型 或 数据名称不准确的数类型 就是结构化数据,
因为无法知道数据结构,所以无法通过事先预定struct来序列化或者反序列化数据;
因为无法知道数据类型,所以要将数据转换为想要的数据类型,就需要使用类型断言;
3. Kubernetes非结构化数据处理:
type Unstructured interface {
Object
NewEmptyInstance() Unstructured
UnstructuredContent() map[string]interface{}
SetUnstructuredContent(map[string]interface{})
IsList() bool
EachListItem(func(Object) error) error
}
Kubernetes通过UnstructuredContent() 用一个map[string]interface{}来接收数据;
然后在后续再判断interface{}类型的数据;
十、Scheme资源注册表
· 在使用window系统的时候,当操作系统安装某个要用程序的时候,该程序的一些信息会注册到注册表中;
当操作系统想要卸载该程序的时候,会从注册表中删除相关信息;需要使用程序时,会从注册表中获取相关信息;
而Kubernetes Scheme资源注册表类似于wwindows上的注册表,这不过注册的是资源类型;
· Kubernetes系统拥有众多资源,每一种资源就是一个资源类型,这些资源类型需要有同一的注册、存储、查询、管理等机制;
Kubernetes的资源注册表是一个内存型的资源注册表,拥有如下特性:
1. 支持注册多种资源类型,包括内部版本和外部版本;
2. 支持多种版本的转换机制;
3. 支持不同资源的序列化/反序列化机制;
Scheme支持两种资源类型的注册:UnversionedType 和KnownType 资源类型:
1. UnversionedType:无版本资源类型,主要应用于某些没有Version的资源类型,这种类型的资源对象不需要转换;
虽然大多数的资源对象都拥有版本,但是在metav1元数据中还有部分类型,它们既属于meta.k8s.io/v1又属于UnversionedType无版本资源类型;
例如 metav1.Status、metav1.APIVersions、metav1.APIGroup、metav1.ResourceList等
2. KnownType:是当前kubernetes最常用的资源类型,称为“拥有版本的资源类型”
在Scheme资源注册表中,
UnversionedType资源类型的对象通过Scheme.AddUnversionedTypes()来注册;
KnownType通过Scheme.AddKnownTypes()注册;
· Scheme资源注册表数据结构
· Scheme资源注册表的数据结构主要由gvkToType、typeToGVK、unversionedTypes、unversionedKinds四个部分组成;
代码位于staging\src\k8s.io\apimachinery\pkg\runtime\scheme.go:
type Scheme struct {
gvkToType map[schema.GroupVersionKind]reflect.Type
typeToGVK map[reflect.Type][]schema.GroupVersionKind
unversionedTypes map[reflect.Type]schema.GroupVersionKind
unversionedKinds map[string]reflect.Type
fieldLabelConversionFuncs map[schema.GroupVersionKind]FieldLabelConversionFunc
defaulterFuncs map[reflect.Type]func(interface{})
converter *conversion.Converter
versionPriority map[string][]string
observedVersions []schema.GroupVersion
schemeName string
}
gvkToType:存储GVK与Type的映射关系;
typeToGVK:存储Type与GVK的映射关系,一个Type会对应多个GVK,所以用切片保存;
unversionedTypes:存储UnversionedType与GVK的映射关系
unversionedKinds:存储Kind名称与UnversionedType的映射关系
...
· Scheme资源注册表通过Go中的map结构事先映射,这些映射关系可以事先高效的正向和反向的检索,
从Scheme资源注册表中去直接检索某个GVK对应的Type,它的事件复杂度为O(1);
· KnownType通过Scheme.AddKnownTypes()注册时,只会向gvkToType和typeToGVK中写入k-v对;
func (s *Scheme) AddKnownTypes(gv schema.GroupVersion, types ...Object) {
s.addObservedVersion(gv)
for _, obj := range types {
t := reflect.TypeOf(obj)
if t.Kind() != reflect.Pointer {
panic("All types must be pointers to structs.")
}
t = t.Elem()
s.AddKnownTypeWithName(gv.WithKind(t.Name()), obj)
}
}
其中addObservedVersion是用于向observedVersions中追加数据的;
AddKnownTypeWithName是向gvkToType和typeToGVK中写入k-v对的:
func (s *Scheme) AddKnownTypeWithName(gvk schema.GroupVersionKind, obj Object) {
...
s.gvkToType[gvk] = t
for _, existingGvk := range s.typeToGVK[t] {
if existingGvk == gvk {
return
}
}
s.typeToGVK[t] = append(s.typeToGVK[t], gvk)
...
}
而UnversionedType资源类型的对象通过Scheme.AddUnversionedTypes()来注册:
func (s *Scheme) AddUnversionedTypes(version schema.GroupVersion, types ...Object) {
s.addObservedVersion(version)
s.AddKnownTypes(version, types...)
for _, obj := range types {
t := reflect.TypeOf(obj).Elem()
gvk := version.WithKind(t.Name())
s.unversionedTypes[t] = gvk
if old, ok := s.unversionedKinds[gvk.Kind]; ok && t != old {
panic(fmt.Sprintf("%v.%v has already been registered as unversioned kind %q - kind name must be unique in scheme %q", old.PkgPath(), old.Name(), gvk, s.schemeName))
}
s.unversionedKinds[gvk.Kind] = t
}
}
可以发现AddUnversionedTypes()中调用了AddKnownTypes();
也就是说,UnversionedType会同时向四个map中注册k-v对;
· 资源注册表注册方法
在资源注册表中,不同的资源类型会使用不同的注册方法:
1. Scheme.AddKnownTypes():注册KnownType资源类型;
2. Scheme.AddUnversionedTypes():注册UnversionedType()资源类型;
3. Scheme.AddKnownTypeWithName():注册KnownType资源类型,需指定资源的Kind;
以Scheme.AddKnownTypes() 为例,在注册时不需要指定Kind名称,而是通过reflect机制获取资源类型的名称作为Kind名称;
然后在内部嵌入Scheme.AddKnownTypeWithName() ,将获取到的Kind传入方法中;
· 资源注册表查询方法
在运行过程中,API Server会对Scheme进行查询:
1. Scheme.KnownTypes:查询注册表中指定GV对应的的Type,返回 map[Kind]Type;
2. Scheme.AllKnownTypes:查询注册表中所有GVK下的资源类型,直接返回Scheme中的 gvkToType;
3. Scheme.ObjectKinds:查询资源对象对应的GVK,一个资源对象可能存在多个GVK,返回 []GVK;
4. Scheme.New:查询GVK所对应的资源对象,返回 runtime.Object;
5. Scheme.IsGroupRegistered:判断指定的GV是否已经存在;
6. Scheme.IsVersionRegistered:判断指定的GV是否已经存在,其实代码上和上一个一样;
7. Scheme.Recognizes:判断GVK是否被注册过,直接使用 s.gvkToType[gvk];
8. Scheme.IsUnversioned:判断指定的资源对象是否是UnversionedType类型;
十一、Codec编解码器
· 编解码器也就是解码器+编码器;
和Serializer序列化器的不同在于,Serializer包含序列化操作和反序列化操作;
序列化操作是将数据转换为字符串的过程;
反序列化时将字符串转化为数据的过程;通过序列化器可以轻松的维护数据结构并存储或传输数据;
而编解码器指的是,可以将数据在任何特定的格式之间相互转化的,所以,Serializer序列化器也可以认为时Codec编解码器的一种;
· Codec interface其实就是Serializer:
(staging\src\k8s.io\apimachinery\pkg\runtime\interfaces.go)
type Encoder interface {
Encode(obj Object, w io.Writer) error
Identifier() Identifier
}
type Decoder interface {
Decode(data []byte, defaults *schema.GroupVersionKind, into Object) (Object, *schema.GroupVersionKind, error)
}
type Serializer interface {
Encoder
Decoder
}
type Codec Serializer
可以发现Serializer属于Codec编解码器的一种;
换句话说,不管是yamlSerializer还是jsonSerializer以及其他的Serializer,只要实现了Encode和Decode就是Serializer;
· Code编解码器包含3种Serializer:yamlSerializer、jsonSerializer、protobufSerializer;
· Codec编解码器通过NewCodecFactory方法实例化的;
在 NewCodecFactory中会调用newSerializersForScheme方法将所有的Serializer实例化;
json和yamlSerializer都通过json.NewSerializerWithOptions实现;
protobufSerializer通过protobuf.NewSerializer;
十二、Converter资源版本转换器
· 之前提到了一个资源可能会拥有多种Version,kubernetes系统允许资源的版本转换;
例如Deployment资源对象,当前使用的Version是v1beta1,但是功能不如v1版本完善,可以将Deployment转换为v1版本;
· converter资源版本转换器主要用于解决多资源版本的问题,如果要在每个版本之间转换;
最简单的方法就是设置一个特殊的版本用于转换的中间层,那么只要支持每个Version都能和这个midVersion相互转换,那么就能实现每个版本之间转换;
这个midVerrsion,就是之前提到的内部版本_Internal;
· Converter转换器数据结构
Converter转换器数据结构主要存放转换函数;
(staging\src\k8s.io\apimachinery\pkg\conversion\converter.go)
type Converter struct {
conversionFuncs ConversionFuncs
generatedConversionFuncs ConversionFuncs
ignoredUntypedConversions map[typePair]struct{}
}
type ConversionFuncs struct {
untyped map[typePair]ConversionFunc
}
type typePair struct {
source reflect.Type
dest reflect.Type
}
type ConversionFunc func(a, b interface{}, scope Scope) error
type Scope interface {
Convert(src, dest interface{}) error
Meta() *Meta
}
conversionFuncs:默认的转换函数,这些转换函数被定义在资源目录的conversion.go中;
generatedConversionFuncs:是自动生成的转换函数,这些转换函数一般定义在资源目录的 zz_generated.conversion.go中,由代码生成器自动生成;
ignoredUntypedConversions:记录为无操作的转换,即不能从这个typePair的source转换到dest;
ConversionFunc:定义了转换函数的类型,a为source,b为dest,scope在Convert中定义了转换函数;
· Converter注册转换函数
Kubernetes支持五种注册转换函数:
1. Scheme.AddIgnoredConversionType:注册应该被忽略的转换操作;
2. Scheme.AddConversionFunc:注册单个conversionFunc;
3. Scheme.AddGeneratedConversionFunc:注册自动生成的conversionFunc;
4. Scheme.AddFieldLabelConversionFunc:注册字段标签的(FieldLabel) 的conversionFunc
5. addConversionFuncs:注册当前Group/Version下所有conversionFunc
· Converter资源版本转换原理
· Scheme资源注册表可以通过两种方式进行版本转换:
1. Scheme.ConvertToVersion:将传入的(in) 资源对象转换成目标(target) 资源版本,
在转换前,会将资源对象深复制再执行转换操作,相当于安全的内存对象转换操作;
2. Scheme.UnsafeConvertToVersion:与scheme.ConvertToVersion功能相同,但在转换的过程中不会深复制对象,
而是直接对原资源对象进行深复制,尽可能的实现快速转换,但是该操作是Unsafe的内存对象转换操作;
无论是哪一种方式,本质上都是封装了 (s *Scheme) convertToVersion(copy bool, in Object, target GroupVersioner) (Object, error);
只是在copy参数处不同而已;
· Converter转换器(convertToVersion) 的流程:
(s *Scheme) convertToVersion(copy bool, in Object, target GroupVersioner) (Object, error)
1. 获取传入的资源对象的反射类型:
资源版本转换的类型可以是runtime.Object 或者runtime.Unstructured,但是它们都是struct
通过Go语言的reflect机制获取该资源类型的反射类型,因为在Sheme中都是以反射的方式注册资源的;
通过 t = reflect.TypeOf(in).Elem() 直接获取in的Type;
2. 从资源注册表中查找传入的资源对象的GVK
kinds, ok := s.typeToGVK[t];
回顾一下,typeToGvk是一个 KEY为Type,Value为GVK切片的map;
if !ok || len(kinds) == 0 {
return nil, NewNotRegisteredErrForType(s.schemeName, t)
}
这一步同时也是在检验传入的资源对象是否注册过;
3. 从GVK切片中选出与目标资源对象匹配的GVK
gvk, ok := target.KindForGroupVersionKinds(kinds);
KindForGroupVersionKinds方法可以从多个GVK中找到与目标(target) GV最匹配的GVK;
会优先返回GV全匹配的GVK,再尝试返回G相同V不同的GVK(target的GV) ,最后返回flase;
4. 判断kind是否在kinds中
如果在,说明target在kinds中,说明target在typeToGVK中,说明在可转换列表中;
可以直接将in的GVK设置为targer GVK,不需要执行转换的擦欧总,缩短了耗时;
5. 判断in是否属于Unversioned类型
Unversioned类型不需要进行转换操作,而是直接将传入的资源对象的GVK设置为target GVK;
6. 执行转换操作
out, err := s.New(gvk)
if copy {
in = in.DeepCopyObject()
}
需要先New一个target GVK的新对象,然后再判断是否需要对in执行深复制,然后再通过 s.converter.Convert方法进行转换;
(c *Converter) Convert(src, dest interface{}, meta *Meta)
if err := s.converter.Convert(in, out, meta); err != nil {...}
a. 先将src和dest转化为typePair格式
b. 先通过ignoredUntypedConversions[pair]查找是否可以转换;
再依次从conversionFuncs和generatedConversionFuncs中查找pair对应的转换函数;
中途达到目标就执行并返回执行结果;
7. 设置转换后资源对象的GVK
setTargetKind(out,gvk)
setTargetKind会将内部版本设置为schema.GroupVersionKind{},表现出来也就是"/,Kind=",而不是_internal
边栏推荐
- 写作系列之contribution
- Qpushbutton- "function refinement"
- HAVE FUN | “飞船计划”活动最新进展
- 安德鲁斯—-多媒体编程
- 安全巡检的工作
- Work of safety inspection
- dotConnect for DB2数据提供者
- Station B's June ranking list - feigua data up main growth ranking list (BiliBili platform) is released!
- Apifox, is your API interface document rolled up like this?
- 3 -- Xintang nuc980 kernel supports JFFS2, JFFS2 file system production, kernel mount JFFS2, uboot network port settings, and uboot supports TFTP
猜你喜欢
Increase 900w+ playback in 1 month! Summarize 2 new trends of top flow qiafan in station B
基于ensp防火墙双击热备二层网络规划与设计
Cloud Mail .NET Edition
Summary of basic debugging steps of S120 driver
Huitong programming introductory course - 2A breakthrough
C#/VB. Net to delete watermarks in word documents
HAVE FUN | “飞船计划”活动最新进展
Google Earth Engine(GEE)——Landsat 全球土地调查 1975年数据集
How to design interface test cases? Teach you a few tips to draft easily
Overall query process of PostgreSQL
随机推荐
[software test] the most complete interview questions and answers. I'm familiar with the full text. If I don't win the offer, I'll lose
Code line breaking problem of untiy text box
【软件测试】最全面试问题和回答,全文背熟不拿下offer算我输
【2022国赛模拟】多边形——计算几何、二分答案、倍增
Statistics of radar data in nuscenes data set
MES管理系统的应用和好处有哪些
Gee upgrade can realize one piece of run tasks
代码调试core-踩内存
Redis入门完整教程:RDB持久化
widerperson数据集转化为YOLO格式
What management points should be paid attention to when implementing MES management system
一本揭秘字节万台节点ClickHouse背后技术实现的白皮书来了!
安全交付工程师
A new path for enterprise mid Platform Construction -- low code platform
QPushButton-》函数精解
Derivative, partial derivative, directional derivative
Niuke programming problem -- double pointer of 101 must be brushed
MySQL
Fundamentals of process management
C # / vb. Net supprime le filigrane d'un document word