《kubernetes源码剖析》- k8s核心数据结构
| 阅读 | 共 4826 字,阅读约
Overview
《kubernetes源码剖析》- k8s核心数据结构
概述
k8s是一个完全以资源为中心的系统,本质是一个资源控制系统。包括:注册、管理、调度资源并维护资源的状态。文章围绕资源展开,介绍k8s核心的数据结构
- Object:所有资源的父类
- Group:资源组
- Version:资源版本
- Resource:资源
- Kind:资源种类
- Codec:编解码器
- Converter:资源转换器
k8s系统支持多个Group,每个Group支持多个Version,每个Version支持多个Resource,部分资源同时会拥有自己的子资源。完整资源表示:<group>/<version>/<resource>/<subresource>
资源操作的8中方法:Verbs
- create
- delete
- deletecollection
- get
- list
- path
- update
- watch
资源的分类
每一个资源至少有两个版本,分别是外部版本和内部版本:
- 外部版本:用于对外暴露给用户请求的接口所使用的资源对象
- 内部版本:不对外暴露,仅在api-server内部使用
从另外一个维度,也可以把资源分为:
- 内置资源:k8s默认自带的资源
- 自定义资源:用户通过CRD自己实现
查看资源
1# 获取所有的资源组和版本
2kubectl api-versions
3# 获取所有的资源
4kubectl api-resources
资源数据结构
使用的是k8s的1.14版本的代码
1mkdir $GOPATH/src/k8s.io
2cd $GOPATH/src/k8s.io
3
4# 通过go get获取
5GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -d -v k8s.io/kubernetes@v1.14.0
6
7# 通过git克隆
8git clone https://github.com/kubernetes/kubernetes -b release-1.14
9
10# 编译
11cd kubernetes
12# 指定编译平台、编译所有组件、开启verbose日志、禁止编译优化
13KUBE_BUILD_PLATFORMS=linux/amd64 make all GOFLAGS=-v GOGCFLAGS="-N -l"
资源父类:runtime.Object
runtime.Object是k8s类型系统的基石,所有的资源类型都有一个共同的结构叫做runtime.Object, 它被设计为接口类型,作为资源对象的通用资源类型。资源对象可以转换为runtime.Object对象,也可以反向转换
源码位置:vendor/k8s.io/apimachinery/pkg/runtime/interface.go
1type Object interface {
2 // 设置并返回ObjectKind
3 GetObjectKind() schema.ObjectKind
4 // 深拷贝当前资源对象
5 DeepCopyObject() Object
6}
7
8type ObjectKind interface {
9 SetGroupVersionKind(kind GroupVersionKind)
10 GroupVersionKind() GroupVersionKind
11}
单个资源信息:APIResource
k8s中每个资源都可以使用metav1.APIResource结构进行描述
源码位置:vendor/k8s.io/apimachinery/pkg/apis/meta/v1/types.go
1type APIResource struct {
2 // 资源名称(复数形式)
3 Name string `json:"name" protobuf:"bytes,1,opt,name=name"`
4
5 // 资源名称(单数形式)
6 SingularName string `json:"singularName" protobuf:"bytes,6,opt,name=singularName"`
7
8 // 命名空间
9 Namespaced bool `json:"namespaced" protobuf:"varint,2,opt,name=namespaced"`
10
11 // 资源分组
12 Group string `json:"group,omitempty" protobuf:"bytes,8,opt,name=group"`
13
14 // 资源版本
15 Version string `json:"version,omitempty" protobuf:"bytes,9,opt,name=version"`
16
17 // 资源类型
18 Kind string `json:"kind" protobuf:"bytes,3,opt,name=kind"`
19
20 // 资源可操作的方法列表
21 Verbs Verbs `json:"verbs" protobuf:"bytes,4,opt,name=verbs"`
22
23 // 资源的简称
24 ShortNames []string `json:"shortNames,omitempty" protobuf:"bytes,5,rep,name=shortNames"`
25
26 // 资源所属的类别列表
27 Categories []string `json:"categories,omitempty" protobuf:"bytes,7,rep,name=categories"`
28
29 // 存储版本的hash值,这只是一个试用的功能,将来可能改变或者移除
30 StorageVersionHash string `json:"storageVersionHash,omitempty" protobuf:"bytes,10,opt,name=storageVersionHash"`
31}
所有的资源列表:APIResourceList
1type APIResourceList struct {
2 TypeMeta `json:",inline"`
3 // groupVersion is the group and version this APIResourceList is for.
4 GroupVersion string `json:"groupVersion" protobuf:"bytes,1,opt,name=groupVersion"`
5 // resources contains the name of the resources and if they are namespaced.
6 APIResources []APIResource `json:"resources" protobuf:"bytes,2,rep,name=resources"`
7}
最常用的资源对象:GVR
源码位置:vendor/k8s.io/apimachinery/pkg/runtime/schema/group_version.go
1type GroupVersionResource struct {
2 Group string
3 Version string
4 Resource string
5}
资源组:APIGroup
资源组按照不同功能将资源进行划分,资源组的特征:
- 允许单独启动、禁用资源组,也允许单独启用、禁用资源组内的资源
- 支持同名资源在不同的资源组内
- 支持CRD自定义资源扩展
源码位置:vendor/k8s.io/apimachinery/pkg/apis/meta/v1/types.go
1type APIGroup struct {
2 TypeMeta `json:",inline"`
3
4 // 资源组名称
5 Name string `json:"name" protobuf:"bytes,1,opt,name=name"`
6
7 // 资源组下所支持的资源版本
8 Versions []GroupVersionForDiscovery `json:"versions" protobuf:"bytes,2,rep,name=versions"`
9
10 // 首选版本,当存在多个资源版本时,优先选择的版本
11 PreferredVersion GroupVersionForDiscovery `json:"preferredVersion,omitempty" protobuf:"bytes,3,opt,name=preferredVersion"`
12
13 ServerAddressByClientCIDRs []ServerAddressByClientCIDR `json:"serverAddressByClientCIDRs,omitempty" protobuf:"bytes,4,rep,name=serverAddressByClientCIDRs"`
14}
版本:APIVersions
资源的版本控制。k8s的资源版本分为三种:
- Alpha:内部测试版本。
- Beta:修复大部分bug,给特定用户群体使用的版本
- Stable:稳定运行版本
Alpha版本
- 该版本不稳定,可能存在很多缺陷和漏洞
- 官方随时可能放弃支持该版本。
- 默认处于该版本的功能会被禁用。
- 版本名称一般为:v1alpha1、v1alpha2、v2alpha1
Beta版本
- 相对稳定的版本,经过官方和社区很多次测试
- 功能迭代时,版本会有改变,但是不会被删除
- 默认该版本的功能会被开启
- 版本名称一般为:v1beta1、v1beta2、v2beta1
Stable版本
- 正式发布的版本
- 基本形成的产品,版本不会被删除
- 功能全部被开启
- 版本名称一般为:v1、v2、v3
1type APIVersions struct {
2 TypeMeta `json:",inline"`
3 // 所支持的资源版本列表
4 Versions []string `json:"versions" protobuf:"bytes,1,rep,name=versions"`
5
6 ServerAddressByClientCIDRs []ServerAddressByClientCIDR `json:"serverAddressByClientCIDRs" protobuf:"bytes,2,rep,name=serverAddressByClientCIDRs"`
7}
资源外部版本与内部版本
资源代码定义在pkg/apis目录下。在k8s中,同一资源对应两个版本,分别是外部版本和内部版本。
- 外部版本:用于对外暴露给用户请求的接口使用的资源对象,比如通过yaml创建资源对象
- 内部版本:不对外暴露,用于多资源版本的转换。比如v1beta1 -> v1, 需要经过v1beta1 -> internal -> v1 的转换
代码存放路径:
- 外部版本:vendor/k8s.io/api/core/v1/types.go
- 有tag信息:proto、json等
- 内部版本:pkg/apis/core/types.go
- 没有tag信息
1// 外部版本
2type Pod struct {
3 metav1.TypeMeta `json:",inline"`
4 // Standard object's metadata.
5 // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
6 // +optional
7 metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
8
9 // Specification of the desired behavior of the pod.
10 // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
11 // +optional
12 Spec PodSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`
13
14 // Most recently observed status of the pod.
15 // This data may not be up to date.
16 // Populated by the system.
17 // Read-only.
18 // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
19 // +optional
20 Status PodStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
21}
1// 内部版本
2type Pod struct {
3 metav1.TypeMeta
4 // +optional
5 metav1.ObjectMeta
6
7 // Spec defines the behavior of a pod.
8 // +optional
9 Spec PodSpec
10
11 // Status represents the current information about a pod. This data may not be up
12 // to date.
13 // +optional
14 Status PodStatus
15}
资源源码
代码定义
资源代码定义在pkg/apis目录下,同一资源对应内部版本和外部版本,代码结构并不相同。
- 内部版本:
- 支持的资源类型:types.go
- 资源验证方法:validation.go
- 注册方法:install.go
- 外部版本:
- 转换方法:conversion.go
- 默认值:default.go
1├── BUILD
2├── OWNERS
3├── doc.go // 定义包注释信息
4├── fuzzer
5├── install
6├── register.go // 定义了资源组、资源版本、资源注册信息
7├── types.go // 定了了在当前资源组、资源版本下支持的资源信息
8├── v1 // 定义了资源组下拥有的v1版本资源
9│ ├── BUILD
10│ ├── conversion.go // 定义了资源转换函数(默认的),并将转换函数注册到资源注册表中
11│ ├── conversion_test.go
12│ ├── defaults.go // 定义了资源默认函数
13│ ├── defaults_test.go
14│ ├── doc.go
15│ ├── register.go
16│ ├── zz_generated.conversion.go // 定义了资源转换函数(自动生成的),由代码生成器生成
17│ └── zz_generated.defaults.go // 定义了资源默认值(自动生成的)
18├── v1beta1 // 定义了资源组下拥有的v1beta1版本资源
19├── v1beta2 // 定义了资源组下拥有的v1beta2版本资源
20├── validation // 定义了资源的验证方法
21└── zz_generated.deepcopy.go // 定义了资源的深拷贝操作,该文件由代码生成器自动生成
资源注册表
K8s的资源注册表,类似于windows的注册表。负责资源类型的统一注册、存储、查询、管理等机制。目前k8s中所有资源类型都已经注册到Scheme资源注册表中,是一个内存型的资源注册表。 注册表支持两种资源类型的注册:
- 有版本资源类型:
- 无版本资源类型:早期k8s中的资源类型,现在逐渐被弱化
注册表数据结构
源码位置:vendor/k8s.io/apimachinery/pkg/runtime/scheme.go
1type Scheme struct {
2 // 存储GVK与type的映射关系
3 gvkToType map[schema.GroupVersionKind]reflect.Type
4
5 // 存储type与GVK的映射关系,一个type会对应一个或多个gvk
6 typeToGVK map[reflect.Type][]schema.GroupVersionKind
7
8 // 无版本类型与gvk的映射关系
9 unversionedTypes map[reflect.Type]schema.GroupVersionKind
10
11 // 资源名称与类型的映射关系
12 unversionedKinds map[string]reflect.Type
13
14 ...
15}
注册方法
在每一个k8s资源组目录中,都有一个install/install.go代码文件,负责将资源信息注册到资源注册表中
1
2func init() {
3 // legacyscheme.Scheme 是api-server的全局资源注册表
4 Install(legacyscheme.Scheme)
5}
6
7// Install registers the API group and adds types to a scheme
8func Install(scheme *runtime.Scheme) {
9 // 注册内部版本的资源
10 utilruntime.Must(core.AddToScheme(scheme))
11 // 注册外部版本的资源
12 utilruntime.Must(v1.AddToScheme(scheme))
13 // 注册资源组的版本顺序,第一个为首选版本
14 utilruntime.Must(scheme.SetVersionPriority(v1.SchemeGroupVersion))
15}
资源首选版本
一个资源组下拥有多个资源版本,在一些场景下,如不指定资源版本,则使用该资源的首选版本
Codec编解码器
Codec编解码器将etcd集群中的数据进行编解码操作。
编解码器与序列化的差别:
Serializer:序列化器。序列化:数据 -> 字符串。反序列化:字符串 -> 数据。 Codec:编解码器。可以表示数据的任何格式。serializer可以理解为codec的一种。
源码位置:vendor/k8s.io/apimachinery/pkg/runtime/interfaces.go
1// 编码
2type Encoder interface {
3 Encode(obj Object, w io.Writer) error
4 Identifier() Identifier
5}
6
7// 解码
8type Decoder interface {
9 Decode(data []byte, defaults *schema.GroupVersionKind, into Object) (Object, *schema.GroupVersionKind, error)
10}
11
12// 序列化器都实现了Encoder和Decoder,属于编解码器的一种
13type Serializer interface {
14 Encoder
15 Decoder
16}
17
18// 编解码器
19type Codec Serializer
三种序列化器
Coderc编解码器包含3种序列化器。每种都分别实现了编码、解码方法。
- jsonSerializer
- yamlSerializer
- protobufSerializer
Codec实例化
Codec实例化时,通过工厂方法,一次性将三个序列化器都进行实例化。其中:
源码位置:vendor/k8s.io/apimachinery/pkg/runtime/serializer/codec_factory.go
1// 该函数进行json和yaml两种格式的序列化器初始化
2func newSerializersForScheme(scheme *runtime.Scheme, mf json.MetaFactory, options CodecFactoryOptions) []serializerType {
3 jsonSerializer := json.NewSerializerWithOptions(
4 mf, scheme, scheme,
5 json.SerializerOptions{Yaml: false, Pretty: false, Strict: options.Strict},
6 )
7 jsonSerializerType := serializerType{
8 AcceptContentTypes: []string{runtime.ContentTypeJSON},
9 ContentType: runtime.ContentTypeJSON,
10 FileExtensions: []string{"json"},
11 EncodesAsText: true,
12 Serializer: jsonSerializer,
13
14 Framer: json.Framer,
15 StreamSerializer: jsonSerializer,
16 }
17 if options.Pretty {
18 jsonSerializerType.PrettySerializer = json.NewSerializerWithOptions(
19 mf, scheme, scheme,
20 json.SerializerOptions{Yaml: false, Pretty: true, Strict: options.Strict},
21 )
22 }
23
24 yamlSerializer := json.NewSerializerWithOptions(
25 mf, scheme, scheme,
26 json.SerializerOptions{Yaml: true, Pretty: false, Strict: options.Strict},
27 )
28 protoSerializer := protobuf.NewSerializer(scheme, scheme)
29 protoRawSerializer := protobuf.NewRawSerializer(scheme, scheme)
30
31 serializers := []serializerType{
32 jsonSerializerType,
33 {
34 AcceptContentTypes: []string{runtime.ContentTypeYAML},
35 ContentType: runtime.ContentTypeYAML,
36 FileExtensions: []string{"yaml"},
37 EncodesAsText: true,
38 Serializer: yamlSerializer,
39 },
40 {
41 AcceptContentTypes: []string{runtime.ContentTypeProtobuf},
42 ContentType: runtime.ContentTypeProtobuf,
43 FileExtensions: []string{"pb"},
44 Serializer: protoSerializer,
45
46 Framer: protobuf.LengthDelimitedFramer,
47 StreamSerializer: protoRawSerializer,
48 },
49 }
50
51 for _, fn := range serializerExtensions {
52 if serializer, ok := fn(scheme); ok {
53 serializers = append(serializers, serializer)
54 }
55 }
56 return serializers
57}
资源版本转换器
k8s中,同一资源拥有多个资源版本,允许同一资源的不同版本进行转换。Conveter资源版本转换器主要用于解决多资源版本转换问题。当版本越来越多,互相转换会成指数增长,为了解决这个问题,k8s通过内部版本实现资源多版本的统一转换。
转换器数据结构
主要存放转换函数
源码位置:vendor/k8s.io/apimachinery/pkg/conversion/converter.go
1// 转换函数结构
2// a:转换源 b:转换目标 scope处理递归调用
3type ConversionFunc func(a, b interface{}, scope Scope) error
4
5type ConversionFuncs struct {
6 untyped map[typePair]ConversionFunc
7}
8
9type Converter struct {
10 // 默认转换函数
11 conversionFuncs ConversionFuncs
12 // 自动生成的转换函数
13 generatedConversionFuncs ConversionFuncs
14
15 // 忽略转换的字段
16 ignoredConversions map[typePair]struct{}
17 ignoredUntypedConversions map[typePair]struct{}
18
19 // This is a map from a source field type and name, to a list of destination
20 // field type and name.
21 structFieldDests map[typeNamePair][]typeNamePair
22
23 // Allows for the opposite lookup of structFieldDests. So that SourceFromDest
24 // copy flag also works. So this is a map of destination field name, to potential
25 // source field name and type to look for.
26 structFieldSources map[typeNamePair][]typeNamePair
27
28 // Map from an input type to a function which can apply a key name mapping
29 inputFieldMappingFuncs map[reflect.Type]FieldMappingFunc
30
31 // Map from an input type to a set of default conversion flags.
32 inputDefaultFlags map[reflect.Type]FieldMatchingFlags
33
34 // If non-nil, will be called to print helpful debugging info. Quite verbose.
35 Debug DebugLogger
36
37 // 转换过程中获取资源种类名称
38 nameFunc func(t reflect.Type) string
39}
注册转换函数
Converter转换函数需要注册才能在k8s中使用,目前k8s支持5个注册转换函数。
- scheme.AddIgnoredConversionType:注册忽略的资源类型,不会执行转换操作
- scheme.AddConversionFuncs:注册多个Conversion Func转换函数
- scheme.AddConversionFunc:注册单个Conversion Func转换函数
- scheme.AddGeneratedConversionFunc:注册自动生成的转换函数
- scheme.AddFieldLabelConversionFunc:注册字段标签的转换函数
资源版本转换流程
- 实例化空的Scheme资源注册,并将不同版本的资源(包括内部版本)注册到资源注册表中
- 实例化源资源版本,并调用scheme.ConvertToVersion转换为内部版本的资源对象
- 同样调用scheme.ConvertToVersion将内部资源对象转换为目标资源对象