K8s核心资源对象- 概述(元数据- metav1.ObjectMeta)

基于1.25

K8s资源对象

K8s中主要按照:工作负载(Workload)、发现和负载均衡(Discovery & LB)、配置和存储(Config & Storage)

下面介绍一下metav1.ObjectMeta的属性

Name

Name是资源对象的入门属性,但是资源对象不一定是客户端传入的Name

PodName

// Generate a pod name that is unique among nodes by appending the nodeName.
func generatePodName(name string, nodeName types.NodeName) string {
return fmt.Sprintf("%s-%s", name, strings.ToLower(string(nodeName)))
}

StatefulSet Name

StaetfulSet生成的Pod Name格式<sts.name> - (资源对象名称+递增序号)

// getPodName gets the name of set's child Pod with an ordinal index of ordinal
func getPodName(set *apps.StatefulSet, ordinal int) string {
return fmt.Sprintf("%s-%d", set.Name, ordinal)
}

Deployment\Job.etc Name

这种是常见的Pod格式,以资源对象名称之上,随机增加5个字符串

const (
// TODO: make this flexible for non-core resources with alternate naming rules.
maxNameLength = 63
randomLength = 5
MaxGeneratedNameLength = maxNameLength - randomLength
)

func (simpleNameGenerator) GenerateName(base string) string {
if len(base) > MaxGeneratedNameLength {
base = base[:MaxGeneratedNameLength]
}
return fmt.Sprintf("%s%s", base, utilrand.String(randomLength))
}

GenerateName

如果资源对象没有设置Name,但是设置了GenerateName,则GenerateName作为前缀+5位随机值作为后缀

// BeforeCreate ensures that common operations for all resources are performed on creation. It only returns
// errors that can be converted to api.Status. It invokes PrepareForCreate, then GenerateName, then Validate.
// It returns nil if the object should be created.
func BeforeCreate(strategy RESTCreateStrategy, ctx context.Context, obj runtime.Object) error {
objectMeta, kind, kerr := objectMetaAndKind(strategy, obj)
if kerr != nil {
return kerr
}

// ensure that system-critical metadata has been populated
if !metav1.HasObjectMetaSystemFieldValues(objectMeta) {
return errors.NewInternalError(fmt.Errorf("system metadata was not initialized"))
}

// ensure namespace on the object is correct, or error if a conflicting namespace was set in the object
requestNamespace, ok := genericapirequest.NamespaceFrom(ctx)
if !ok {
return errors.NewInternalError(fmt.Errorf("no namespace information found in request context"))
}
if err := EnsureObjectNamespaceMatchesRequestNamespace(ExpectedNamespaceForScope(requestNamespace, strategy.NamespaceScoped()), objectMeta); err != nil {
return err
}

strategy.PrepareForCreate(ctx, obj)

if len(objectMeta.GetGenerateName()) > 0 && len(objectMeta.GetName()) == 0 {
objectMeta.SetName(strategy.GenerateName(objectMeta.GetGenerateName()))
}
......

Annotation

用户通过注解给资源对象添加非标识的元数据。

  • 注解通过KV形式存储
  • 通过可选前缀和名称,以/分割
  • 名称一63个字符之内,以字母、数字字符(a~z,0~9,A~Z),允许使用-.字母和数字
  • kuberetes.io/k8s.io是K8s组件保留注解

Generation

Generation是部署的版本,初始值为1,随spec改变而自增


// PrepareForCreate clears fields that are not allowed to be set by end users on creation.
func (deploymentStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
deployment := obj.(*apps.Deployment)
deployment.Status = apps.DeploymentStatus{}
deployment.Generation = 1

pod.DropDisabledTemplateFields(&deployment.Spec.Template, nil)
}


// Ref:https://github.com/kubernetes/kubernetes/blob/88e994f6bf8fc88114c5b733e09afea339bea66d/pkg/registry/apps/deployment/strategy.go#L118
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
func (deploymentStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
newDeployment := obj.(*apps.Deployment)
oldDeployment := old.(*apps.Deployment)
newDeployment.Status = oldDeployment.Status

pod.DropDisabledTemplateFields(&newDeployment.Spec.Template, &oldDeployment.Spec.Template)

// Spec updates bump the generation so that we can distinguish between
// scaling events and template changes, annotation updates bump the generation
// because annotations are copied from deployments to their replica sets.
if !apiequality.Semantic.DeepEqual(newDeployment.Spec, oldDeployment.Spec) ||
!apiequality.Semantic.DeepEqual(newDeployment.Annotations, oldDeployment.Annotations) {
newDeployment.Generation = oldDeployment.Generation + 1
}
}

ResourceVersion

ResourceVersion的值来自etcd的modifedindex,对象修改的时候,字段随着update。

// Get implements storage.Interface.Get.
func (s *store) Get(ctx context.Context, key string, opts storage.GetOptions, out runtime.Object) error {
preparedKey, err := s.prepareKey(key)
if err != nil {
return err
}
startTime := time.Now()
getResp, err := s.client.KV.Get(ctx, preparedKey)
metrics.RecordEtcdRequestLatency("get", getTypeName(out), startTime)
if err != nil {
return err
}
if err = s.validateMinimumResourceVersion(opts.ResourceVersion, uint64(getResp.Header.Revision)); err != nil {
return err
}

if len(getResp.Kvs) == 0 {
if opts.IgnoreNotFound {
return runtime.SetZeroValue(out)
}
return storage.NewKeyNotFoundError(preparedKey, 0)
}
kv := getResp.Kvs[0]

data, _, err := s.transformer.TransformFromStorage(ctx, kv.Value, authenticatedDataString(preparedKey))
if err != nil {
return storage.NewInternalError(err.Error())
}

return decode(s.codec, s.versioner, data, out, kv.ModRevision)
}

ETCD的4种版本:

字段 作用范围 说明
Version key 单个key的修改次数,单调递增
Revision 全局 key在集群中的全局版本号,全局唯一
ModRevsion key key最后一次修改时的Revision
CreateRevsion 全局 key创建时的Revision

Owner Reference

OwnerReference字段主要用于垃圾回收的资源级联删除。

// OwnerReference contains enough information to let you identify an owning
// object. An owning object must be in the same namespace as the dependent, or
// be cluster-scoped, so there is no namespace field.
// +structType=atomic
type OwnerReference struct {
// API version of the referent.
// 属主对象的APIVersion
APIVersion string `json:"apiVersion" protobuf:"bytes,5,opt,name=apiVersion"`
// Kind of the referent.
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
// 属主对象的Kind
Kind string `json:"kind" protobuf:"bytes,1,opt,name=kind"`
// Name of the referent.
// More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names
// 属主对象的Name
Name string `json:"name" protobuf:"bytes,3,opt,name=name"`
// UID of the referent.
// More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids
// 属主对象的UID
UID types.UID `json:"uid" protobuf:"bytes,4,opt,name=uid,casttype=k8s.io/apimachinery/pkg/types.UID"`
// If true, this reference points to the managing controller.
// +optional
// 属主对象的关系的控制器,没有被控制器自动控制,默认为false
Controller *bool `json:"controller,omitempty" protobuf:"varint,6,opt,name=controller"`
// If true, AND if the owner has the "foregroundDeletion" finalizer, then
// the owner cannot be deleted from the key-value store until this
// reference is removed.
// See https://kubernetes.io/docs/concepts/architecture/garbage-collection/#foreground-deletion
// for how the garbage collector interacts with this field and enforces the foreground deletion.
// Defaults to false.
// To set this field, a user needs "delete" permission of the owner,
// otherwise 422 (Unprocessable Entity) will be returned.
// +optional
// 设置是否自动被GC,默认为true
BlockOwnerDeletion *bool `json:"blockOwnerDeletion,omitempty" protobuf:"varint,7,opt,name=blockOwnerDeletion"`
}
  1. 使用kubectl-check-ownerreference检查OwnerReference是否有效
  2. 在v1.20.0+中,如果GC检查到无效的跨命名空间的属主,则报告一个告警事件。通过kubectl get events -A –filed-selecon=reason=OwnerRefInvalidNamespace命令获取该类型的事件

Finalizers

Finalizers字段包含多个Finalizer,Finalizer主要用于控制资源被删除前的清理工作。

  • K8s API会通过.metadata.deletionTimestamp 字段标记要删除的对象,并返回202状态的,进入只读状态
  • 当.metadata.finalizers字段为空,K8s认为已经成功删除对象

ManagedFileds

ManagedFileds字段管理器,主要用于协调多个客户端更新同一资源对象时的冲突。

1.21版本,ManagedFileds被隐藏,需要通过 –show-managed-fields=true,才能展示

// ManagedFieldsEntry is a workflow-id, a FieldSet and the group version of the resource
// that the fieldset applies to.
type ManagedFieldsEntry struct {
// Manager is an identifier of the workflow managing these fields.
// 执行操作的
Manager string `json:"manager,omitempty" protobuf:"bytes,1,opt,name=manager"`
// Operation is the type of operation which lead to this ManagedFieldsEntry being created.
// The only valid values for this field are 'Apply' and 'Update'.
// 操作的类型,包括Apply和Update
Operation ManagedFieldsOperationType `json:"operation,omitempty" protobuf:"bytes,2,opt,name=operation,casttype=ManagedFieldsOperationType"`
// APIVersion defines the version of this resource that this field set
// applies to. The format is "group/version" just like the top-level
// APIVersion field. It is necessary to track the version of a field
// set because it cannot be automatically converted.
// 定义这个资源的字段集使用的版本
APIVersion string `json:"apiVersion,omitempty" protobuf:"bytes,3,opt,name=apiVersion"`
// Time is the timestamp of when the ManagedFields entry was added. The
// timestamp will also be updated if a field is added, the manager
// changes any of the owned fields value or removes a field. The
// timestamp does not update when a field is removed from the entry
// because another manager took it over.
// +optional
// 操作时间
Time *Time `json:"time,omitempty" protobuf:"bytes,4,opt,name=time"`

// Fields is tombstoned to show why 5 is a reserved protobuf tag.
//Fields *Fields `json:"fields,omitempty" protobuf:"bytes,5,opt,name=fields,casttype=Fields"`

// FieldsType is the discriminator for the different fields format and version.
// There is currently only one possible value: "FieldsV1"
// 不同字段格式和版本的鉴别器,目前都是Fields V1
FieldsType string `json:"fieldsType,omitempty" protobuf:"bytes,6,opt,name=fieldsType"`
// FieldsV1 holds the first JSON version format as described in the "FieldsV1" type.
// +optional
// 以JSON展示类似Trie的数据结构中,要么以'.'要么是子字段
// f:<name> <name>是结构中的字段的名称或者映射中的键
// v:<value> <value>列表项的确切的JSON格式的值
// i:<index> <index>是列表项的位置
// k:<key> <key>是列表项关键字段与唯一值的映射,如果映射的为空字段值,则该键代表的字段是集合的一部分
FieldsV1 *FieldsV1 `json:"fieldsV1,omitempty" protobuf:"bytes,7,opt,name=fieldsV1"`

// Subresource is the name of the subresource used to update that object, or
// empty string if the object was updated through the main resource. The
// value of this field is used to distinguish between managers, even if they
// share the same name. For example, a status update will be distinct from a
// regular update using the same manager name.
// Note that the APIVersion field is not related to the Subresource field and
// it always corresponds to the version of the main resource.
// 更新字段的子资源的名称,如果更新资源通过主资源更新,则为空
Subresource string `json:"subresource,omitempty" protobuf:"bytes,8,opt,name=subresource"`
}

Manager接口定义了ManageFields并合并Apply操作配置的标准接口

// Manager updates the managed fields and merges applied configurations.
type Manager interface {
// Update is used when the object has already been merged (non-apply
// use-case), and simply updates the managed fields in the output
// object.
// * `liveObj` is not mutated by this function
// * `newObj` may be mutated by this function
// Returns the new object with managedFields removed, and the object's new
// proposed managedFields separately.
// Update在对象已经被合并的时候使用,并且在输出的对象中更新MangedFields,返回删除了ManagedFields的新对象,以及新对象单独提出的ManagedFields
Update(liveObj, newObj runtime.Object, managed Managed, manager string) (runtime.Object, Managed, error)

// Apply is used when server-side apply is called, as it merges the
// object and updates the managed fields.
// * `liveObj` is not mutated by this function
// * `newObj` may be mutated by this function
// Returns the new object with managedFields removed, and the object's new
// proposed managedFields separately.
// 在调用服务端应用的时候使用,因为他合并了对象并更新了ManagedFields
Apply(liveObj, appliedObj runtime.Object, managed Managed, fieldManager string, force bool) (runtime.Object, Managed, error)
}
  • 俩个func,都有liveObjnewObj
    • liveObj:该对象不会呗Update和Apply func改变
    • newObj:可能会被Update和Apply func改变
  • Update和Apply都有三个参数
    • liveObj:原有的对象
    • appliedObj:新创建、待合并的对象
    • manager:执行操作,通过xxxOptions传入

许多Manager都被实现,通过装饰器模式暴露对外:

	f, err := NewStructuredMergeManager(typeConverter, objectConverter, objectDefaulter, kind.GroupVersion(), hub, resetFields)
if err != nil {
return nil, fmt.Errorf("failed to create field manager: %v", err)
}
return newDefaultFieldManager(f, typeConverter, objectConverter, objectCreater, kind, subresource), nil
}

// newDefaultFieldManager is a helper function which wraps a Manager with certain default logic.
func newDefaultFieldManager(f Manager, typeConverter TypeConverter, objectConverter runtime.ObjectConvertor, objectCreater runtime.ObjectCreater, kind schema.GroupVersionKind, subresource string) *FieldManager {
return NewFieldManager(
NewLastAppliedUpdater(
NewLastAppliedManager(
NewProbabilisticSkipNonAppliedManager(
NewCapManagersManager(
NewBuildManagerInfoManager(
NewManagedFieldsUpdater(
NewStripMetaManager(f),
), kind.GroupVersion(), subresource,
), DefaultMaxUpdateManagers,
), objectCreater, kind, DefaultTrackOnCreateProbability,
), typeConverter, objectConverter, kind.GroupVersion()),
), subresource,
)
}

清除ManageFields

只把managedFieilds字段设置并不重置字段,managedFields字段永远不会该字段无关的客户删除。可以通过MergePatch、StrategicMergePatch、JSONPatch、Update,以及非应用方式操作覆盖,可以通过Patch方式清空managedFields字段,实现对象的完整删除ManageFields