说明:本博客中,metav1指k8s.io/apimachinery/pkg/apis/meta/v1包,runtime指k8s.io/apimachinery/pkg/runtime包,schema指k8s.io/apimachinery/pkg/runtime/schema包
k8s本质上是一个资源控制系统——注册、管理、调度资源并维护资源的状态
目前Kubernetes系统支持8种资源操作方法,分别是create、delete、deletecollection、get、list、patch、update、watch。
资源的完整表现形式为<group>/<version>/<resource>/<subresource>,如deployment就是apps/v1/deployments/status
-
Group
拥有组名的资源组:其表现形式为<group>/<version>/<resource>,HTTP路径表现形式为/apis/<group>/<version>/<resource>
没有组名的资源组:被称为Core Groups、Legacy Groups、GroupLess。其表现形式为<version>/<resource>,HTTP路径表现形式为/api/<version>/<resource>
metav1.APIGroup: type APIGroup struct{ Name string Version []GroupVersionForDiscovery PreferedVersion GroupVersionForDiscovery //首选版本 }
-
Version
每一个资源都属于一个或多个资源版本,资源所属的版本通过metav1.APIVersions结构描述,一个或多个资源版本通过Versions []string字符串数组进行存储。
每一个资源都至少有两个版本,分别是外部版本(External Version)和内部版本(InternalVersion)。
外部版本代码定义在pkg/apis/<group>/<version>/目录下,用于对外暴露给用户请求的接口所使用的资源对象。
Alpha:该软件可能包含错误。启用一个功能可能会导致bug。默认情况下,功能可能会被禁用。
Beta:该软件经过很好的测试。启用功能被认为是安全的。默认情况下功能是开启的。
新资源类型的加入k8s,会经历版本变迁v1alpha1-->v1alpha2-->....-->v1alphaN-->v1beta1-->v1beta2-->v1 。
内部版本代码定义在pkg/apis/<group>/目录下,不对外暴露,仅在apiserver内部使用。一般用于多资源版本的转换
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"` }
-
Resource
每个资源可使用metav1.APIResource结构进行描述,它描述资源的基本信息,例如资源名称(即Name字段)、资源所属的命名空间(即Namespaced字段)、资源种类(即Kind字段)、资源可操作的方法列表(即Verbs字段)。
metav1.APIResorce:
type APIResource struct{ Name string SingularName string //资源的单数名称,它必须由小写字母组成 Namespaced bool Group string Version string Kind string Verbs Verbs //资源可操作的方法列表 ShortNames []string //资源的简称 }
资源对象(Resource Object)由Group+Version+Resource组成并实例化。它是持久化的实体,表示整个集群的状态。
在pkg/apis/meta/v1/meta.go中为资源对象单体和列表的公共属性(meta)做了抽象,分别为metav1.Object和metav1.ListInterface:
type Object interface { GetNamespace() string SetNamespace(namespace string) GetName() string SetName(name string) GetGenerateName() string SetGenerateName(name string) GetUID() types.UID SetUID(uid types.UID) GetResourceVersion() string SetResourceVersion(version string) GetGeneration() int64 SetGeneration(generation int64) GetSelfLink() string SetSelfLink(selfLink string) GetCreationTimestamp() Time SetCreationTimestamp(timestamp Time) GetDeletionTimestamp() *Time SetDeletionTimestamp(timestamp *Time) GetDeletionGracePeriodSeconds() *int64 SetDeletionGracePeriodSeconds(*int64) GetLabels() map[string]string SetLabels(labels map[string]string) GetAnnotations() map[string]string SetAnnotations(annotations map[string]string) GetFinalizers() []string SetFinalizers(finalizers []string) GetOwnerReferences() []OwnerReference SetOwnerReferences([]OwnerReference) GetClusterName() string SetClusterName(clusterName string) GetManagedFields() []ManagedFieldsEntry SetManagedFields(managedFields []ManagedFieldsEntry) } type ListInterface interface { GetResourceVersion() string SetResourceVersion(version string) GetSelfLink() string SetSelfLink(selfLink string) GetContinue() string SetContinue(c string) GetRemainingItemCount() *int64 SetRemainingItemCount(c *int64) }
在pkg/apis/meta/v1/types.go中为这两个抽象做了实现,分别为metav1.ObjectMeta和metav.listMeta:
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"` SelfLink string `json:"selfLink,omitempty" protobuf:"bytes,4,opt,name=selfLink"` UID types.UID `json:"uid,omitempty" protobuf:"bytes,5,opt,name=uid,casttype=k8s.io/kubernetes/pkg/types.UID"` ResourceVersion string `json:"resourceVersion,omitempty" protobuf:"bytes,6,opt,name=resourceVersion"` Generation int64 `json:"generation,omitempty" protobuf:"varint,7,opt,name=generation"` CreationTimestamp Time `json:"creationTimestamp,omitempty" protobuf:"bytes,8,opt,name=creationTimestamp"` DeletionTimestamp *Time `json:"deletionTimestamp,omitempty" protobuf:"bytes,9,opt,name=deletionTimestamp"` DeletionGracePeriodSeconds *int64 `json:"deletionGracePeriodSeconds,omitempty" protobuf:"varint,10,opt,name=deletionGracePeriodSeconds"` Labels map[string]string `json:"labels,omitempty" protobuf:"bytes,11,rep,name=labels"` Annotations map[string]string `json:"annotations,omitempty" protobuf:"bytes,12,rep,name=annotations"` OwnerReferences []OwnerReference `json:"ownerReferences,omitempty" patchStrategy:"merge" patchMergeKey:"uid" protobuf:"bytes,13,rep,name=ownerReferences"` Finalizers []string `json:"finalizers,omitempty" patchStrategy:"merge" protobuf:"bytes,14,rep,name=finalizers"` ClusterName string `json:"clusterName,omitempty" protobuf:"bytes,15,opt,name=clusterName"` ManagedFields []ManagedFieldsEntry `json:"managedFields,omitempty" protobuf:"bytes,17,rep,name=managedFields"` } type ListMeta struct { SelfLink string `json:"selfLink,omitempty" protobuf:"bytes,1,opt,name=selfLink"` ResourceVersion string `json:"resourceVersion,omitempty" protobuf:"bytes,2,opt,name=resourceVersion"` Continue string `json:"continue,omitempty" protobuf:"bytes,3,opt,name=continue"` RemainingItemCount *int64 `json:"remainingItemCount,omitempty" protobuf:"bytes,4,opt,name=remainingItemCount"` }
metadata为资源提供元数据信息,其内嵌多个字段用于定义资源的元数据。
创建资源对象时,这些字段全部是可选字段。
-
Name和UID
所有对象都由一个名称(由小写字母、数字、-、和.组成)和一个uid(系统产生的唯一字符串)明确标识。
-
GenerateName
Name为空时,系统会自动为对象生成唯一的名字
-
Namespace
Kubernetes中支持将一个物理集群划分为多个虚拟集群(Namespace)
默认内置了4个Namespace:
default:所有未指定命名空间的资源对象都会被分配给该命名空间
kube-system:所有由Kubernetes系统创建的资源对象都会被分配给该命名空间
kube-public:此命名空间下的资源对象可以被所有人访问(包括未认证用户)
kube-node-lease:此命名空间下存放来自节点的心跳记录Lease对象(节点租约信息)。
-
SelfLink
代表此资源对象的URL,由系统自动生成
-
Generation
代表期待状态的数字,由系统自动生成
-
CreationTimestamp
创建此资源对象的时间戳
-
DeletionTimestamp和DeletionGracePeriodSeconds
DeletionTimestamp代表了某个时间,资源对象期待在这个时间被删除。
到达这个时间后,系统会等待DeletionGracePeriodSeconds,再删除此资源对象。
例如Pod的DeletionGracePeriodSeconds默认被设置为若干秒,从而通过kubelet的信号实现优雅删除。
-
Labels
label是一种具有标识型的key:value元数据
标签的名字往往包括了一个域名的前缀,用来描述打标签的系统和工具,域名前还可以增加版本的标识beta字符串。
标签主要用来筛选资源和组合资源,可以使用类似于SQL查询select,来根据Label查询相关的资源。
-
Annotations
annotations一般是系统或者工具用来存储资源的非标示性信息,可以用来扩展资源的spec/status的描述。
annotations一样可以拥有域名的前缀,标注中也可以包含版本信息。而且可以包括逗号这样无法出现在label中的特殊字符。
例如可以用来存储证书ID、nginx接入层的配置信息、上一次kubectl操作的资源的json等。
-
OwnerReference
一些Kubernetes对象是其它一些对象的owner,owner资源的控制器会创建对应的归属资源。归属资源对象是owner的dependent。
比如:replicaset控制器在操作中会创建Pod,被创建Pod的ownereference就指向了创建Pod的replicaset。
也可以通过手动设置ownerReference的值,来指定owner和dependent之间的关系。
ownereference使得用户可以方便地查找一个创建资源的对象,还可以用来实现级联删除下属资源的效果。
pod的owner可能有replicaset、statefulset、daemonset等。例如某pod的ownerreference如下所示:
ownerReferences:
- apiVersion: apps/v1
blockOwnerDeletion: true
controller: true
kind: DaemonSet
name: calico-node
uid: 5d839653-da10-11e9-8a65-6c92bfb7fb72
-
ResourceVersion
每个资源的事件中都带一个resourceVersion的标签,这个标签是递增的数字
当客户端并发处理同一个资源的事件时,它就可以对比resourceVersion来保证最终的状态和最新的事件所期望的状态保持一致。
-
Finalizers
存在 finalizers说明该资源是由某程序创建的,程序在finalizers里加了一个它的标识,这意味着这个资源被删除时需要由创建资源的程序来做删除前的清理,清理完了它需要将标识从该资源的finalizers中移除,然后才会最终彻底删除资源。
-
ClusterName
仅用来标识不同集群中的同Name、同NameSpace资源对象
-
ManagedFields
pkg/runtime/schema/interfaces.go中定义了schema.ObjectKind接口:
type ObjectKind interface { SetGroupVersionKind(kind GroupVersionKind) GroupVersionKind() GroupVersionKind } type GroupVersionKind struct { Group string Version string Kind string }
metav1.TypeMeta实现了此接口:
type TypeMeta struct { Kind string `json:"kind,omitempty" protobuf:"bytes,1,opt,name=kind"` APIVersion string `json:"apiVersion,omitempty" protobuf:"bytes,2,opt,name=apiVersion"` }
在k8s.io/api项目下,继承这些类定义了所有资源对象
例如在core/v1/types.go定义了Pod:
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"` } type PodList struct { metav1.TypeMeta `json:",inline"` metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` Items []Pod `json:"items" protobuf:"bytes,2,rep,name=items"` }
-
Spec
用于定义用户期望的状态。
不同的资源类型,其状态的意义各不相同,例如Pod资源最为核心的功能在于运行容器
-
Status
用于记录对象在系统上的当前状态。
仅对活动对象才有意义,它由kubernetes系统自行维护,用户不能手动定义。
开发者可以根据自己的需求通过CRD对Kubernetes资源进行扩展
自定义资源和原生内置的资源一样,都可以用kubectl来创建、查看,也享有RBAC、安全功能。
如图的CRD定义了一种名为Foo的自定义资源。
.spec.name.plural字段是一个昵称,当一些资源的名字比较长时,可以用该字段自定义昵称来简化它的长度
.spec.scope字段表明该CRD是否被命名空间管理。如ClusterRoleBinding是Cluster级别的,Pod则可以被创建到不同的命名空间里
.spec.validation是校验段:首先是一个openAPIV3Schema的定义,spec中则定义了有哪些资源,这里将replicas定义为一个1~10范围的integer的资源。
此处创建了一个名为example-foo的Foo,apiVersion就是刚才所定义的samplecontroller.k8s.io/v1alpha1,kind就是Foo;
此处的spec字段其实没有在CRD的Schema中定义,因此没有作用。
只定义CRD其实没有作用,自定义资源只会被apiserver简单地计入到etcd中。
如何依据这个CRD定义的资源做一些复杂的操作,是由Controller来实现的。用户需要开发自定义Controller来感知或者操作自定义资源的变化。
Kubernetes提供了一个用于代码生成的项目code-generator,它提供了以下工具为CRD生成代码:
deepcopy-gen:生成深度拷贝方法,为每个T类型生成 func (t* T) DeepCopy() *T 方法
client-gen:为资源生成标准的clientset
informer-gen:生成 informer,提供事件机制来响应资源的事件
lister-gen:生成 Lister**,**为 get 和 list 请求提供只读缓存层(通过 indexer 获取)
其它:例如 Conversion-gen负责产生内外部类型的转换函数、Defaulter-gen负责处理字段默认值......
大部分的生成器支持--input-dirs参数来读取一系列输入包,处理其中的每个类型,然后生成代码
k8s.io/apimachinery/pkg/runtime/interfaces.go中定义了runime.Object接口:
type Object interface { GetObjectKind() schema.ObjectKind //访问对象的类型域 DeepCopyObject() Object //深度复制对象 }
metav1.TypeMeta实现了GetObjectKind();每种资源对象都实现了DeepCopyObject(),该方法一般被定义在zz_generated.depcopy.go中
因此,所有资源对象(不管是内置还是自定义的)都可以转换成runime.Object通用资源对象。runime.Object相当于Kubernetes类型系统的基石。
转换示例:
pod := &corev1.Pod{ TypeMeta: metav1.TypeMeta{ Kind: "Pods", }, ObjectMeta : metav1.ObjectMeta{ Labels: map[string]string{"name":"foo"}, }, } obj := runtime.Object(pod) pod2, ok := obj.(*corev1.Pod)
可以使用反射机制判断两者类型相同:
reflect.DeepEqual(pod, pod2)