前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Kubernetes Operator 测试面面观

Kubernetes Operator 测试面面观

作者头像
CNCF
发布2019-12-03 13:07:47
1.5K1
发布2019-12-03 13:07:47
举报
文章被收录于专栏:CNCF

软件测试是一门工程技术,更是一门艺术。维护良好、质量过硬的测试用例不仅能大幅提高开发者的工作幸福感,也是企业对外提供优质软件服务的重要基础。在这篇文章中,才云工程师 gaocegege 将分享团队在 Kubernetes Operator 测试方案上的一些心得。

发布 | 才云 Caicloud

作者 | gaocegege

本文将介绍一些比较成熟的 Kubernetes Operator 测试方案与方法,分析目前对 Kubernetes Operator 进行测试的最佳实践。

01

单元测试

首先,让我们把镜头对准单元测试。单元测试又称模块测试,它是针对程序模块(软件设计的最小单位)进行正确性检验的测试工作,也被视为是软件质量的第一道保障。

Kubernetes 的做法

在 tf-operator 中,我们采取了跟 Kubernetes 内置的 controller 类似的测试方案(例子可见 job_controller_test.go)。

如下面的代码所示,我们通过 Fake KubeConfig 创建了 Fake Clientset 和 Informer。之后,我们利用 Indexer,将测试的数据注入到 Informer 的 Cache 中,同时把 Informer 的 Sync 状态置为 AlwaysReady。最后,我们用到了 Fake PodControl 和 ServiceControl,这一操作使得我们不会真正在 Kubernetes 中创建对象,而是只进行一个记录。

随后,我们将状态更新的函数也 Fake 掉,将其赋值到内存的一个对象中,以便在后续的测试中进行状态比对。通过手动调用 SyncTFJob,我们可以利用之前自己手动构造的 Cache 进行状态的 Sync。

最后,就是根据构造对象和实际更新后对象的比对,来判断 Operator 在 Sync 的过程中是否达到了期望状态。

这一方法利用了 Kubernetes 的一些机制,绕过了对 Kubernetes API Server 和其他组件的依赖,直接利用 Informer 针对 Operator 的代码逻辑进行测试,可以测试单次 Sync 过程中,Operator 的工作是否符合预期。

etcd-operator 的做法

etcd-operator 是问世最久的 Operator 之一,Operator 这一模式也是由 CoreOS 提出的,了解 etcd-operator 有助于我们以史为镜。

目前 etcd-operator 在实现中仍没有像主流的 Operator 一样,采用 Work Queue 的方式来避免阻塞问题,而是在 Informer 中直接进行处理,而它的处理逻辑则被统一写成了:

因此,在做单元测试时,这种方法相对容易些,可以直接构造 Event 对象来进行测试。

Kubebuilder-generated Operator 的做法

严格意义上,Kubebuilder 的测试有别于传统单元测试,它背后依赖的是 "sigs.k8s.io/controller-runtime/pkg/envtest".Environment。

在运行时,它会启动一个真正的 API Server 和 etcd,随后把 CRD 注册到 Scheme 中并把 Operator 运行起来。但值得注意的是,它不会启动 Controller Manager,这也意味着来自 API Server 的关于 Kubernetes 资源的事件不会真正被处理。

相比其他测试方法,它有许多比较明显的不同。首先,它需要运行一个真正的 API Server 和 etcd 来做对象存储。这就意味着在测试时可以使用真正的 Clientset 对 API Server 进行各种请求。

其次,它会运行一个真正的 Operator,而不只是通过手动调用 Sync 过程的方式进行测试。如下方代码所示,inner 这一对象就是真正的 Operator 的逻辑,而这一函数对其进行了再次封装,利用一个没有缓冲的 Channel 对其进行了执行的控制。

根据 Golang 的内存模型,不带缓冲的 Channel 的接收操作 happens-before 相应 Channel 的发送操作完成。利用这一特性,在同一个测试用例中,测试的对象可以被多次 Sync,每次 Sync 的状态都可以被检查。如果需要检查其中的 reconcile.Result 的值,如 Requeue 等,也可以改动这部分的逻辑来扩展。

这样做的优势是可以在单个测试中多次 Sync,并且依赖真实的 API Server,直接简单地利用 Clientset 进行操作。而它的劣势也在于依赖真实的 API Server,由于有些 CI 系统对多进程支持不好,真正在持续集成环境下运行时会有各种各样的问题。

02

端到端测试

端到端测试是利用真实的外部组件,将系统当做黑盒,站在终端用户的角度进行的测试。这里的“真实的组件”指的是 Kubernetes 还有一些外部依赖。相比单元测试,端到端测试需要依赖一个真实的 Kubernetes 集群,同时由于其黑盒属性,我们就有了更多不同选型。

Kubernetes 的做法

首先介绍下 Kubernetes 自身 Controller 的 e2e 测试是如何做的。

Kubernetes 的端到端测试依赖一个关键的抽象,也就是 Framework。Framework 负责把需要的 Client 创建好,同时,它也会为测试创建一个 Namespace。

当前的测试用例会在这个 Namespace 下运行,因此它从设计上就避免了并行测试可能引起的冲突,是一个非常有价值的特性。这也使得 Kubernetes 的测试用例可以并行地运行。

其实 Kubernetes 的端到端测试有许多值得学习的地方,包括其整体的原则、哲学、设计和实现。限于篇幅原因,这里不过多介绍。

Operator-SDK generated Operator 的做法

Operator-SDK 的做法和 etcd-operator 的做法类似,和 Kubernetes 的做法也有异曲同工之妙,相当于是基于 Kubernetes 社区的实现做了一个新的抽象和改写。

首先它需要设置 Main Entry:

这一函数会根据传入的 Kubeconfig、ProjectRoot 等参数,创建出 CRD 和 Operator。Operator 可以运行在集群外,也可以以 Pod 的方式运行在集群内。

随后,集群就可以进行测试了。我们可以完全把它当做黑盒,把 Clientset 当做 kubectl,将端到端的黑盒测试自动化。不过这里值得一提的是,这一方式不能通过 go test 直接进行测试。因为在 MainEntry 中,它会依赖一些参数,而这些参数会涉及一些预处理的逻辑。

举个例子,Operator-SDK 会把 Operator 所有的 YAML 文件合并成一个 YAML,然后再把这一临时的 YAML 文件的路径作为参数传递给后续的命令。

因此 Operator-SDK 是利用 operator-sdk test local 这一命令先进行预处理,然后再把处理好的参数传递给 go test 命令的。这一方式对用户并不是那么友好,必须依赖 operator-sdk 才能发起测试。但它默认支持一个用例一个 Namespace,与 Kubernetes 测试时的行为类似。

Kubebuilder-generated Operator 的做法

由于 Kubebuilder 生成的测试原本就依赖一个真实的 API Server 和 etcd,所以我们只要再创建出其他 Kubernetes 的组件就可以了。

但是,如果已经有正在运行的 Kubernetes 集群,那么我们就可以利用 UseExistingCluster,通过运行的集群进行测试。

相比前几种方式,这种方法可以真正检查所有资源的状态。而之前的方法只有 API Server 的运行中,是做不到对状态的检查的,因为事件不会被 Kubernetes Controller Manager 处理,因此状态更新无法进行。

tf-operator 的做法

因为端到端测试是黑盒测试,只要能够利用 Kubernetes 的 API 进行请求就可以完成,因此 tf-operator 的端到端测试是用 Python 实现的。

其实不只是端到端测试,tf-operator 的构建也不是用 Make 或者 Bazel 做的,而是用 Python 实现的,它们其实都是历史遗留问题。

不过这也证明了,只要能够解决集群部署、CRD 以及 Operator 的安装,用什么语言都可以做 Operator 的端到端测试。

03

总结

除了以上内容,Operator 的单元测试还存在多种不同的实现方案,本文只是基于才云内部实践,列举了几种较为主流且高效的方法

对于单元测试,Kubernetes 和 tf-operator 的做法能够细粒度地构造测试用例,同时检查 Sync 的过程可否满足期望。

etcd-operator 的做法为单元测试提供了一种新思路,通过对 Operator 进行更高层次的抽象,针对高层次的抽象进行单元测试,我们可以避免手动利用 Indexer 构造测试场景。

kubebuilder 生成 Operator 实现的测试并不是传统意义上的单元测试。它利用了真实的 API Server,在测试时可以利用 Client 获取真实的资源。但由于没有 Controller 的支持,所以对于不少需要依赖一些 Kubernetes 自身资源状态来更新自己状态的 CRD 而言,没办法进行状态的检查。这一问题在前面的方法中不存在。

而对于端到端测试,我列举的所有方案基本都是利用 Client 来对已经创建好的集群进行端到端的黑盒测试,它们的区别主要在于运行集群的方式

Kubernetes 和 Operator-SDK 的做法利用 Framework 这一抽象来部署集群环境。

而 Operator-SDK 由于需要部署 CRD 和 Operator,因此基于 Kubernetes 原本的理念做了一些修改,支持从本地或者利用 Deployment 的方式部署 Operator 以便测试。

kubebuilder 采用了类似单元测试的方法,利用 controller-runtime 提供的抽象和能力,在运行时注册 CRD,在测试代码中运行 Operator 的逻辑,依赖已经部署好的标准的 Kubernetes 集群进行端到端测试。但它在默认情况下,没有每个测试使用一个 Namespace 的支持,需要用户自行实现这样的逻辑。

不同的测试选型适合于不同的 Operator,在测试时,大家可以根据 Operator 的特点来确定具体的测试方案。目前来看,社区并没有一个 One for all 的方案

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019/07/18 11:26:05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 CNCF 微信公众号,前往查看

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

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档