說明:本博客中,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)
參考資料:
[1] https://blog.csdn.net/weixin_42663840/article/details/102558455
[2] 鄭東旭《Kubernetes源碼剖析》