欢迎访问我的GitHub
这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos
本篇概览
- 本文是《Kubernetes对象深入学习》系列的第三篇,主要内容是关于对象属性的知识点,关于对象属性,先通过一个具体实例来建立第一印象,在kubernetes环境执行命令kubectl get pod kube-apiserver-hedy -n kube-system -o yaml,可以看到指定pod的基本信息(注意,pod名请根据您自己环境的实际情况调整),内容如下图
- 上图中,黄色箭头2和3之间的大片区域都是对象属性,对应在kubernetes源码中,接口是metav1.Object,实现是metav1.ObjectMeta
- 来看pod的数据结构源码,内嵌了metav1.ObjectMeta,其他资源也是同样的套路
// 1. 代码生成器来生成runtime.Object接口的实现
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object// Pod is a collection of containers, used as either input (create, update) or as output (list, get).
type Pod struct {// 2. 这样就实现了schema.ObjectKind接口metav1.TypeMeta// 3. 这样就实现了metav1.Object接口// +optionalmetav1.ObjectMeta// Spec defines the behavior of a pod.// +optionalSpec PodSpec// Status represents the current information about a pod. This data may not be up// to date.// +optionalStatus PodStatus
}
- 以上是从使用的角度了解属性,接下来深入属性相关的源码学习
接口metav1.Object
- 先看接口metav1.Object的源码,可见前面咱们看到的annonations,labels等等信息,在metav1.Object中都有Get方法获取,也有Set方法来设置
type Object interface {GetNamespace() stringSetNamespace(namespace string)GetName() stringSetName(name string)GetGenerateName() stringSetGenerateName(name string)GetUID() types.UIDSetUID(uid types.UID)GetResourceVersion() stringSetResourceVersion(version string)GetGeneration() int64SetGeneration(generation int64)GetSelfLink() stringSetSelfLink(selfLink string)GetCreationTimestamp() TimeSetCreationTimestamp(timestamp Time)GetDeletionTimestamp() *TimeSetDeletionTimestamp(timestamp *Time)GetDeletionGracePeriodSeconds() *int64SetDeletionGracePeriodSeconds(*int64)GetLabels() map[string]stringSetLabels(labels map[string]string)GetAnnotations() map[string]stringSetAnnotations(annotations map[string]string)GetFinalizers() []stringSetFinalizers(finalizers []string)GetOwnerReferences() []OwnerReferenceSetOwnerReferences([]OwnerReference)GetClusterName() stringSetClusterName(clusterName string)GetManagedFields() []ManagedFieldsEntrySetManagedFields(managedFields []ManagedFieldsEntry)
}
接口的实现metav1.ObjectMeta
- 前面看到接口定义是一堆Get和Set方法,这里再来了解这些Get和Set方法在实现中返回了哪些内容,又设置了哪些内容
- 先看结构体定义,与前面截图中的pod的meta信息是能对应上的
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"`
}
- 那么接口实现的方法,其内容就不言而喻了:对结构体中相关字段的Get和Set,具体代码如下图所示,唯一要注意的是GetObjectMeta方法返回的是结构体自己
- 现在源码已经了解,接下来要看使用场景
使用场景
-
对象属性是非常重要的,在官方资料中明确规定,MetaData中的一些字段是所有资源类型必须要有的,如下图所示
-
如下图,在kubernetes源码中搜索各种常见资源的定义,ObjectMeta是必不可少的(上一篇学到的TypeMeta也同样一定会有)
-
再打开client-go库的源码,看看client-go如何使用ObjectMeta,通过单元测试可以看到官方的标准用法,如下图,创建对象的操作在单元测试中随处可见,一样离不开ObjectMeta
-
看到这里,咱们把对象属性的源码和使用场景都看过了,是不是本章可以结束了?不就是一堆get和set方法嘛,以后想读写哪个字段,调用该字段对应的get和set方法就行了呗
-
我的建议是:您先别离开,还有个重要内容即将呈现,那是官方的馈赠,那是很实用的工具
实用工具meta.Accessor
- 试想一个场景:开发一个函数,此函数不关注资源对象的具体类型(例如可能是pod,也可能是deployment),只想获取这个对象的一些meta信息,例如namespace、label等,这个函数如何实现呢?
- 看过前面的内容后,其实聪明的您应该能想到:ObjectMeta是嵌入到各个资源数据结构中的,所以这些资源对象都算是实现了meta1.Object接口了,只要能把对象转换成meta1.Object接口,上述函数就能做出来了
- 和资源关系密切client-go库自然也会遇到上述场景,所以库中已经封装好了这个函数,源码如下所示
func Accessor(obj interface{}) (metav1.Object, error) {switch t := obj.(type) {case metav1.Object:return t, nilcase metav1.ObjectMetaAccessor:if m := t.GetObjectMeta(); m != nil {return m, nil}return nil, errNotObjectdefault:return nil, errNotObject}
}
- 这个meta.Accessor方法很实用,来看client-go是怎么使用的,如下图,在从本地缓存中取得资源列表时,无需关注资源类型,也能得到对象的namespace、labels等字段的信息,因此这个ListAllByNamespace方法就更加通用了,各种资源都能用这个方法来获取
- 下面这个MetaNamespaceIndexFunc方法在client-go库中是被高频使用的,源码如下,很简单,任何资源类型都能用这个方法得到其namespace,也是借用了meta.Accessor来无视资源类型
// MetaNamespaceIndexFunc is a default index function that indexes based on an object's namespace
func MetaNamespaceIndexFunc(obj interface{}) ([]string, error) {meta, err := meta.Accessor(obj)if err != nil {return []string{""}, fmt.Errorf("object has no meta: %v", err)}return []string{meta.GetNamespace()}, nil
}
Unstructured的使用场景,也会用到meta.Accessor方法
- meta.Accessor方法还有一处比较典型使用,就是利用Unstructured对象创建对象,先来看看什么是Unstructured
- Unstructured是个map,在创建Deployment、Pod等对象的时候,除了使用Deployment、Pod等特定的数据结构,还可以直接用Unstructured对象作为创建的参数,这样写出的代码更有通用性,以下是个代码片段,用来创建Deployment对象,可见通过map就能完成Deployment资源的创建,那个Create方法的入参就是Unstructured
deploymentRes := schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"}deployment := &unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "apps/v1","kind": "Deployment","metadata": map[string]interface{}{"name": "demo-deployment",},"spec": map[string]interface{}{"replicas": 2,"selector": map[string]interface{}{"matchLabels": map[string]interface{}{"app": "demo",},},"template": map[string]interface{}{"metadata": map[string]interface{}{"labels": map[string]interface{}{"app": "demo",},},"spec": map[string]interface{}{"containers": []map[string]interface{}{{"name": "web","image": "nginx:1.12","ports": []map[string]interface{}{{"name": "http","protocol": "TCP","containerPort": 80,},},},},},},},},}// Create Deploymentfmt.Println("Creating deployment...")result, err := client.Resource(deploymentRes).Namespace(apiv1.NamespaceDefault).Create(context.TODO(), deployment, metav1.CreateOptions{})
- 进入Create方法内部看看,如下图所示,这个Create方法可以用来创建多种资源类型,但使用了meta.Accessor,无需知道资源类型也能得到资源名称
- 至此,对象属性的学习就完成了,相信您对metav1.Object和metav1.ObjectMeta都有了深入的理解,也会有动手写代码试试的冲动
- 实战一直是欣宸原创的招牌,这里也不会缺席,碍于篇幅限制本篇就只聊理论,下一篇,咱们实战走起,写代码体验对象属性的操作
你不孤单,欣宸原创一路相伴
- Java系列
- Spring系列
- Docker系列
- kubernetes系列
- 数据库+中间件系列
- DevOps系列