背景

众所周知,kubernetes利用etcd存储API对象,例如Pod、Deployment、StatefulSet等等。笔者认为kubernetes这种API对象的设计方案当前来看非常先进,基于etcd实现对象存储是这个设计方案的关键基础。笔者和很多读者都有这样一些需求:

  1. 自己设计的系统也希望采用etcd;
  2. 系统也想采用kubernetes设计的API对象思想,这些对象是为系统设计的,与kubernetes无关;

像kubernetes一样实现一套自己的代码工作量是惊人的,即便是照抄一份难度也是非常大的,毕竟这里面涉及到的大量的代码依赖。所以笔者认为能够复用kubernetes已经实现的代码,并在此基础上扩展自定义的API对象,这会是一个比较有意思的事情。所以本文标题为站在巨人的肩膀上,kubernetes是一个巨人,有大量的优秀设计和代码实现可以借鉴和引用。需要注意的是,本文的示例代码笔者不会做过多详细的原理解释,如果读者需要了解具体实现原理,可以阅读笔者的《深入剖析kubernetes的API对象类型定义》、《etcd在kubernetes中的应用》以及《深入剖析kubernetes apiserver的存储实现》。

本文代码依赖如下:

k8s.io/apimachinery v0.0.0-20191017185446-6e68a40eebf9
k8s.io/apiserver v0.0.0-20191018030144-550b75f0da71

实现

首先,先建一个项目,笔者姑且把这个项目命名为customeapi,并且在这个项目根目录创建cmd和pkg两个目录。然后就是自定义API对象的了,笔者把api对象定义在customeapi/pkg/api/test/v1包中(读者需要注意kubernetes中api都是定义在一个单独的项目k8s.io/api中,本文为了演示方便定义在了自己的项目中),如下代码所示:

// 代码源自customeapi/pkg/api/test/v1/types.go
package v1import (metav1 "k8s.io/apimachinery/pkg/apis/meta/v1""k8s.io/apimachinery/pkg/runtime"
)
// 定义自定义API对象Custom.
type Custom struct {// 继承metav1.TypeMeta和metav1.ObjectMeta才实现了runtime.Object,这样Custom对象// 的yaml的格式就像如下:// kind: Custom// apiVersion: test/v1// metadata: //   labels: //     name: custommetav1.TypeMeta   `json:",inline"`metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`// 为了演示方便,Custom对象的规格和状态都定义为空类型,读者根据自己的业务进行设计Spec              CustomSpecStatus            CustomStatus
}
type CustomSpec struct{}
type CustomStatus struct{}// DeepCopyObject()是必须要实现的,这是runtime.Objec定义的接口,否则编译就会报错。读者需要注意,
// kubernetes的API对象的DeepCopyObject()函数是代码生成工具生成的,本文的示例是笔者自己写的。
func (in *Custom) DeepCopyObject() runtime.Object {if in == nil {return nil}out := new(Custom)*out = *inreturn out
}
var _ runtime.Object = &Custom{}

好了,自定义对象已经定义完了,现在需要让codec能够识别Custom这个类型,否则Custom在写入etcd前序列化会失败,这部分代码实现如下:

// 代码源自customeapi/pkg/api/test/v1/register.go
package v1import ("k8s.io/apimachinery/pkg/runtime""k8s.io/apimachinery/pkg/runtime/schema"
)// 定义自定义类型组名,因为是测试例子,所以笔者把组名定义为test,这样test/Custom才是类型全称
const GroupName = "test"// 定义自定义类型的组名+版本,即test v1
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1"}// 程序初始化部分需要调用AddToScheme()来实现自定义类型的注册,具体的实现在addKnownTypes()
var (SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)AddToScheme   = SchemeBuilder.AddToScheme
)// 把笔者上面定义的Custom添加到scheme中就完成了类型的注册,就是这么简单。读者需要注意,类型注册
// 其实是一个比较复杂的过程,kubernetes把这部分实现全部交给了scheme,把简单的接口留给了使用者。
func addKnownTypes(scheme *runtime.Scheme) error {scheme.AddKnownTypes(SchemeGroupVersion,&Custom{},)return nil
}

最后就是要把这个对象存入etcd中,笔者把它实现自cmd/main.go文件中,如下代码所示:

// 代码源自customeapi/cmd/main.go
package mainimport ("fmt""context"testv1 "customapi/pkg/api/test/v1""k8s.io/apimachinery/pkg/runtime""k8s.io/apimachinery/pkg/runtime/serializer""k8s.io/apiserver/pkg/storage/storagebackend""k8s.io/apiserver/pkg/storage/storagebackend/factory"
)func main() {// 构造scheme,然后调用刚刚实现的注册函数实现自定义API对象的注册。scheme := runtime.NewScheme()testv1.AddToScheme(scheme)// 注册了自定义API对象,就可以构造codec工厂了,在通过codec工厂构造codec。所谓的codec就是// 自定义API对象的序列化与反序列化的实现。cf := serializer.NewCodecFactory(scheme)codec := cf.LegacyCodec(testv1.SchemeGroupVersion)// 有了codec就可以创建storagebackend.Config,他是构造storage.Interfaces的必要参数config := storagebackend.NewDefaultConfig("", codec)// 笔者在本机装了etcd,所以把etcd的地址填写为本机地址,这样方便测试config.Transport.ServerList = append(config.Transport.ServerList, "127.0.0.1:2379")// 创建storage.Interfaces对象,storage.Interfaces是apiserver对存储的抽象,这样我们// 就可以像apiserver一样在etcd上操作自己定义的对象了。storage, destroy, err := factory.Create(*config)if nil != err {fmt.Printf("%v\n", err)}// 构造Custom对象custom := testv1.Custom{}// 把Custom对象写入etcdif err = storage.Create(context.Background(), "test", &custom, &custom, 0); nil != err {fmt.Println(err)}// 把写入的Custom对象打印出来看看结果if data, err := runtime.Encode(codec, &custom); nil == err {fmt.Printf("%s\n", string(data))}// 必要的析构函数destroy()
}

好了,至此笔者就完成了利用apiserver已经实现的storage.Interface接口存储自定义对象。编译运行后的结果如下:

I1018 11:10:21.782324   25634 client.go:357] parsed scheme: "endpoint"
I1018 11:10:21.782665   25634 endpoint.go:68] ccResolverWrapper: sending new addresses to cc: [{127.0.0.1:2379 0  <nil>}]
I1018 11:10:21.784459   25634 once.go:66] CPU time info is unavailable on non-linux or appengine environment.
I1018 11:10:21.787267   25634 client.go:357] parsed scheme: "endpoint"
I1018 11:10:21.787306   25634 endpoint.go:68] ccResolverWrapper: sending new addresses to cc: [{127.0.0.1:2379 0  <nil>}]
{"kind":"Custom","apiVersion":"test/v1","metadata":{"creationTimestamp":null},"Spec":{},"Status":{}}

再通过etcdctl --endpoints=http://localhost:2379 get /test 验证结果如下:

{"kind":"Custom","apiVersion":"test/v1","metadata":{"creationTimestamp":null},"Spec":{},"Status":{}}

大功告成,惊不惊喜?意不意外?只编写少量代码就可以写出跟kubernetes一样操作API对象的程序,关键在于这个对象类型我们是自定义的,并且存储在了我们自己部署的etcd上。

至于对象的get、watch笔者就不一一演示了,读者可以自己动手试试

总结

上面的内容只是证明了利用kubernetes现成的代码实现一个能像kubernetes一样在etcd上操作对象的程序是可行的。但是使用上多少还是有一些限制,比如:

  1. 对象类型必须继承metav1.TypeMeta和metav1.ObjectMeta,metav1.TypeMeta还算是非常通用,但是metav1.ObjectMeta就显得有些冗余或者可能不够;
  2. 从例子上看对象序列化是json格式,如果和对象需要通过rpc(比如grpc)与其他系统交互,对象需要protobuf序列化该怎么办?

其实这部分在笔者的《深入剖析kubernetes的API对象类型定义》以及《深入剖析kubernetes apiserver的存储实现》可以找到答案,感兴趣的读者不妨看看。此时此刻,笔者有一种到站在kubernetes这个巨人肩膀上的感觉。

站在巨人的肩膀上-像kubernetes一样用etcd存储自定义对象相关推荐

  1. 站在巨人的肩膀上“思考”问题,重在思考而不是拿来主义

    米老师按:觉得值得讨论的小文!我还要认真地想一想 主题:围绕职责链设计模式-计算收费有效时间博客展开讨论 参与人: 讨论时间: 讨论内容 这次讨论主要分为以下几点: 一.职责链模式应用于机房收费系统计 ...

  2. 站在巨人的肩膀上看Servlet——原来如此(更适合初学者认识Servlet)

    前言: 有段时间没更新博客了,这段时间因为要准备考试,考完试后又忙了一阵别的事,一直没能静下心来写博客.大学考试真是越来越恶心了,各种心酸,那酸爽,够味.不过还好,马上就要大三了,听大三学长学姐说大三 ...

  3. “站在巨人的肩膀上”

    站在巨人的肩膀上需要拥有巨人的实力 容易错误理解成:"盗取"巨人的成果并漠视了巨人

  4. 【Visual C++】游戏开发笔记三十五 站在巨人的肩膀上:游戏引擎导论

    看到在留言中很多朋友提到不太清楚DirectX与游戏引擎的区别的问题,在这里浅墨就专门把自己对游戏引擎的一些理解写成一篇文章,作为我们<Visual C++游戏开发>专栏的游戏引擎导论,也 ...

  5. 3小时快速入门数学建模竞赛-建模技巧2:追根溯源,站在巨人的肩膀上

    我们要检索哪些信息?和怎样获得快速.准确的信息?这两个问题. 首先回答第一个问题,我们需要检索哪些信息?我们从我们需要求解的内容入手,请看思维导图和系统框图,我们需要求解35年寿命期内的发电总量.经济 ...

  6. 知识就是力量,让机器站在巨人的肩膀上

    作者:微软亚洲研究院 闫峻博士 知识是什么?抽象来说,是人类对世界的认知,对宏观及微观世界客观规律的总结.具体来说,大到牛顿定律.量子力学中的科学发现,小到自家孩子的生日.太太做饭常用的酱油颜色,都是 ...

  7. 站在巨人的肩膀上看世界,MySQL初步使用语法

    站在巨人的肩膀上看世界,MySQL初步使用  MySQL是一个常用的数据库,对于他的使用,我们需要尽量的了解它,但是掌握初步的语法,也可以勉强使用它,这都归功于它的研发者们.正是因为这些大佬,写好了软 ...

  8. 游戏开发笔记三十五 站在巨人的肩膀上:游戏引擎导论

    本系列文章由zhmxy555(毛星云)编写,转载请注明出处. 文章链接: http://blog.csdn.net/zhmxy555/article/details/8250057 作者:毛星云(浅墨 ...

  9. 站在巨人的肩膀上:C#编写WindowsService

    本文将为大家详细介绍如何用C#编写一个系统服务,当然肯定不是教大家从头写,而是在一个搭建好的框架上进行自我创作! 一.项目整体架构 本项目的开发是针对某设备的开启异常情况进行邮件提醒,针对监控到的未开 ...

最新文章

  1. 论坛报名 | “她时代”来临,AI科技女性将如何影响世界
  2. 碾压谷歌量子霸权!中国量子计算原型机 九章 问世,比超级计算机快百万亿倍...
  3. 何恺明团队最新力作SimSiam:消除表征学习“崩溃解”,探寻对比表达学习成功之根源...
  4. 将div垂直居中放置在另一个div中[重复]
  5. python.freelycode.com-快速提示-使用Modin加速Pandas
  6. 【PHP】循环 调用第三方API (curl ),性能优化
  7. Android JNI原理分析
  8. 深度学习:优化器工厂,各种优化器介绍,numpy实现深度学习(一)
  9. spark.yarn.archive 的正确设置方法
  10. Deep Belief Networks
  11. 提交表单到弹出窗口 Submit a form into a popup
  12. 分屏总屏计算机电缆,分屏加总屏电缆DJYVP计算机电缆14x2x0.75
  13. java程序设计图形题_面向对象与Java程序设计基础题目:设计一个程序可以一计算平面图形的面积和立体图形的体积。1.使用interface关键...
  14. YJX_Driver_031_再谈SSDT_HOOK驱动保护原理
  15. pandas pivot 计算占比_pandas使用9:如何处理时间序列数据
  16. Mybatis教程-实战
  17. 记录数据库面试题及答案21~41
  18. 太阳当空照-Windows服务化方式instsrv与srvany
  19. 一文搞懂深度学习所有工具——Anaconda、CUDA、cuDNN
  20. 人物回眸效果怎么用Vegas设置

热门文章

  1. 正点原子ESP8266通过Air-LINK模式无法接入机智云的解决方案
  2. freemarker 应用模板导出Execle(解决生成弹出版本不适合)
  3. Python3.9.7英文版软件安装教程
  4. SQLZOO JOIN答案
  5. Python入门100个实例(27)——len函数
  6. 项目 http 升级到 https 全程总结
  7. 油烟净化器电源坏了是换还是修呢?
  8. JMeter Assertions
  9. 猎头阶段性成长第二篇
  10. 亲影:那些尘封的美好值得再回味