Resource(资源)

Kubernetes的本质是一个资源控制系统——管理、调度资源并维护资源的状态。
一个资源被实例化后会表达为一个资源对象(即Resource Object).
所有的资源对象都是Entity,Kubernetes使用Entity来表示资源的状态。
目前Kubernetes支持两种Entity:

  • 持久性实体(Persistent Entity):在资源对象被创建后,Kubernetes会持久确保该资源对象存在。例如Deployment。
  • 短暂性实体(Ephemeral Entity):也称为非持久性实体(Non-Persistent Entity)。在资源对象被创建后,如果故障或调度失败,不会重新创建该资源对象。例如Pod。

资源代码数据结构实例如下:
代码路径:vendor/k8s.io/apimachinery/pkg/apis/meta/v1/types.go

  1. // APIResource specifies the name of a resource and whether it is namespaced.
  2. type APIResource struct {
  3. // name is the plural name of the resource.
  4. Name string `json:"name" protobuf:"bytes,1,opt,name=name"`
  5. // singularName is the singular name of the resource. This allows clients to handle plural and singular opaquely.
  6. // The singularName is more correct for reporting status on a single item and both singular and plural are allowed
  7. // from the kubectl CLI interface.
  8. SingularName string `json:"singularName" protobuf:"bytes,6,opt,name=singularName"`
  9. // namespaced indicates if a resource is namespaced or not.
  10. Namespaced bool `json:"namespaced" protobuf:"varint,2,opt,name=namespaced"`
  11. // group is the preferred group of the resource. Empty implies the group of the containing resource list.
  12. // For subresources, this may have a different value, for example: Scale".
  13. Group string `json:"group,omitempty" protobuf:"bytes,8,opt,name=group"`
  14. // version is the preferred version of the resource. Empty implies the version of the containing resource list
  15. // For subresources, this may have a different value, for example: v1 (while inside a v1beta1 version of the core resource's group)".
  16. Version string `json:"version,omitempty" protobuf:"bytes,9,opt,name=version"`
  17. // kind is the kind for the resource (e.g. 'Foo' is the kind for a resource 'foo')
  18. Kind string `json:"kind" protobuf:"bytes,3,opt,name=kind"`
  19. // verbs is a list of supported kube verbs (this includes get, list, watch, create,
  20. // update, patch, delete, deletecollection, and proxy)
  21. Verbs Verbs `json:"verbs" protobuf:"bytes,4,opt,name=verbs"`
  22. // shortNames is a list of suggested short names of the resource.
  23. ShortNames []string `json:"shortNames,omitempty" protobuf:"bytes,5,rep,name=shortNames"`
  24. // categories is a list of the grouped resources this resource belongs to (e.g. 'all')
  25. Categories []string `json:"categories,omitempty" protobuf:"bytes,7,rep,name=categories"`
  26. // The hash value of the storage version, the version this resource is
  27. // converted to when written to the data store. Value must be treated
  28. // as opaque by clients. Only equality comparison on the value is valid.
  29. // This is an alpha feature and may change or be removed in the future.
  30. // The field is populated by the apiserver only if the
  31. // StorageVersionHash feature gate is enabled.
  32. // This field will remain optional even if it graduates.
  33. // +optional
  34. StorageVersionHash string `json:"storageVersionHash,omitempty" protobuf:"bytes,10,opt,name=storageVersionHash"`
  35. }

资源的内部版本和外部版本

Kuberntes的资源定义在源码pkg/api目录下。同一个资源对应着两个版本,内部版本(Internal Version)和外部版本(External Version)。
例子:Deployment资源对象,外部版本表现为apps/v1,内部版本表现为apps/_internal.

外部版本资源对象(External Object)

外部版本资源对象,也成为Versioned Object(即拥有资源版本的资源对象)。外部版本用于暴露给用户,比如通过Json和Yaml格式的请求。外部版本的资源对象通过资源版本进行标识(Alpha,Beta,Stable)。
资源的外部版本定义在:

  1. pkg/apis/<group>/<version>/

Kubernetes源码中,外部版本的资源类型定义在vendor/k8s.io/api目录:

  1. vendor/k8s.io/api/<gourp>/<version>/<resource file>/

外部资源由于需要对外暴露,因此定义了JSON Tag和Protocal Tag。
不同资源版本包在源码中的引用路径不同,代码示例如下:

  1. corev1 "k8s.io/api/core/v1" //外部资源版本(资源类型)
  2. core "k8s.io/kubernetes/pkg/apis/core" //内部资源版本
  3. k8s_api_v1 "k8s.io/kubernetes/pkg/api/core/v1" //外部资源版本(与资源相关的函数,例如资源转换函数)

内部版本资源对象(Internal Object)

内部版本资源对象。内部版本不对外暴露,尽在Kubernetes API Server内部使用。内部版本用于多资源版本的转换,例如v1beta1 >> v1,其过程为v1beta1 >> internal >> v1。内部版本资源对象通过runtime.APIVersionInternal标识。
内部部资源对象代码定义在下面的目录中:

  1. pkg/apis/<group>/

资源代码定义

Kubernetes资源代码定义在pkg/apis目录下,同一资源对应的内部版本和外部版本的资源代码结构并不相同。
资源内部版本定义了所有支持的资源类型(types.go)、资源验证方法(validation/validation.go)、资源注册至资源注册表的方法(install/install.go)。
资源的外部版本定义了资源的转换方法(conversion.go)、资源的默认值(defaults.go)等。

内部版本的资源代码结构

以Deployment为例,它的内部资源版本定义在pkg/apis/apps目录下

  1. .
  2. ├── BUILD
  3. ├── OWNERS
  4. ├── doc.go // GoDoc文件,定义了当前包的注释信息。在Kubernetes资源包中,它还担当了代码生成器的全局Tags描述文件。
  5. ├── fuzzer
  6. ├── BUILD
  7. └── fuzzer.go
  8. ├── install // 把资源组下的所有资源注册到资源注册表中
  9. ├── BUILD
  10. └── install.go
  11. ├── register.go // 定义了资源组、资源版本及资源的注册信息。
  12. ├── types.go // 定义了当前资源组、资源版本下所支持的资源类型。
  13. ├── v1 // 定义了资源组下拥有的资源版本的资源(即外部版本)
  14. ├── BUILD
  15. ├── conversion.go
  16. ├── conversion_test.go
  17. ├── defaults.go
  18. ├── defaults_test.go
  19. ├── doc.go
  20. ├── register.go
  21. ├── zz_generated.conversion.go
  22. └── zz_generated.defaults.go
  23. ├── v1beta1 // 定义了资源组下拥有的资源版本的资源(即外部版本)
  24. ├── BUILD
  25. ├── conversion.go
  26. ├── defaults.go
  27. ├── defaults_test.go
  28. ├── doc.go
  29. ├── register.go
  30. ├── zz_generated.conversion.go
  31. └── zz_generated.defaults.go
  32. ├── v1beta2 // 定义了资源组下拥有的资源版本的资源(即外部版本)
  33. ├── BUILD
  34. ├── conversion.go
  35. ├── conversion_test.go
  36. ├── defaults.go
  37. ├── defaults_test.go
  38. ├── doc.go
  39. ├── register.go
  40. ├── zz_generated.conversion.go
  41. └── zz_generated.defaults.go
  42. ├── validation // 定义了资源的验证方法
  43. ├── BUILD
  44. ├── validation.go
  45. └── validation_test.go
  46. └── zz_generated.deepcopy.go // 定义了资源的深度复制操作,该文件由代码生成器自动生成

每个Kubernetes资源目录都通过register.go代码文件定义所属的资源组和资源版本,内部版本资源对象通过runtime.APIVersionInternal(即__internal)标识。代码如下(pkg/apis/apps/register.go):

  1. // SchemeGroupVersion is group version used to register these objects
  2. var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal}

每一个Kubernetes资源目录,都通过type.go代码文件定义当前资源组/资源版本下所支持的资源类型,代码如下:

  1. type StatefulSet struct {...}
  2. type Deployment struct {...}

外部版本的资源代码结构

以Deployment为例,它的外部版本定义在pkg/apis/apps{v1,v1beta1,v1beta2}目录下,其资源代码结构如下:

  1. .
  2. ├── BUILD
  3. ├── conversion.go //定义了资源的转换函数(默认转换函数),并将默认的转换函数注册到资源注册表中。
  4. ├── conversion_test.go
  5. ├── defaults.go // 定义了资源的默认值函数,并将默认值函数注册到资源注册表。
  6. ├── defaults_test.go
  7. ├── doc.go // GoDoc文件,定义了当前包的注释信息。在Kubernetes资源包中,它还担当了代码生成器的全局Tags描述文件。
  8. ├── register.go // 定义了资源组、资源版本及资源的注册信息。
  9. ├── zz_generated.conversion.go //定义了资源的转换函数(自动生成的转换函数),并将生成的转换函数注册到资源注册表中。该文件由代码生成器自动生成
  10. └── zz_generated.defaults.go //定义了资源的默认值函数(自动生成的默认值函数),并将生成的转换函数注册到资源注册表中。该文件由代码生成器自动生成

外部版本资源对象通过资源版本(Alpha,Beta,Stable)标识,代码示例如下:(pkg/apis/apps/v1/register.go)

  1. // SchemeGroupVersion is group version used to register these objects
  2. var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1"}

将资源注册到资源注册表

在Kubernetes的资源目录中,都拥有一个install/install.go代码文件,它负责将资源信息注册到资源注册表中(scheme)。
core资源组代码示例(pkg/apis/core/install/install.go

  1. // Package install installs the v1 monolithic api, making it available as an
  2. // option to all of the API encoding/decoding machinery.
  3. package install
  4. import (
  5. "k8s.io/apimachinery/pkg/runtime"
  6. utilruntime "k8s.io/apimachinery/pkg/util/runtime"
  7. "k8s.io/kubernetes/pkg/api/legacyscheme"
  8. "k8s.io/kubernetes/pkg/apis/core"
  9. "k8s.io/kubernetes/pkg/apis/core/v1"
  10. )
  11. func init() {
  12. // legacyscheme.Scheme是Kubernetes组件的全局资源注册表,Kubernetes的所有资源信息都交给资源注册表统一管理。
  13. Install(legacyscheme.Scheme)
  14. }
  15. // Install registers the API group and adds types to a scheme
  16. func Install(scheme *runtime.Scheme) {
  17. utilruntime.Must(core.AddToScheme(scheme)) // 注册core资源组内部版本资源
  18. utilruntime.Must(v1.AddToScheme(scheme)) // 注册core资源组外部版本资源
  19. utilruntime.Must(scheme.SetVersionPriority(v1.SchemeGroupVersion)) // 注册资源组的版本顺序,如果多个资源,潜在前面优先级越高。
  20. }

资源首选版本

首选版本(Preferred Version),也称为优选版本(Priority Version)。某些资源拥有多个版本,在某些场景下不指定版本,则使用首选版本。
以apps资源组为例,注册资源时会注册多个资源版本,分别是v1,v1beta2,v1beta1,代码示例如下(pkg/apis/apps/install/install.go):

  1. utilruntime.Must(scheme.SetVersionPriority(v1.SchemeGroupVersion, v1beta2.SchemeGroupVersion, v1beta1.SchemeGroupVersion))

在资源注册表的versionPriority结构中,资源的首选版本如下所示:

  1. versionPriority map[string][]string // 数据结构
  2. versionPriority "apps"[{"v1"},{"v1beta2"},{"v1beta1"}] // 所有的外部版本的值,不存储内部版本

当通过资源注册表scheme.PrefferedVersionAllGroups函数获取所有资源组下的首选版本时,将位于前面的资源版本作为首选版本,代码如下所示:

  1. // PreferredVersionAllGroups returns the most preferred version for every group.
  2. // group ordering is random.
  3. func (s *Scheme) PreferredVersionAllGroups() []schema.GroupVersion {
  4. ret := []schema.GroupVersion{}
  5. for group, versions := range s.versionPriority {
  6. for _, version := range versions {
  7. ret = append(ret, schema.GroupVersion{Group: group, Version: version})
  8. break // 循环一次就break,所以支取versions数组的第一个元素
  9. }
  10. }
  11. for _, observedVersion := range s.observedVersions {
  12. found := false
  13. for _, existing := range ret {
  14. if existing.Group == observedVersion.Group {
  15. found = true
  16. break
  17. }
  18. }
  19. if !found {
  20. ret = append(ret, observedVersion)
  21. }
  22. }
  23. return ret
  24. }

除了scheme.PreferredVersionAllGroups,还有两个函数与获取资源版本的顺序有关:

  • 获取所有资源的所有资源版本,按优先级顺序返回

    1. // PrioritizedVersionsAllGroups returns all known versions in their priority order. Groups are random, but
    2. // versions for a single group are prioritized
    3. func (s *Scheme) PrioritizedVersionsAllGroups() []schema.GroupVersion {
    4. ret := []schema.GroupVersion{}
    5. for group, versions := range s.versionPriority {
    6. for _, version := range versions {
    7. ret = append(ret, schema.GroupVersion{Group: group, Version: version})
    8. }
    9. }
    10. for _, observedVersion := range s.observedVersions {
    11. found := false
    12. for _, existing := range ret {
    13. if existing == observedVersion {
    14. found = true
    15. break
    16. }
    17. }
    18. if !found {
    19. ret = append(ret, observedVersion)
    20. }
    21. }
    22. return ret
    23. }
  • 获取指定资源组的资源版本,按照优先级顺序返回

    1. // PrioritizedVersionsForGroup returns versions for a single group in priority order
    2. func (s *Scheme) PrioritizedVersionsForGroup(group string) []schema.GroupVersion {
    3. ret := []schema.GroupVersion{}
    4. for _, version := range s.versionPriority[group] {
    5. ret = append(ret, schema.GroupVersion{Group: group, Version: version})
    6. }
    7. for _, observedVersion := range s.observedVersions {
    8. if observedVersion.Group != group {
    9. continue
    10. }
    11. found := false
    12. for _, existing := range ret {
    13. if existing == observedVersion {
    14. found = true
    15. break
    16. }
    17. }
    18. if !found {
    19. ret = append(ret, observedVersion)
    20. }
    21. }
    22. return ret
    23. }

    资源操作方法

    Kubernetes支持的操作有8种,分别为:createdeletedeletecollectiongetlistpatchupdatewatch
    这些操作可以分为4大类:

  • 创建:create

  • 删除:deletedeletecollection
  • 更新:patchupdate
  • 查询:getlistwatch

资源操作通过通过metav1.Verbs数据结构进行描述,代码示例如下(vendor/k8s.io/apimachinery/pkg/apis/meta/v1/types.go):

  1. type Verbs []string
  2. func (vs Verbs) String() string {
  3. return fmt.Sprintf("%v", []string(vs))
  4. }

资源操作方法都是针对存储(Storage)进行操作,vendor/k8s.io/apiserver/pkg/registry/目录定义了资源对象拥有的操作类型。
每种操作方法对应一个操作方法接口(Interface),如下表所示:

操作方法(Verbs) 操作方法接口(Interface)
create rest.Creater 资源对象创建接口
delete rest.GracefulDeleter 资源对象删除接口(单个资源对象)
deletecollection rest.CollectionDeleter 资源对象删除接口(多个资源对象)
update rest.Updater 资源对象更新接口(完整资源对象的更新)
patch rest.Patcher 资源对象更新接口(局部资源对象的更新)
get rest.Getter 资源对象获取接口(单个资源对象)
list rest.Lister 资源对象获取接口(多个资源对象)
watch rest.Watcher 资源对象监控接口

如果某个资源对象在存储(Storage上实现了)某接口,那么该资源同时就拥有了相关的操作方法。
相关接口定义如下(vendor/k8s.io/apiserver/pkg/registry/rest/rest.go),该文件中定义了很多接口,下面以Creater为例:

  1. ...
  2. // Creater is an object that can create an instance of a RESTful object.
  3. type Creater interface {
  4. // New returns an empty object that can be used with Create after request data has been put into it.
  5. // This object must be a pointer type for use with Codec.DecodeInto([]byte, runtime.Object)
  6. New() runtime.Object
  7. // Create creates a new version of a resource.
  8. Create(ctx context.Context, obj runtime.Object, createValidation ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error)
  9. }
  10. ...

以Pod对象为例,Pod资源对象的存储(Storage)实现了以上接口的方法,Pod资源对象继承了genericregistry.Store,该对象可以管理存储(Storage)的增删改查操作,代码示例如下
代码路径:pkg/registry/core/pod/storage/storage.go

  1. type PodStorage struct {
  2. Pod *REST
  3. ...
  4. }
  5. type REST struct {
  6. *genericregistry.Store
  7. ...
  8. }

代码路径:vendor/k8s.io/apiserver/pkg/registry/generic/registry/store.go

  1. func (e *Store) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
  2. ...
  3. }
  4. func (e *Store) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
  5. ...
  6. }

下面以pod/logs子资源为例,该资源只实现了get操作方法,代码示例如下:
代码路径:pkg/registry/core/pod/storage/storage.go

  1. type PodStorage struct {
  2. ...
  3. Log *podrest.LogREST
  4. ...
  5. }

代码路径:pkg/registry/core/pod/rest/log.go

  1. func (r *LogREST) Get(ctx context.Context, name string, opts runtime.Object) (runtime.Object, error) {
  2. ...
  3. }

资源与命名空间

在Kubernetes中,大部分资源都属于某些命名空间,但并不是所有资源都有属于某个命名空间,比如Node资源对象。
可以通过ObjectMeta.Namespace查看一个资源对象所属的命名空间。

  1. ------------------- ------------------
  2. | Pod | | ObjectMata |
  3. ------------------- ------------------
  4. |metav1.TypeMeta | -----> | ... |
  5. |metav1.ObjectMeta| |Namespace string|
  6. |Spec PodSpec | | ... |
  7. |Status PodStatus | ------------------
  8. -------------------

查看存在于和不存在于命名空间中的资源对象

  1. kubectl api-resources --namespaced=true
  2. kubectl api-resources --namespaced=false