Kubernetes:kube-apiserver 之 scheme(二)
接 Kubernetes:kube-apiserver 之 scheme(一)。
2.2 资源 convert
上篇说到资源版本之间通过内部版本 __internal 进行资源转换。这里进一步扩展介绍资源转换内容,以加深理解。
同样以例子开始,通过 kubectl 将 apps/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-apiserver。kube-apiserver 根据事先注册的转换函数 convert 将 apps/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/DeploymentSpec 到 v1/DeploymentSpec。
关于资源版本转换基本告一段落。下面进一步介绍,在 kube-apiserver 是怎么使用 scheme 资源注册表的。
2.3 使用资源注册表 scheme
这一节会进入 kube-apiserver 看 scheme 是如何使用的。需要说明的是,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.Object。runtime.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(二)的更多相关文章
- Kubernetes学习之路(二十)之K8S组件运行原理详解总结
目录 一.看图说K8S 二.K8S的概念和术语 三.K8S集群组件 1.Master组件 2.Node组件 3.核心附件 四.K8S的网络模型 五.Kubernetes的核心对象详解 1.Pod资源对 ...
- Kubernetes学习之路(二十五)之Helm程序包管理器
目录 1.Helm的概念和架构 2.部署Helm (1)下载helm (2)部署Tiller 3.helm的使用 4.chart 目录结构 5.chart模板 6.定制安装MySQL chart (1 ...
- Kubernetes学习之路(二十四)之Prometheus监控
目录 1.Prometheus概述 2.Prometheus部署 2.1.创建名称空间prom 2.2.部署node_exporter 2.3.部署prometheus-server 2.4.部署ku ...
- kubernetes学习与实践篇(二) kubernetes1.5 的安装和集群环境部署
kubernetes 1.5 的安装和集群环境部署 文章转载自:http://www.cnblogs.com/tynia/p/k8s-cluster.html 简介: Docker:是一个开源的应用容 ...
- Kubernetes 服务部署最佳实践(二) ——如何提高服务可用性
引言 上一篇文章我们围绕如何合理利用资源的主题做了一些最佳实践的分享,这一次我们就如何提高服务可用性的主题来展开探讨. 怎样提高我们部署服务的可用性呢?K8S 设计本身就考虑到了各种故障的可能性,并提 ...
- Kubernetes的Controller进阶(十二)
一.Controller 既然学习了Pod进阶,对于管理Pod的Controller肯定也要进阶一下,之前我们已经学习过的Controller有RC.RS和Deployment,除此之外还有吗,如果感 ...
- 【云原生 · Kubernetes】apiserver高可用
个人名片: 因为云计算成为了监控工程师 个人博客:念舒_C.ying CSDN主页️:念舒_C.ying 7.1 高可用选型 ipvs+keepalived nginx+keepalived hap ...
- kubernetes之Ingress发布Dashboard(二)
1.什么是Dashboard Dashboard 是基于网页的 Kubernetes 用户界面. 你可以使用 Dashboard 将容器应用部署到 Kubernetes 集群中,也可以对容器应用排错, ...
- kubernetes学习笔记之十二:资源指标API及自定义指标API
第一章.前言 以前是用heapster来收集资源指标才能看,现在heapster要废弃了从1.8以后引入了资源api指标监视 资源指标:metrics-server(核心指标) 自定义指标:prome ...
- Kubernetes集群部署之二CA证书制作
创建TLS证书和秘钥 kubernetes 系统的各组件需要使用 TLS 证书对通信进行加密,本文档使用 CloudFlare 的 PKI 工具集 cfssl 来生成 Certificate Auth ...
随机推荐
- O2OA(翱途)开发平台如何在流程表单中使用基于Vue的ElementUI组件?
本文主要介绍如何在O2OA中进行审批流程表单或者工作流表单设计,O2OA主要采用拖拽可视化开发的方式完成流程表单的设计和配置,不需要过多的代码编写,业务人员可以直接进行修改操作. 在流程表单设计界面, ...
- 万字长文解析最常见的数据库恢复算法: ARIES
万字长文解析最常见的数据库恢复算法: ARIES 首发地址: https://mp.weixin.qq.com/s/Kc13g8OHK1h_f7eMlnl4Aw Introduction 上图中为基于 ...
- 聊聊JVM虚方法表和方法调用
作者:小牛呼噜噜 | https://xiaoniuhululu.com 计算机内功.源码解析.科技故事.项目实战.面试八股等更多硬核文章,首发于公众号「小牛呼噜噜」 大家好,我是呼噜噜,好久没更新文 ...
- Django容易被遗忘却无比重要的框架默认文件介绍及使用方法
在Python Web开发领域,Django框架的地位犹如璀璨的明星,其全面.高效和安全的特性使其在全球范围内广受欢迎.本文将全面解析Django框架的默认文件,并深入探讨每个文件及其组成的意义和用途 ...
- 通用权限系统-Dozer对象转换
Dozer对象转换 介绍 Dozer 是一个 Java Bean 到 Java Bean 的映射器,它可以递归地将数据从一个对象复制到另一个.通常情况下,这些Java Beans将是不同的复杂类型. ...
- 图扑 AR 技术应用与管理:施工建造、机柜扫描、办公室导航解决方案
随着科技的不断革新和创新,越来越多的行业开始迎来数字化时代的变革.建筑行业作为人类历史上最重要的产业之一,在数字化转型方面同样也在不断推进.图扑软件结合 AR 技术的应用,为建筑行业带来了更加便捷高效 ...
- html实现原生table并设置表格边框的两种方式
虽然第三方表格插件多不胜数,但是很多场景还是需要用到原生<table>,掌握html原生table的实现方法,是前端开发的必备技能.例如:print-js打印.html2canvas生成图 ...
- http请求其他接口的utils
首先在pom中加入 关于http需要的jar包 <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpcor ...
- 【阅读笔记】低照度图像增强-《Fast efficient algorithm for enhancement of low lighting video》
本文介绍的是一种比较实用的低照度图像增强效果很好的方法,Xuan Dong论文<Fast efficient algorithm for enhancement of low lighting ...
- 代码随想录算法训练营第四天|力扣24.两两交换链表节点、力扣19.删除链表的倒数第N个结点、力扣面试02.07链表相交、力扣142.环形链表
两两交换链表中的节点(力扣24.) dummyhead .next = head; cur = dummyhead; while(cur.next!=null&&cur.next.ne ...