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. OC项目集成flutter后,编译卡死

    oc项目集成flutter的项目,本来运行的好好的,突然就再Xcode编译就是卡死的情况, 先运行一下flutter的项目,再编译Xcode的项目,就好了

  2. 一文了解 io.LimitedReader类型

    1. 引言 io.LimitedReader 提供了一个有限的读取功能,能够手动设置最多从数据源最多读取的字节数.本文我们将从 io.LimitedReader 的基本定义出发,讲述其基本使用和实现原 ...

  3. 了解前端中的BFC(块级格式化上下文)

    BFC(块级格式化上下文) 什么是BFC 指的是一个块级渲染作用域,该区域内拥有一套完整的规则来约束块级盒子的布局,且与区域外部无关. 为什么要使用BFC 当一个盒子不设置高度,当其中的子元素都浮动时 ...

  4. 【SpringBoot】注解

    Controller - @RestController - @RequestMapping("/path") Controller内方法 @GetMapping("/p ...

  5. java发送http请求(jquery发送http请求,前后端看这一篇文章够了,很完整)

    为什么写这篇博客? 1.目前很多系统使用了微服务架构,那么各个微服务之间进行内部通信一般采用http协议的方式,springcloud中提供了ribbon,feign,openFeign等组件. 但是 ...

  6. 【技术实战】Vue技术实战【四】

    需求实战一 效果展示 代码展示 <template> <ARow> <ACol> <a-statistic-countdown :value="de ...

  7. 关于"覆盖问题”的反思

    [HAOI2007]覆盖问题 题目描述 某人在山上种了N棵小树苗.冬天来了,温度急速下降,小树苗脆弱得不堪一击,于是树主人想用一些塑料薄膜把这些小树遮盖起来,经过一番长久的思考,他决定 用3个L*L的 ...

  8. Python操纵数据库:peewee

    模块名:peewee 安装方法: pip install peewee 官方教程:http://docs.peewee-orm.com/en/latest/

  9. java反射newInstance()带删除线的问题

    从java9开始,newInstance()方法不建议使用导致idea自动画了条删除横线 解决方法: //改用getDeclaredConstructor().newInstance() Object ...

  10. http头部字段Origin和Access-Control-Allow-Origin解决请求跨域

    Http协议中请求头和响应头携带了很多信息,其中 请求头 Origin,响应头 Access-Control-Allow-Origin 与跨域有关. 为了验证跨域,要将客户端和服务端分配在不同端口,这 ...