专栏首页01ZOOkubernetes-api-machinery
原创

kubernetes-api-machinery

Decode 问题

http server 或者 rpc server 要解决的一个问题是:如何解析用户的请求数据,并把他反序列化为语言中的一个具体的类型。反序列化的程序需要知道具体的类型(这在收到请求的时候就已经知道一些信息了,比如 用户访问的是 EchoService,那么请求肯定是 EchoRequest,不管是 EchoRequestV1,还是 EchoRequestV2), 同时反序列化程序即 decode 程序,还需要知道 他对应的语言里面的具体结构的信息,以便新建这个结构,填充数据,提交给上层处理。以一个 EchoService 为例,decode 程序需要从用户请求(如 post http://echo ) 文本或者二进制数据中创建出 EchoRequestV1,提供给上层处理,同时这个 decode 函数需要足够通用,他返回的是可能是一个 Message Interface, 里面是 EchoRequestV1,decode 相关的细节要么通过代码生成的技术提供给 decoder,要么在 二进制或者文本请求数据(或者 header等元数据)中携带这部分信息。

我们先来看看 golang protobuf 是如何解决这个问题的。

Protobuf Unmarshal

简单来说就是根据 生成的 golang 结构体的 Field tag来做 Unmarshal

// 生成的 golang 结构体
type EchoRequest struct {
	A  string   `protobuf:"bytes,1,opt,name=A,proto3" json:"A,omitempty"`
}

// 收到请求,在 Unmarshal 过程中会调用这个函数
func (m *EchoRequest) XXX_Unmarshal(b []byte) error {
	return xxx_messageInfo_EchoRequest.Unmarshal(m, b)
}

var xxx_messageInfo_EchoRequest proto.InternalMessageInfo


// InternalMessageInfo 是 Unmarshal 相关信息的存储位置
// b 是 protocol buffer raw 数据,而a 是要 unmarshal 到的结构
// 基础库不关心具体 unmarshal 类型,始终 unmarshal 到一个 interface Message
// 实际上面到结构调用到时候 会是 EchoRequest 类型
func (a *InternalMessageInfo) Unmarshal(msg Message, b []byte) error {
	// ... 略
	err := u.unmarshal(toPointer(&msg), b)
	return err
}

func (u *unmarshalInfo) unmarshal(m pointer, b []byte) error{
    if atomic.LoadInt32(&u.initialized) == 0 {
        // 保存 unmarshal 这个类型的函数、信息到一个结构里面,加速重复的 unmarshal
		u.computeUnmarshalInfo()
	}
	
	// .... 略
	
	if fn := f.unmarshal; fn != nil {
		var err error
		// unmarshal 这个 field 这里的关键是 unmarshal 到原始 bytes 设置到对应字段的
		// offset上面去,里面比较关键的是用了 golang reflect的 StructField 
		// StructField 的 Offset 是固定的,根据 一个结构的指针的 pointer 以及 Field的
		// offset 就可以直接用指针设置 结构的某个字段内容了
		b, err = fn(b, m.offset(f.field), wire)
		// ....
	}
}

Kubernetes Scheme

kubernetes 解决这个问题的方法很类似。github.com/kubernetes/apimachinery 部分为了解决这个问题而存在的,当然 kubernetes 的问题更为复杂一些,由于支持 资源的版本,他还需要解决版本之间互相转化的问题。具体的说 apimachinery 解决的是 kubernetes 的 API Object 的 Scheme, typing, encoding, decoding, and conversion问题。

GVK(GroupVersionKind) 和 GVR(GroupVersionResource) 是 apimachinery 里面的两个重要概念. 区别是 GVK 是一个 Object 概念,而 GVR 代表一个 Http Path

Http Path:

/apis/batch/v2alpha1/(namespaces/<name>)jobs
       |     |                           | 
    Apigroup Version                  Resource
    
/apis/extensions/v1alpha1/jobs 
/apis/batch/v2alpha1/jobs 
/apis/batch/v1/jobs

------------------------------------------ 
对象:

apiVersion: batch/v2alpha1
kind: Job
metadata:
  name: aaa
  namespace: tiems
spec:
    ...
    
apiVersion: v1/v2alpha1/v1alpha1 彼此之间可以转换 => "storage version" (比如v1)  => Etcd

反序列化使用 api.Scheme + gvk,而 gvk 中的信息可以从 request中获取

gvk := schema.GroupVersionKind{Group: "batch", Version: "v2alpha1", Kind: "Job"} 
obj := api.Scheme.New(gvk)
codec := api.Codecs.LegacyCodec(gvk.GroupVersion())
codec.Decode(reqBody, gvk, obj)

type Job struct {  
    metav1.TypeMeta     ---> type TypeMeta struct { Kind string; APIVersion string }
    metav1.ObjectMeta   ---> type ObjectMeta struct { Name string...}
    Spec JobSpec
    Status JobStatus 
}
 

代码流程分析

  • API groups install, 包括了 InstallLegacyAPI,InstallAPIs, LegacyAPI 指core group 在 /api/v1, 而named groups 在 /apis/$GROUP_NAME/$VERSION
  • (a *APIInstaller) Install 里面安装了各种 http path的处理流程
  • 比如 "POST" /apis/extensions/v1alpha1/jobs => restfulCreateNamedResource
  • endpoints/handlers/create.go: createHandler => 反序列化语句为 decoder.Decode(body, &defaultGVK, original)
  • defaultGVK 是默认 GVK 信息来自 path (APIGroup), 但是还是以 body 里面的 gvk 为准
  • 如 runtime/serializer/json/json.go: Decode 语句,会先运行 s.meta.Interpret(data), 从中或取真正的 GVK, 信息有不足的时候才会用 defaultGVK 补充
  • runtime.UseOrCreateObject(s.typer, s.creater, *actual, into)
  • 几个重要的函数 在 init的时候就执行了 var Scheme = runtime.NewScheme(); var Codecs = serializer.NewCodecFactory(Scheme); var ParameterCodec = runtime.NewParameterCodec(Scheme) 这里是全局的 Scheme. Codecs. ParameterCodec 正因为各种 type 都注册到了(用 scheme.AddKnownTypes ) 这个 scheme, 所以 Codecs 才有所有类型的信息以便于做序列化 反序列化
  • 本质 runtime.UseOrCreateObject 会调用 (s *Scheme) New(kind schema.GroupVersionKind) -> s.gvkToTypekind 从gvkToType去找类型,如果没有注册,会报错 NotRegisteredErr
  • 创建完成对象之后 则调用 caseSensitiveJsonIterator.Unmarshal 反序列数据到这个对象, 这个就比较简单了,使用 golang 的 unmarshal 函数就可以,kubernetes 里面用了 json-iterator
  • NewCodecFactory 存储了各种 content-type 以及各种 type 对应的 反序列化工具
  • Scheme 同时还注册了各种版本类型互相转换的信息, default变量信息,通过DefaultingFunc,AddConversionFunc 函数注册 localSchemeBuilder.Register(addDefaultingFuncs, addConversionFuncs)
image

应用

了解 api-machinery 对于编写 crd controller 或者 aggregation apiserver很重要,由于api-machinery 以及 controller 逻辑的复杂性,有很多辅助工具可以帮助生成很多相关的代码,比如 kubebuilder, wrangler, 这里我们只关注 api-machinery 相关的代码,可以找到需要上面流程中提到的注册,转换代码. 比如这里

image

参考1

参考2

原创声明,本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

登录 后参与评论
0 条评论

相关文章

  • 在 Go 中使用 Kubernetes 对象

    通常,在某些情况下,我们需要通用的方法去使用 Kubernetes 资源对象,而不是编写代码来处理特定类型。 比如,如下一些简单的用例参考场景:

    Luga Lee
  • Kubernetes 1.18即将发布:OIDC发现、Windows节点支持,还有哪些新特性值得期待?

    根据Kubernetes官方计划,明日Kubernetes 1.18版本即将发布!

    灵雀云
  • Kubebuilder介绍:使用CRD构建Kubernetes API的SDK

    我们如何才能让MySQL、Spark和Cassandra这样的应用程序来管理自己,就像Kubernetes Deployments和Pods一样?我们如何配置这...

    CNCF
  • CNCF网研会:Kubernetes1.11的新增功能

    Ihor Dvoretskyi, Jun Du, Chris O’Haver, Michael Taufen, Hemant Kumar

    CNCF
  • Kubernetes 1.15:25项增强功能,kubeadm喜提新logo

    上周,Kubernetes正式发布了Kubernetes 1.15版本,这也是Kubernetes团队在2019年发布的第二个版本!Kubernetes 1.1...

    灵雀云
  • Kubernetes 1.18 福履将之

    Kubernetes 1.18即将发布!在发布了1.17的小版本之后,1.18变得日益健壮并充满了新颖性。关于新版本的介绍从哪里开始?有一些新功能,例如API ...

    zouyee
  • 简介Kube-liveboard——一款可视化操作工具

    Kubernetes这个自动化容器操作的开源平台,这些操作包括部署,调度和节点集群间扩展。因其具有对容器集群的方便部署,自动伸缩扩容,以及便于维护等功能和特性,...

    CNCF
  • Kubernetes 1.15:可扩展性和持续改进

    https://github.com/kubernetes/sig-release/blob/master/releases/release-1.15/rele...

    CNCF
  • 在 Kubernetes 实施混沌工程 —— Chaos Mesh 原理分析与控制面开发

    作者:黄涵(Mayo Cream)[1],CNCF TAG Security 成员,云原生社区贡献者。

    CNCF
  • K8S 1.10 | 技术角度解读 10 大模块更新

    DevOps时代
  • 如何为 Kubernetes 定制特性

    Kubernetes 是非常复杂的集群编排系统,然而哪怕包含丰富的功能和特性,因为容器的调度和管理本身就有较高的复杂性,所以它无法满足所有场景下的需求。虽然 K...

    我是阳明
  • K8s 系列(二) - K8s PR 怎样才能被 merge?

    K8s 作为一个开源项目,鼓励全世界的参与者积极贡献力量,包括 kubernetes/kubernetes 主项目、kubernetes/website、kub...

    astraw99
  • Golang任务队列machinery使用与源码剖析(一)

    异步任务,是每一位开发者都遇到过的技术名词,在任何一个稍微复杂的后台系统中,异步任务总是无法避免的,而任务队列由于其松耦合、易扩展的特性,成为了实现异步任务的可...

    netkiddy
  • K8S 1.12 重磅发布|全面解读 15 个重大功能更新

    | 为 | 容 | 器 | 技 | 术 | 而 | 生 |

    CNCF
  • 一文告诉你 K8s PR (Pull Request) 怎样才能被 merge?

    作者:腾讯云云巢团队研发工程师 王成 | 导语 Pull Request 本质上是一种软件的合作方式,它是将涉及不同功能的代码,纳入主干的一种流程。这个过程中,...

    腾源会
  • 什么是容器、微服务与服务网格?

    如你所知,已经有很多关于服务网格的资料,但这是另外一篇。是的!但是为什么会有这篇文章呢?因为我想给你们一些不同的视角,他们希望服务网格在10年前就已经存在,远早...

    用户5927304
  • Go语言中使用K8s API及一些常用API整理

    在进入代码之前,理解k8s的go client项目是对我们又帮助的。它是k8s client中最古老的一个,因此具有很多特性。 Client-go 没有使用Sw...

    常见_youmen
  • Golang任务队列machinery使用与源码剖析(二)

    在Golang任务队列machinery使用与源码剖析(一)一文中,我们主要对golang中任务队列machinery的设计结构以及具体模块的功能与源码实现进行...

    netkiddy
  • 在 Kubernetes 实施混沌工程—— Chaos Mesh® 原理分析与控制面开发

    Chaos Mesh® 是由 TiDB 背后的 PingCAP 公司开发,运行在 Kubernetes 上的混沌工程(Chaos Engineering)系统。...

    PingCAP

扫码关注云+社区

领取腾讯云代金券