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月) 开源的一个容器编排引 ...
随机推荐
- python笔记:第四章使用字典
1.1 概述 说白了就是键值对的映射关系 不会丢失数据本身关联的结构,但不关注数据的顺序 是一种可变类型 格式:dic = {键:值, 键:值} 键的类型:字典的键可以是任何不可变的类型,如浮点数,字 ...
- CMU15-445 Project4 Concurrency Control心得
一.概述 过瘾!过瘾!过瘾!P4 真过瘾!写 P3 的博客时我说过"感觉自己在数据库方面真正成长了",但写完 P4 之后最大的感受就是,我终于理解了 andy 在第一课说过的&qu ...
- python: 获取整个字段转换成列表,并将列表转换成字典
获取整个字段转换成列表,并将列表转换成字典
- 用React仿钉钉审批流
引言 这几天帮朋友忙,用了一周时间,高仿了一个钉钉审批流.这个东西会有不少朋友有类似需求,就分享出来,希望能有所帮助.为了方便朋友的使用,设计制作的时候,尽量做到节点配置可定制,减少集成成本.如果您的 ...
- 基于md5加密的模拟管理员登录系统
import os import pandas as pd def md5(string:str=''): import hashlib md5 = hashlib.md5() ...
- LabVIEW图形化的AI视觉开发平台(非NI Vision)VI简介
前言 今天想和大家分享的是:仪酷LabVIEW AI视觉工具包的VI简介,如介绍内容有误,欢迎各位朋友们帮忙纠正~ 一.AI视觉工具包VI简介 已经安装好的AI工具包位于程序框图-函数选板-Addon ...
- quarkus依赖注入之十三:其他重要知识点大串讲(终篇)
欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇概览 本篇是<quarkus依赖注入> ...
- virtualbox克隆虚拟机
1.选择要克隆的虚拟机 2.设置克隆机的名称和存放位置 3.选择克隆类型 4.克隆结果
- Python 基础面试第二弹
1. 解释下Python中的面向对象,以及面向对象的三大特点: 在Python中,面向对象编程(Object-Oriented Programming,简称OOP)是一种编程范式,它将数据和操作数据的 ...
- role
角色权限管理改造方案 # 为什么需要角色 现有的权限方案 .net后台权限管理 在后台类中配置,权限 = 一级菜单:二级菜单:三级菜单: 通过在view模板中判断是否有权限显示菜单 后端通过权限配 ...