Kubernetes:kube-apiserver 之 scheme(一)
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))
}
示例中有几点需要关注。
- 通过
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 也挺重要,这里先跳过不讲。
- 调用
Scheme的AddKnownTypes和AddUnversionedTypes方法建立资源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。
最后调用 AddKnownTypeWithName 将 schema.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]
Kubernetes资源分为外部资源和内部资源。外部资源是对外可访问的,其版本为v1/v1beta1等,内部资源是Kubernetes内部访问的资源,其版本为__internal。
为什么会有内部版本呢?
为了适配版本间的 convert。
试想,如果 v1、v1apha1、v1beta1、v1beta2 要相互转换需要建立十二种转换关系:
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-apiserver 中 scheme 的应用。
2.1 注册资源
kube-apiserver 中资源的注册通过导入包的形式实现。Kubernetes 将资源拆分为三类,并且由三种 HTTP Server 负责处理这三类资源,架构如下。

关于这幅图,这里不多讲,后续会逐层介绍。
要知道的是,三类资源分别注册到 extensionsapiserver.Scheme,legacyscheme.Scheme 和 aggregatorscheme.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.AddToScheme,v1beta1.AddToScheme,v1beta2.AddToScheme 和 v1.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.AddToScheme 和 v1.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"`
}
资源声明了 json 和 protobuf tag,以便于外部访问的序列化,反序列化操作。
内部版本引用的是 kubernetes/pkg/apis/apps/types.go 中的资源定义:
type Deployment struct {
metav1.TypeMeta
metav1.ObjectMeta
Spec DeploymentSpec
Status DeploymentStatus
}
内部资源不需要被外部访问,没有 tag 声明。
到这里,kube-apiserver 关于资源的注册就差不多了。下一篇将进一步介绍 scheme 的 convert,kube-apiserver 中如何使用 scheme 及 scheme 和 object 相互转换等内容。
Kubernetes:kube-apiserver 之 scheme(一)的更多相关文章
- kubernetes的apiserver
1. API Server简介 k8s API Server提供了k8s各类资源对象(pod,RC,Service等)的增删改查及watch等HTTP Rest接口,是整个系统的数据总线和数据中心. ...
- 【云原生 · Kubernetes】apiserver高可用
个人名片: 因为云计算成为了监控工程师 个人博客:念舒_C.ying CSDN主页️:念舒_C.ying 7.1 高可用选型 ipvs+keepalived nginx+keepalived hap ...
- 一文读懂 Kubernetes APIServer 原理
前言 整个Kubernetes技术体系由声明式API以及Controller构成,而kube-apiserver是Kubernetes的声明式api server,并为其它组件交互提供了桥梁.因此加深 ...
- 【kubernetes 自带监控】 container级别cadvisor+kubelet,集群级别apiserver
apiserver https://feisky.gitbooks.io/kubernetes/components/apiserver.html kube-apiserver 支持同时提供 http ...
- Centos7 二进制安装 Kubernetes 1.13
目录 1.目录 1.1.什么是 Kubernetes? 1.2.Kubernetes 有哪些优势? 2.环境准备 2.1.网络配置 2.2.更改 HOSTNAME 2.3.配置ssh免密码登录登录 2 ...
- kubernetes CRD学习笔记
前言 最近在极客时间订阅了kubernetes的专栏,这篇文章是想记录一下自己学习CRD(custom resource definition)的过程,加深一下记忆. 准备工作 首先安装一下我们用的g ...
- kubernetes Dashboard 使用RBAC 权限认证控制
kubernetes RBAC实战 环境准备 先用kubeadm安装好kubernetes集群,[包地址在此](https://market.aliyun.com/products/56014009/ ...
- 【Kubernetes】深入解析声明式API
在Kubernetes中,一个API对象在Etcd里的完整资源路径,是由:Group(API组).Version(API版本)和Resource(API资源类型)三个部分组成的. 通过这样的结构,整个 ...
- 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 ...
- 基于Containerd安装部署高可用Kubernetes集群
转载自:https://blog.weiyigeek.top/2021/7-30-623.html 简述 Kubernetes(后续简称k8s)是 Google(2014年6月) 开源的一个容器编排引 ...
随机推荐
- macOS 系统 Kafka 快速入门
Kafka 的核心功能是高性能的消息发送与高性能的消息消费.以下是 Kafka 的快速入门教程. 下载并解压缩 Kafka 二进制代码压缩文件 打开 Kafka 官网的下载地址,可以看到不同版本的 K ...
- 《最新出炉》系列初窥篇-Python+Playwright自动化测试-6-元素定位大法-下篇
1.简介 上一篇主要是讲解我们日常工作中在使用Playwright进行元素定位的一些比较常用的定位方法的理论基础知识以及在什么情况下推荐使用.今天这一篇讲解和分享一下,在日常中很少用到或者很少见的定位 ...
- 2023-07-12:RocketMQ如何做到消息不丢失?
2023-07-12:RocketMQ如何做到消息不丢失? 答案2023-07-12: RocketMQ通过刷盘机制.消息拉取机制和ACK机制等多种方式来确保消息投递的可靠性,防止消息丢失. 1.刷盘 ...
- k8s 的特点
Kubernetes 的信条是基于自动化的.API 驱动的基础设施,同时避免组件间紧密耦合.
- 【go语言】3.1.2 接口的定义和实现
在 Go 中,接口是一种抽象类型,用来描述其他类型应该有哪些方法.它定义了一组方法,但没有实现.这些方法由其他类型实现. 接口的定义 接口定义的格式如下: type InterfaceName int ...
- vite — 超快且方便的编译工具
我们编写的代码,比如 ES6. TypeScript.react 等是不能被浏览器直接识别的,需要通过 webpack .rollup 这样的构建工具来对代码进行转换.编译. 但随着项目越来越大,需要 ...
- FreeSWITCH添加自定义endpoint之媒体交互
操作系统 :CentOS 7.6_x64 FreeSWITCH版本 :1.10.9 之前写过FreeSWITCH添加自定义endpoint的文章: https://www.cnblogs.com/ ...
- frp内网穿透环境搭建--服务端ubuntu 客户端win10
前提条件:1个公网ip服务器,例如我的是腾讯云服务器ubuntu20 下载frp软件,下的是0.33.0版本,该版本直接把软件封装成服务,能用ubuntu直接定义开机自启等 github:https: ...
- 【Unity3D】运动模糊特效
1 运动模糊原理 开启混合(Blend)后,通过 Alpha 通道控制当前屏幕纹理与历史屏幕纹理进行混合,当有物体运动时,就会将当前位置的物体影像与历史位置的物体影像进行混合,从而实现运动模糊效果 ...
- SQL 注入学习手册【笔记】
SQL 注入基础 [若本文有问题请指正] 有回显 回显正常 基本步骤 1. 判断注入类型 数字型 or 字符型 数字型[示例]:?id=1 字符型[示例]:?id=1' 这也是在尝试闭合原来的 sql ...