0. 前言

在进入 kube-apiserver 源码分析前,有一个非常重要的概念需要了解甚至熟悉的:资源注册表(scheme)。

Kubernetes 中一切皆资源,管理的是资源,创建、更新、删除的是资源。如何对庞杂的资源进行管理就成了一件大事。Kubernetes 通过引入 scheme 资源注册表,将资源信息注册到资源注册表,各个组件通过索引资源注册表实现资源的管理。

1. 介绍

直接开始阅读 kube-apiserver scheme 部分的源码是比较困难的,这里举代码示例如下:

package main

import (
"fmt" appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
) func main() {
// KnownType external
coreGV := schema.GroupVersion{Group: "", Version: "v1"}
extensionsGV := schema.GroupVersion{Group: "extensions", Version: "v1beta1"} // KnownType internal
coreInternalGV := schema.GroupVersion{Group: "", Version: runtime.APIVersionInternal} // UnversionedType
Unversioned := schema.GroupVersion{Group: "", Version: "v1"} schema := runtime.NewScheme()
schema.AddKnownTypes(coreGV, &corev1.Pod{})
schema.AddKnownTypes(extensionsGV, &appsv1.DaemonSet{})
schema.AddKnownTypes(coreInternalGV, &corev1.Pod{})
schema.AddUnversionedTypes(Unversioned, &metav1.Status{}) fmt.Println(*schema)
fmt.Println(schema.KnownTypes(coreGV))
}

示例中有几点需要关注。

  1. 通过 runtime.NewScheme 创建 scheme 实例:
func NewScheme() *Scheme {
s := &Scheme{
gvkToType: map[schema.GroupVersionKind]reflect.Type{},
typeToGVK: map[reflect.Type][]schema.GroupVersionKind{},
unversionedTypes: map[reflect.Type]schema.GroupVersionKind{},
unversionedKinds: map[string]reflect.Type{},
fieldLabelConversionFuncs: map[schema.GroupVersionKind]FieldLabelConversionFunc{},
defaulterFuncs: map[reflect.Type]func(interface{}){},
versionPriority: map[string][]string{},
schemeName: naming.GetNameFromCallsite(internalPackages...),
}
s.converter = conversion.NewConverter(nil) ...
return s
}

Scheme 的结构体有四个 field 需要重点关注:

  • gvkToType: 记录的是 schema.GroupVersionKind 和资源类型的映射关系。
  • typeToGVK: 记录的是资源类型和 schema.GroupVersionKind 的映射关系。
  • unversionedTypes: 记录的是无版本资源类型和 schema.GroupVersionKind 的映射关系。
  • unversionedKinds: 记录的是资源种类 Kind 和无版本资源类型的映射关系。

还有一个 Scheme.converter 也挺重要,这里先跳过不讲。

  1. 调用 SchemeAddKnownTypesAddUnversionedTypes 方法建立资源 Group/Version/Kind 和资源类型的相互映射关系。

AddKnownTypes 为例,函数通过反射 reflect.TypeOf(obj) 获得对象 runtime.Object 的资源类型。这里,对象是资源的接口,可实现资源和对象的相互转换。

func (s *Scheme) AddKnownTypes(gv schema.GroupVersion, types ...Object) {
s.addObservedVersion(gv)
for _, obj := range types {
t := reflect.TypeOf(obj)
if t.Kind() != reflect.Pointer {
panic("All types must be pointers to structs.")
}
t = t.Elem()
s.AddKnownTypeWithName(gv.WithKind(t.Name()), obj)
}
} func (gv GroupVersion) WithKind(kind string) GroupVersionKind {
return GroupVersionKind{Group: gv.Group, Version: gv.Version, Kind: kind}
} func (s *Scheme) AddKnownTypeWithName(gvk schema.GroupVersionKind, obj Object) {
...
t := reflect.TypeOf(obj)
s.gvkToType[gvk] = t
}

然后调用 gv.WithKind(t.Name()) 转换为 GroupVersionKind,可以看出上面反射的资源类型作为 kind 赋给 GroupVersionKind

最后调用 AddKnownTypeWithNameschema.GroupVersionKind 和资源类型写入 Scheme.gvkToType

执行代码结果如下:

{
map[
{ __internal Pod}:0xb46180
{ v1 Pod}:0xb46180
{ v1 Status}:0xb56d40
{extensions v1beta1 DaemonSet}:0xb44e40
]
map[
0xb44e40:[{extensions v1beta1 DaemonSet}]
0xb46180:[{ v1 Pod} { __internal Pod}]
0xb56d40:[{ v1 Status}]
]
map[
0xb56d40:{ v1 Status}
]
map[
Status:0xb56d40
]
map[]
map[]
0xc000124888
map[]
[{ v1} {extensions v1beta1}]
pkg/runtime/scheme.go:100
}
map[Pod:v1.Pod Status:v1.Status]
  1. Kubernetes 资源分为外部资源和内部资源。外部资源是对外可访问的,其版本为 v1/v1beta1 等,内部资源是 Kubernetes 内部访问的资源,其版本为 __internal

为什么会有内部版本呢?

为了适配版本间的 convert

试想,如果 v1v1apha1v1beta1v1beta2 要相互转换需要建立十二种转换关系:

v1 		 ---->  v1alpha1
v1 ----> v1beta1
v1 ----> v1beta2
v1alpha1 ----> v1
v1alpha1 ----> v1beta1
v1alpha1 ----> v1beta2
v1beta1 ----> v1
v1beta1 ----> v1alpha1
v1beta1 ----> v1beta2
v1beta2 ----> v1
v1beta2 ----> v1alpha1
v1beta2 ----> v1beta1

可以看到这种转换已经比较复杂了,随着资源增多,这种资源版本之间的转换会越来越复杂且难以维护。Kubernetes 通过引入内部版本 __internl 建立每种资源到 __internal 的转换,从而实现不同资源版本的相互转换。

v1 			---->  __internal
__internal ----> v1
v1alpha1 ----> __internal
__internal ----> v1alpha1
v1beta1 ----> __internal
__internal ----> v1beta1
v1beta2 ----> __internal
__internal ----> v1beta2

转换示意图如下:

可以看到,相当干净,清爽。

2. kube-apiserver scheme

第一节介绍了 scheme 结构及部分特性。继续看 kube-apiserverscheme 的应用。

2.1 注册资源

kube-apiserver 中资源的注册通过导入包的形式实现。Kubernetes 将资源拆分为三类,并且由三种 HTTP Server 负责处理这三类资源,架构如下。

关于这幅图,这里不多讲,后续会逐层介绍。

要知道的是,三类资源分别注册到 extensionsapiserver.Schemelegacyscheme.Schemeaggregatorscheme.Scheme 三种资源注册表中。

legacyscheme.Scheme 资源注册表为例。

kube-apiserver 的启动 app 包中导入 "k8s.io/kubernetes/pkg/api/legacyscheme""k8s.io/kubernetes/pkg/controlplane"包:

package app

import (
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/controlplane"
)

legacyscheme 包中创建了 Scheme 资源注册表,Codecs 编码器和 ParameterCodec 参数编码器。

# kubernetes/pkg/api/legacyscheme/scheme.go

package legacyscheme

var (
Scheme = runtime.NewScheme() Codecs = serializer.NewCodecFactory(Scheme) ParameterCodec = runtime.NewParameterCodec(Scheme)
)

controlplane 包中导入需要注册的资源组:

# kubernetes/pkg/controlplane/import_known_versions.go

package controlplane

import (
_ "k8s.io/kubernetes/pkg/apis/apps/install"
)

以注册 apps 资源组为例:

# kubernetes/pkg/apis/apps/install/install.go

package install

import (
"k8s.io/apimachinery/pkg/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/apps"
"k8s.io/kubernetes/pkg/apis/apps/v1"
"k8s.io/kubernetes/pkg/apis/apps/v1beta1"
"k8s.io/kubernetes/pkg/apis/apps/v1beta2"
) func init() {
Install(legacyscheme.Scheme)
} func Install(scheme *runtime.Scheme) {
utilruntime.Must(apps.AddToScheme(scheme))
utilruntime.Must(v1beta1.AddToScheme(scheme))
utilruntime.Must(v1beta2.AddToScheme(scheme))
utilruntime.Must(v1.AddToScheme(scheme))
utilruntime.Must(scheme.SetVersionPriority(v1.SchemeGroupVersion, v1beta2.SchemeGroupVersion, v1beta1.SchemeGroupVersion))
}

资源组中调用 apps.AddToSchemev1beta1.AddToScheme,v1beta2.AddToSchemev1.AddToScheme 函数将同一资源组不同版本的资源注册到 legacyscheme.Scheme 资源注册表中。

其中 apps.AddToScheme 注册的是内部版本的资源:

package apps

var (
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
AddToScheme = SchemeBuilder.AddToScheme
) const GroupName = "apps" var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal} func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&DaemonSet{},
&DaemonSetList{},
&Deployment{},
&DeploymentList{},
&DeploymentRollback{},
&autoscaling.Scale{},
&StatefulSet{},
&StatefulSetList{},
&ControllerRevision{},
&ControllerRevisionList{},
&ReplicaSet{},
&ReplicaSetList{},
)
return nil
}

v1beta1.AddToScheme,v1beta2.AddToSchemev1.AddToScheme 注册的是外部版本的资源,以 v1.AddToScheme 为例:

package v1

const GroupName = "apps"

var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1"}

var (
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
localSchemeBuilder = &SchemeBuilder
AddToScheme = localSchemeBuilder.AddToScheme
) func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&Deployment{},
&DeploymentList{},
&StatefulSet{},
&StatefulSetList{},
&DaemonSet{},
&DaemonSetList{},
&ReplicaSet{},
&ReplicaSetList{},
&ControllerRevision{},
&ControllerRevisionList{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil
}

需要注意的是,内部版本和外部版本注册的资源并不一样。比如 Deployment 资源,外部版本引用的是 kubernetes/vendor/k8s.io/api/apps/v1/types.go 中的资源定义:

type Deployment struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
Spec DeploymentSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`
Status DeploymentStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
}

资源声明了 jsonprotobuf tag,以便于外部访问的序列化,反序列化操作。

内部版本引用的是 kubernetes/pkg/apis/apps/types.go 中的资源定义:

type Deployment struct {
metav1.TypeMeta
metav1.ObjectMeta
Spec DeploymentSpec
Status DeploymentStatus
}

内部资源不需要被外部访问,没有 tag 声明。

到这里,kube-apiserver 关于资源的注册就差不多了。下一篇将进一步介绍 schemeconvertkube-apiserver 中如何使用 schemeschemeobject 相互转换等内容。


Kubernetes:kube-apiserver 之 scheme(一)的更多相关文章

  1. kubernetes的apiserver

    1. API Server简介 k8s API Server提供了k8s各类资源对象(pod,RC,Service等)的增删改查及watch等HTTP Rest接口,是整个系统的数据总线和数据中心. ...

  2. 【云原生 · Kubernetes】apiserver高可用

    个人名片: 因为云计算成为了监控工程师‍ 个人博客:念舒_C.ying CSDN主页️:念舒_C.ying 7.1 高可用选型 ipvs+keepalived nginx+keepalived hap ...

  3. 一文读懂 Kubernetes APIServer 原理

    前言 整个Kubernetes技术体系由声明式API以及Controller构成,而kube-apiserver是Kubernetes的声明式api server,并为其它组件交互提供了桥梁.因此加深 ...

  4. 【kubernetes 自带监控】 container级别cadvisor+kubelet,集群级别apiserver

    apiserver https://feisky.gitbooks.io/kubernetes/components/apiserver.html kube-apiserver 支持同时提供 http ...

  5. Centos7 二进制安装 Kubernetes 1.13

    目录 1.目录 1.1.什么是 Kubernetes? 1.2.Kubernetes 有哪些优势? 2.环境准备 2.1.网络配置 2.2.更改 HOSTNAME 2.3.配置ssh免密码登录登录 2 ...

  6. kubernetes CRD学习笔记

    前言 最近在极客时间订阅了kubernetes的专栏,这篇文章是想记录一下自己学习CRD(custom resource definition)的过程,加深一下记忆. 准备工作 首先安装一下我们用的g ...

  7. kubernetes Dashboard 使用RBAC 权限认证控制

    kubernetes RBAC实战 环境准备 先用kubeadm安装好kubernetes集群,[包地址在此](https://market.aliyun.com/products/56014009/ ...

  8. 【Kubernetes】深入解析声明式API

    在Kubernetes中,一个API对象在Etcd里的完整资源路径,是由:Group(API组).Version(API版本)和Resource(API资源类型)三个部分组成的. 通过这样的结构,整个 ...

  9. kubernetes之手动部署k8s 1.14.1高可用集群

    1. 架构信息 系统版本:CentOS 7.6 内核:3.10.0-957.el7.x86_64 Kubernetes: v1.14.1 Docker-ce: 18.09.5 推荐硬件配置:4核8G ...

  10. 基于Containerd安装部署高可用Kubernetes集群

    转载自:https://blog.weiyigeek.top/2021/7-30-623.html 简述 Kubernetes(后续简称k8s)是 Google(2014年6月) 开源的一个容器编排引 ...

随机推荐

  1. Kali下载安装以及基础配置

    Kali官网:Kali Linux | Penetration Testing and Ethical Hacking Linux Distribution Kali下载地址:Get Kali | K ...

  2. 2023年郑州轻工业大学校赛邀请赛mjh

    首先,很感谢老师的信任,选择我去参加此次比赛:其次,感谢wh,wyh两位队友的全程带飞.此次比赛是我第一次参加线下的比赛活动,内心难免激动与紧张. 比赛开始,队友直接找到了签到题进行解答,而我拿到了看 ...

  3. Unity UGUI的LayoutElement(布局元素)组件的介绍及使用

    Unity UGUI的LayoutElement(布局元素)组件的介绍及使用 1. 什么是LayoutElement组件? LayoutElement是Unity UGUI中的一个布局元素组件,用于控 ...

  4. Diffusers 一岁啦 !

    十分高兴 Diffusers 迎来它的一岁生日!这是令人激动的一年,感谢社区和开源贡献者,我们对我们的工作感到十分骄傲和自豪.去年,文本到图像的模型,如 DALL-E 2, Imagen, 和 Sta ...

  5. CTF比赛中Web的php伪协议类型题小结

    php协议类型 file:// - 访问本地文件系统 http:// - 访问 HTTP(s) 网址 ftp:// - 访问 FTP(s) URLs php:// - 访问各个输入/输出流(I/O s ...

  6. [django]数据的导入和导出

    除了使用mysqldump或者MySQL客户端进行数据导出,django也提供了类似的功能. 导出 # 导出整个数据库并保存为json文件 python manage.py dumpdata > ...

  7. Dirty-Pipe Linux内核提权漏洞(CVE-2022-0847)

    前言: 划水一波,哈哈,以后复现漏洞不再直接傻瓜无脑的走流程了,首先码字写加构思比较麻烦且写的不多还效率不高,现在就是当做见到了一个漏洞,在此记录一下这个漏洞,包括其来源,简单的描述,适用范围,以及其 ...

  8. html5 2.0学习

    列表定义:是一种特别的对象集合.集合:集中在一起合二为一(聚集). 聚集:多个列(信息资源)排在一起.信息资源:一堆数据,可能是字符,可能是图片. 列表分类:有序列表 无序列表  (自)定义列表  有 ...

  9. C# 使用openxml解析PPTX中的文本内容

    前言 本文讨论的仅针对微软Office 2007以后的(OOXML定义)PowerPoint文档,Office 2007以前的用二进制格式定义的(ppt格式)文档不在本文讨论范围. 一.依赖类库 本文 ...

  10. 部分 Linux 换国内源

    Centos 8 / Redhat 8 换国内源 操作步骤 先把原本的官方 yum 源 删除 或 备份 cd /etc/yum.repos.d/ 备份(Redhat 同理) rename repo r ...