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. Python web 框架对比:Flask vs Django

    哈喽大家好,我是咸鱼 今天我们从几个方面来比较一些现在流行的两个 python web 框架--Flask 和 Django,突出它们的主要特性.优缺点和简单案例 到最后,大家将更好地了解哪个框架更适 ...

  2. python笔记:第二章基本数据类型

    基本数据类型 Python中的数据类型可以分为五大类:字符串.数字.容器.布尔.None 1.字符串 可以使用单引号或双引号创建字符串,可以用加号将两个字符串合并 name = '小明' age = ...

  3. java解析CSV文件(getCsvData 解析CSV文件)

    首先需要下载opencsv的jar包 <!-- https://mvnrepository.com/artifact/com.opencsv/opencsv --> <depende ...

  4. FHQ-Treap的详细图解

    第一部分 按值分裂的 FHQ-Treap 按值分裂的 FHQ-Treap 的典型例题是P3369 [模板]普通平衡树. 思路 FHQ-Treap 是什么? FHQ-Treap 是二叉搜索树的一种. 比 ...

  5. 在映客的虚拟KTV里唱了一首“爱你”

      如果你突然打了个喷嚏 那一定就是我在想你 如果半夜被手机吵醒 啊~那是你的虚拟 KTV 在响起 2022 年 5 月 18 日,映客 App 上线了业内首个元宇宙 K 歌玩法「全景 K 歌」,给用 ...

  6. you-get的使用

    转载自: 利用Python下载:You-Get的安装及使用方法 - 宁佳兵 - 博客园   宁佳兵 所谓的光辉岁月,并不是后来闪耀的日子,而是无人问津时,对梦想的偏执. 博客园 首页 标签 GitHu ...

  7. Python 潮流周刊第 11 期(2023-07-15)

    查看全文:Python潮流周刊#11:如何使用 Golang 运行 Python 代码? 文章&教程 1.使用 Golang 和 Docker 运行 Python 代码 2.答案在代码中:&q ...

  8. C++笔记(1)——类的基本概念知识

    C#和C++的部分面向对象方式类似,只记录不同点. 定义类与关键字: 下面是C++类的定义方式与C++关键字public和private的表达方式: class Box { private: // 私 ...

  9. PE结构总览

    pe文件经历了从16位系统到32位系统的过度.因此32系统下的每一个PE文件都可以在16位系统下运行. 16位系统下的PE结构 在16位系统下,PE结构可以大致分为两个部分:DOS头和一些其他数据 # ...

  10. 重温C#中的值类型和引用类型

    在C#中,数据类型分为值类型和引用类型两种. 引用类型变量存储的是数据的引用,数据存储在数据堆中,而值类型变量直接存储数据.对于引用类型,两个变量可以引用同一个对象.因此,对一个变量的操作可能会影响另 ...