Kubernetes:kube-apiserver 之 scheme(一)

2.2 资源 convert

上篇说到资源版本之间通过内部版本 __internal 进行资源转换。这里进一步扩展介绍资源转换内容,以加深理解。

同样以例子开始,通过 kubectlapps/v1beta1/Deployment 转换为 apps/v1/Deployment

apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 1
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myapp:1.0.0
resources:
limits:
memory: "128Mi"
cpu: "500m"
ports:
- containerPort: 80

执行 kubectl convert -f v1beta1Deployment.yaml --output-version=apps/v1,输出 apps/v1/Deployment 资源配置:

apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: myapp
name: myapp
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 2
...
status: {}

这条命令背后,kubectl 将转换命令组成 client-go 识别的 Restful API 消息,通过 client-go 发给 kube-apiserverkube-apiserver 根据事先注册的转换函数 convertapps/v1beta1/Deployment 转换为 apps/__internal/Deployment,接着将 apps/__internal/Deployment 转换为 apps/v1/Deployment

代码示例如下:

v1beta1Deployment := &appsv1beta1.Deployment{
TypeMeta: metav1.TypeMeta{
Kind: "Deployment",
APIVersion: "apps/v1beta1",
},
} // v1beta1 -> __internal
targetVersion := schema.GroupVersion{Group: "apps", Version: "__internal"}
objInternal, err := scheme.ConvertToVersion(v1beta1Deployment, targetVersion)
if err != nil {
panic(err)
} // __internal -> v1
objV1, err := scheme.ConvertToVersion(objInternal, appsv1.SchemeGroupVersion)
if err != nil {
panic(err)
}

scheme.ConvertToVersion 函数转换对象到指定资源版本。

2.2.1 kube-apiserver 资源版本 convert

资源版本的转换首先需要注册版本转换函数到 scheme,只有注册过的版本才能进行资源版本转换。

kube-apiserver 通过导入包的方式注册转换函数。

# kubernetes/cmd/kube-apiserver/app/server.go
package app import (
"k8s.io/kubernetes/pkg/controlplane"
...
) # kubernetes/pkg/controlplane/import_known_versions.go
package controlplane import (
_ "k8s.io/kubernetes/pkg/apis/apps/install"
...
)

kube-apiserver 启动的 app 包导入 controlplane 包,controlplane 继续导入资源组的安装包。以 apps 资源组为例,查看资源组下资源转换函数是怎么注册的。

资源组下的每个外部资源版本都有 zz_generated.conversion.go 文件,该文件由 conversion-go 自动生成,文件中定义了外部资源到内部资源的相互转换。

# kubernetes/pkg/apis/apps/
apps/
/v1
/zz_generated.conversion.go
/v1beta1
/zz_generated.conversion.go
/v1beta2
/zz_generated.conversion.go

v1 版本为例,zz_generated.conversion.go 中注册资源转换函数:

package v1

func init() {
localSchemeBuilder.Register(RegisterConversions)
} func RegisterConversions(s *runtime.Scheme) error {
if err := s.AddGeneratedConversionFunc((*v1.DeploymentSpec)(nil), (*apps.DeploymentSpec)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_DeploymentSpec_To_apps_DeploymentSpec(a.(*v1.DeploymentSpec), b.(*apps.DeploymentSpec), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*apps.DeploymentSpec)(nil), (*v1.DeploymentSpec)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_apps_DeploymentSpec_To_v1_DeploymentSpec(a.(*apps.DeploymentSpec), b.(*v1.DeploymentSpec), scope)
}); err != nil {
return err
}
...
}

可以看到,函数 Convert_v1_DeploymentSpec_To_apps_DeploymentSpec 注册了 v1/DeploymentSpec__internal/DeploymentSpec 的资源转换,Convert_apps_DeploymentSpec_To_v1_DeploymentSpec 注册了 __internal/DeploymentSpecv1/DeploymentSpec

关于资源版本转换基本告一段落。下面进一步介绍,在 kube-apiserver 是怎么使用 scheme 资源注册表的。

2.3 使用资源注册表 scheme

这一节会进入 kube-apiserverscheme 是如何使用的。需要说明的是,kube-apiserver 非常复杂,这里并不介绍启动流程,建立 Restful API 等内容,仅从 scheme 的视角看 kube-apiserver

# kubernetes/cmd/kube-apiserver/app/server.go

func CreateServerChain(config CompletedConfig) (*aggregatorapiserver.APIAggregator, error) {
notFoundHandler := notfoundhandler.New(config.ControlPlane.GenericConfig.Serializer, genericapifilters.NoMuxAndDiscoveryIncompleteKey)
apiExtensionsServer, err := config.ApiExtensions.New(genericapiserver.NewEmptyDelegateWithCustomHandler(notFoundHandler))
if err != nil {
return nil, err
}
...
}

CreateServerChain 函数中创建 API 扩展服务(APIExtensionServer)API 核心服务(KubeAPIServer)API 聚合服务(AggregatorServer)。这里以 API 扩展服务(APIExtensionServer)scheme 是如何使用的。

进入 config.ApiExtensions.New(genericapiserver.NewEmptyDelegateWithCustomHandler(notFoundHandler)) 方法。

# kubernetes/vendor/k8s.io/apiserver/pkg/server/genericapiserver.go
// APIGroupInfo 中保存资源注册表 Scheme
func NewDefaultAPIGroupInfo(group string, scheme *runtime.Scheme, parameterCodec runtime.ParameterCodec, codecs serializer.CodecFactory) APIGroupInfo {
return APIGroupInfo{
PrioritizedVersions: scheme.PrioritizedVersionsForGroup(group),
VersionedResourcesStorageMap: map[string]map[string]rest.Storage{},
OptionsExternalVersion: &schema.GroupVersion{Version: "v1"},
Scheme: scheme,
ParameterCodec: parameterCodec,
NegotiatedSerializer: codecs,
}
} # kubernetes/vendor/k8s.io/apiextensions-apiserver/pkg/apiserver/apiserver.go
// 将 apiserver.Scheme 传入 NewDefaultAPIGroupInfo
func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget) (*CustomResourceDefinitions, error) {
...
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(apiextensions.GroupName, Scheme, metav1.ParameterCodec, Codecs)
...
if err := s.GenericAPIServer.InstallAPIGroup(&apiGroupInfo); err != nil {
return nil, err
}
} # kubernetes/vendor/k8s.io/apiserver/pkg/server/genericapiserver.go
func (s *GenericAPIServer) InstallAPIGroup(apiGroupInfo *APIGroupInfo) error {
return s.InstallAPIGroups(apiGroupInfo)
} func (s *GenericAPIServer) InstallAPIGroups(apiGroupInfos ...*APIGroupInfo) error {
...
for _, apiGroupInfo := range apiGroupInfos {
if err := s.installAPIResources(APIGroupPrefix, apiGroupInfo, openAPIModels); err != nil {
return fmt.Errorf("unable to install api resources: %v", err)
}
}
} // 在 GenericAPIServer.getAPIGroupVersion 方法中将 apiGroupInfo 转换解析为 apiGroupVersion
// apiGroupVersion 中保存资源注册表 scheme
func (s *GenericAPIServer) installAPIResources(apiPrefix string, apiGroupInfo *APIGroupInfo, typeConverter managedfields.TypeConverter) error {
for _, groupVersion := range apiGroupInfo.PrioritizedVersions {
apiGroupVersion, err := s.getAPIGroupVersion(apiGroupInfo, groupVersion, apiPrefix)
if err != nil {
return err
} ...
discoveryAPIResources, r, err := apiGroupVersion.InstallREST(s.Handler.GoRestfulContainer)
}
} # kubernetes/vendor/k8s.io/apiserver/pkg/endpoints/groupversion.go
func (g *APIGroupVersion) InstallREST(container *restful.Container) ([]apidiscoveryv2beta1.APIResourceDiscovery, []*storageversion.ResourceInfo, error) {
installer := &APIInstaller{
group: g,
prefix: prefix,
minRequestTimeout: g.MinRequestTimeout,
} apiResources, resourceInfos, ws, registrationErrors := installer.Install()
...
} # kubernetes/vendor/k8s.io/apiserver/pkg/endpoints/installer.go
func (a *APIInstaller) Install() ([]metav1.APIResource, []*storageversion.ResourceInfo, *restful.WebService, []error) {
for _, path := range paths {
apiResource, resourceInfo, err := a.registerResourceHandlers(path, a.group.Storage[path], ws)
...
}
...
} func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storage, ws *restful.WebService) (*metav1.APIResource, *storageversion.ResourceInfo, error) {
fqKindToRegister, err := GetResourceKind(a.group.GroupVersion, storage, a.group.Typer)
if err != nil {
return nil, nil, err
}
} func GetResourceKind(groupVersion schema.GroupVersion, storage rest.Storage, typer runtime.ObjectTyper) (schema.GroupVersionKind, error) {
object := storage.New()
fqKinds, _, err := typer.ObjectKinds(object)
if err != nil {
return schema.GroupVersionKind{}, err
}
...
}

函数链很长,最后在函数 GetResourceKind 中调用 typer.ObjectKinds(object) 获取对象 object 的类型。其中,typer 是一个获取对象类型的接口,对应的实例是 scheme,实际是调用 scheme.ObjectKinds 方法获取对象类型。

注意,能获取对象类型是因为该对象提前注册在资源注册表 scheme 中,否则将会报 no kind is registered for the type xxx 错误。

示例代码如下。

package main

import (
"fmt" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/apis/core"
) func main() {
pod := &core.Pod{
TypeMeta: metav1.TypeMeta{
Kind: "Pod",
},
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"name": "foo"},
},
} coreGV := schema.GroupVersion{Group: "", Version: "v1"}
schema := runtime.NewScheme()
schema.AddKnownTypes(coreGV, &core.Pod{}) gvk, _, err := schema.ObjectKinds(pod)
if err != nil {
fmt.Println(err)
} fmt.Println(gvk)
}

2.3.1 资源对象 runtime.Object

上面说到资源对象,这里有必要在扩展下资源对象 runtime.Objectruntime.Object 是一个接口,资源需要实现该接口,通过接口方法可以实现资源和接口的相互转换。示意图如下。

示意代码如下:

package main

import (
"reflect" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubernetes/pkg/apis/core"
) func main() {
pod := &core.Pod{
TypeMeta: metav1.TypeMeta{
Kind: "Pod",
},
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"name": "foo"},
},
} obj := runtime.Object(pod) pod2, ok := obj.(*core.Pod)
if !ok {
panic("unexpected runtime object")
} if !reflect.DeepEqual(pod, pod2) {
panic("unexpected")
}
}

介绍完资源注册表 scheme,后续将继续介绍 kube-apiserver 是怎么启动,注册 RESTful API,如何实现认证,鉴权等操作,未完待续...


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

  1. Kubernetes学习之路(二十)之K8S组件运行原理详解总结

    目录 一.看图说K8S 二.K8S的概念和术语 三.K8S集群组件 1.Master组件 2.Node组件 3.核心附件 四.K8S的网络模型 五.Kubernetes的核心对象详解 1.Pod资源对 ...

  2. Kubernetes学习之路(二十五)之Helm程序包管理器

    目录 1.Helm的概念和架构 2.部署Helm (1)下载helm (2)部署Tiller 3.helm的使用 4.chart 目录结构 5.chart模板 6.定制安装MySQL chart (1 ...

  3. Kubernetes学习之路(二十四)之Prometheus监控

    目录 1.Prometheus概述 2.Prometheus部署 2.1.创建名称空间prom 2.2.部署node_exporter 2.3.部署prometheus-server 2.4.部署ku ...

  4. kubernetes学习与实践篇(二) kubernetes1.5 的安装和集群环境部署

    kubernetes 1.5 的安装和集群环境部署 文章转载自:http://www.cnblogs.com/tynia/p/k8s-cluster.html 简介: Docker:是一个开源的应用容 ...

  5. Kubernetes 服务部署最佳实践(二) ——如何提高服务可用性

    引言 上一篇文章我们围绕如何合理利用资源的主题做了一些最佳实践的分享,这一次我们就如何提高服务可用性的主题来展开探讨. 怎样提高我们部署服务的可用性呢?K8S 设计本身就考虑到了各种故障的可能性,并提 ...

  6. Kubernetes的Controller进阶(十二)

    一.Controller 既然学习了Pod进阶,对于管理Pod的Controller肯定也要进阶一下,之前我们已经学习过的Controller有RC.RS和Deployment,除此之外还有吗,如果感 ...

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

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

  8. kubernetes之Ingress发布Dashboard(二)

    1.什么是Dashboard Dashboard 是基于网页的 Kubernetes 用户界面. 你可以使用 Dashboard 将容器应用部署到 Kubernetes 集群中,也可以对容器应用排错, ...

  9. kubernetes学习笔记之十二:资源指标API及自定义指标API

    第一章.前言 以前是用heapster来收集资源指标才能看,现在heapster要废弃了从1.8以后引入了资源api指标监视 资源指标:metrics-server(核心指标) 自定义指标:prome ...

  10. Kubernetes集群部署之二CA证书制作

    创建TLS证书和秘钥 kubernetes 系统的各组件需要使用 TLS 证书对通信进行加密,本文档使用 CloudFlare 的 PKI 工具集 cfssl 来生成 Certificate Auth ...

随机推荐

  1. Jmeter压测实战:Jmeter二次开发之自定义函数

    1 前言 Jmeter是Apache基金会下的一款应用场景非常广的压力测试工具,具备轻量.高扩展性.分布式等特性.Jmeter已支持实现随机数.计数器.时间戳.大小写转换.属性校验等多种函数,方便使用 ...

  2. SQL ERVER 表转化为C#实体(SQL 代码)

    本文推出SqlServer表转化为实体的sql代码 在VS中有可以自带生成实体类的快捷操作,但是生成的代码比较杂乱,很多东西都是不需要的,一个一个去敲又很浪费时间,关键太无聊了 在闲暇之余写一份代码供 ...

  3. 怎样优雅地增删查改(一):从0开始搭建Volo.Abp项目

    @ 目录 项目介绍 模块化 由框架实现的 需要实现的 创建项目 创建业务模块 配置引用和依赖 配置DbContext 创建实体和Dto 配置AutoMapper 软件系统中数据库或者持久层的基本操作功 ...

  4. 使用 Sa-Token 实现不同的登录模式:单地登录、多地登录、同端互斥登录

    一.需求分析 如果你经常使用腾讯QQ,就会发现它的登录有如下特点:它可以手机电脑同时在线,但是不能在两个手机上同时登录一个账号. 同端互斥登录,指的就是:像腾讯QQ一样,在同一类型设备上只允许单地点登 ...

  5. 基于GPT搭建私有知识库聊天机器人(四)问答实现

    前文链接: 基于GPT搭建私有知识库聊天机器人(一)实现原理 基于GPT搭建私有知识库聊天机器人(二)环境安装 基于GPT搭建私有知识库聊天机器人(三)向量数据训练 在前面的文章中,我们介绍了如何使用 ...

  6. Map集合_HashMap_TreeMap_等_小记

    Map是一种依照键值对数据存储元素的容器. Map中的元素是两个对象,一个对象作为键,一个对象作为值.一个键(key)和它对应的值构成map集合中的一个元素.Map集合的数据结构只跟键有关,键不可以重 ...

  7. 【转载】老男孩读PCIe

    目录 老男孩读PCIe之一:从PCIe速度说起 老男孩读PCIe之二:PCIe拓扑结构 老男孩读PCIe之三:PCIe分层结构 老男孩读PCIe之四:TLP类型 老男孩读PCIe之五:TLP结构 老男 ...

  8. Linux: rsyslog.conf 配置

    refer to: https://www.debian.org/doc/manuals/debian-handbook/sect.syslog.en.html 日志子系统 Each log mess ...

  9. FlinkSQL类型系统

    类型有什么作用, 类型可以提供编译期检查, 避免到运行期才报错. 类型 首先Flink中自己定义了一套类型, 有LogicalType和DataType两个表示 LogicalType Logical ...

  10. 如何选择最适合您的Excel处理库?

    摘要:本文由葡萄城技术团队于博客园原创并首发.转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者. 引言 GcExcel和POI是两个应用于处理Excel文件的技 ...