欢迎访问我的GitHub

本篇概览

  • 本文是《Kubernetes对象深入学习》系列的第三篇,主要内容是关于对象属性的知识点,关于对象属性,先通过一个具体实例来建立第一印象,在kubernetes环境执行命令kubectl get pod kube-apiserver-hedy -n kube-system -o yaml,可以看到指定pod的基本信息(注意,pod名请根据您自己环境的实际情况调整),内容如下图
    Kubernetes对象深入学习之三:对象属性-LMLPHP
  • 上图中,黄色箭头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接口
	// +optional
	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
}

  • 以上是从使用的角度了解属性,接下来深入属性相关的源码学习

接口metav1.Object

  • 先看接口metav1.Object的源码,可见前面咱们看到的annonations,labels等等信息,在metav1.Object中都有Get方法获取,也有Set方法来设置
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)
}

接口的实现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方法返回的是结构体自己
    Kubernetes对象深入学习之三:对象属性-LMLPHP
  • 现在源码已经了解,接下来要看使用场景

使用场景

  • 对象属性是非常重要的,在官方资料中明确规定,MetaData中的一些字段是所有资源类型必须要有的,如下图所示
    Kubernetes对象深入学习之三:对象属性-LMLPHP

  • 如下图,在kubernetes源码中搜索各种常见资源的定义,ObjectMeta是必不可少的(上一篇学到的TypeMeta也同样一定会有)
    Kubernetes对象深入学习之三:对象属性-LMLPHP

  • 再打开client-go库的源码,看看client-go如何使用ObjectMeta,通过单元测试可以看到官方的标准用法,如下图,创建对象的操作在单元测试中随处可见,一样离不开ObjectMeta
    Kubernetes对象深入学习之三:对象属性-LMLPHP

  • 看到这里,咱们把对象属性的源码和使用场景都看过了,是不是本章可以结束了?不就是一堆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, nil
	case metav1.ObjectMetaAccessor:
		if m := t.GetObjectMeta(); m != nil {
			return m, nil
		}
		return nil, errNotObject
	default:
		return nil, errNotObject
	}
}
  • 这个meta.Accessor方法很实用,来看client-go是怎么使用的,如下图,在从本地缓存中取得资源列表时,无需关注资源类型,也能得到对象的namespace、labels等字段的信息,因此这个ListAllByNamespace方法就更加通用了,各种资源都能用这个方法来获取
    Kubernetes对象深入学习之三:对象属性-LMLPHP
  • 下面这个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 Deployment
	fmt.Println("Creating deployment...")
	result, err := client.Resource(deploymentRes).Namespace(apiv1.NamespaceDefault).Create(context.TODO(), deployment, metav1.CreateOptions{})
  • 进入Create方法内部看看,如下图所示,这个Create方法可以用来创建多种资源类型,但使用了meta.Accessor,无需知道资源类型也能得到资源名称
    Kubernetes对象深入学习之三:对象属性-LMLPHP
  • 至此,对象属性的学习就完成了,相信您对metav1.Object和metav1.ObjectMeta都有了深入的理解,也会有动手写代码试试的冲动
  • 实战一直是欣宸原创的招牌,这里也不会缺席,碍于篇幅限制本篇就只聊理论,下一篇,咱们实战走起,写代码体验对象属性的操作

你不孤单,欣宸原创一路相伴

  1. Java系列
  2. Spring系列
  3. Docker系列
  4. kubernetes系列
  5. 数据库+中间件系列
  6. DevOps系列
07-17 10:52