为每个人提供Kubernetes端到端测试
作者:Patrick Ohly(英特尔)
越来越多过去是Kubernetes组件的一部分,现在搬到在Kubernetes之外开发。例如,存储驱动程序曾经被编译成Kubernetes二进制文件,然后被转移到主机上的独立Flexvolume二进制文件中,现在作为容器存储接口(Container Storage Interface,CSI)驱动程序提供,这些驱动程序部署在Kubernetes集群内部的pod中。
这对于处理此类组件的开发者来说是一个挑战:如何在这样的外部组件上对Kubernetes集群进行端到端(E2E)测试?用于测试Kubernetes本身的E2E框架具有所有必要的功能。但是,尝试在Kubernetes之外使用它很困难,只有通过仔细选择大量依赖项的正确版本才能实现。在Kubernetes 1.13中,E2E测试变得更加简单。
这篇博客文章总结了Kubernetes 1.13的变化。对于CSI驱动程序开发者,它将涵盖使存储测试可用于测试第三方CSI驱动程序。如何使用它们将基于两个Intel CSI驱动程序显示:
测试这些驱动程序是大多数这些增强功能的主要动机。
E2E概述
E2E测试包括几个阶段:
- 实现测试套件。这是本篇博文的主要焦点。Kubernetes E2E框架是用Go编写的。它依赖于Ginkgo来管理测试,而断言(assertion)则依赖于Gomega。这些工具支持“行为驱动开发”,它描述了“规范”中的预期行为。在这篇博客文章中,“test”用于引用个别Ginkgo.It规范。测试使用client-go与Kubernetes集群进行交互。
- 启动测试集群。像kubetest这样的工具可以帮忙。
- 针对该群集运行E2E测试套件。Ginkgo测试套件可以使用ginkgo工具运行,也可以使用go test进行正常的Go测试。没有任何参数,Kubernetes E2E测试套件将基于环境变量(如KUBECONFIG)连接到默认集群,与kubectl完全相同。 Kubetest还知道如何运行Kubernetes E2E套件。
Kubernetes 1.13中的E2E框架增强功能
所有以下增强都遵循相同的基本模式:它们使E2E框架在Kubernetes之外更有用和更容易使用,而不会改变原始Kubernetes e2e.test二进制文件的行为。
拆分供应商支持
使用Kubernetes <= 1.12的E2E框架很困难的主要原因是依赖于特定于提供者的SDK,这些SDK使用了大量的软件包。只是编译它已经不简单。
许多这些软件包仅在某些测试中需要。例如,测试预配置卷的安装必须首先通过一些非Kubernetes API,直接与特定存储后端通信,以管理员相同的方式配置这样的卷。
现在有尝试从核心Kubernetes中删除特定于云供应商的测试。在PR#68483中采用的方法可以看作是朝着这个目标迈出的一步:不是立即剥离代码并打破所有依赖它的测试,所有特定于云供应商的代码都被移动到test/e2e/framework/providers下的可选包中。然后,E2E框架通过每个供应商包单独实现的接口访问它。
E2E测试套件的作者决定将哪些软件包导入测试套件。然后通过--provider命令行标志激活供应商支持。1.13和1.14中的Kubernetes e2e.test二进制文件仍然支持与1.12中相同的供应商程序。也可以不包含任何包,这意味着只有通用供应上程序可用:
- “skeleton”:通过Kubernetes API访问集群,没有别的
- “local”:跟“skeleton”差不多,但是另外kubernetes/kubernetes/cluster中的脚本可以在运行测试套件后通过ssh检索日志
外部文件
测试可能必须在运行时读取其他文件,例如.yaml清单。但是Kubernetes e2e.test二进制文件应该是可用的并且完全独立,因为这简化了发布和运行它。Kubernetes构建系统中的解决方案是使用go-bindata将test/e2e/testing-manifests下的所有文件链接到二进制文件中。E2E框架过去对go-bindata的输出有很强的依赖性,现在bindata支持是可选的。通过testfiles包访问文件时,将从不同的源检索文件:
- 相对于使用--repo-root参数指定的目录
- 零个或多个bindata块
测试参数
e2e.test二进制文件采用控制测试执行的附加参数。2016年,开始尝试用Viper配置文件替换所有E2E命令行参数。但是这种努力停滞不前,这使得开发者没有明确指导他们应该如何处理特定于测试的参数。
v1.12中的方法是将所有标志添加到中央test/e2e/framework/test_context.go,这对于独立于框架开发的测试不行。自PR#69105以来,建议使用普通标志包在其自己的源代码中定义其参数。标记名称必须是分层的,点分隔不同的级别,例如my.test.parameter,并且必须是唯一的。标志包强制执行唯一性,第二次注册标志时会发生混乱。新的配置包简化了多个选项的定义,这些选项存储在单个结构中。
总而言之,这就是现在如何处理参数:
- 测试包中的init代码定义了测试和参数。实际参数值尚不可用,因此测试定义不能使用它们。
- 测试套件的init代码解析参数和配置文件(可选)。
- 测试运行并可以使用参数值。
但是,最近有人指出,比较可取且有可能不将测试设置公开为命令行标志,只能通过配置文件设置它们。关于这个有一个开放的bug和一个待定的PR。
Viper支持得到了增强。与供应商支持一样,它是完全可选的。它通过导入viperconfig包被拉入e2e.test二进制文件,并在解析正常的命令行标志后调用它。这已经实现,以便当标志出现在Viper配置文件中时,也可以设置所有可以通过命令行标志设置的变量。例如,Kubernetes v1.13 e2e.test二进制文件接受--viper-config=/tmp/my-config.yaml,该文件将my.test.parameter设置为具有此内容的值:my: test: parameter: value
在较旧的Kubernetes版本中,该选项只能从当前目录加载文件,后缀必须省略,实际上只能通过这种方式设置几个参数。请注意Viper的一个限制仍然存在:它通过匹配已知标志的配置文件条目,而不会发出有关未知配置文件条目的警告,从而不会检测到错别字。Kubernetes的更好的配置文件解析器仍在开发中。
从.yaml创建项目清单
在Kubernetes 1.12中,有一些支持从.yaml文件加载单个项目,但是然后创建该项目必须通过手写代码完成。现在,框架提供新方法加载具有多个项目的.yaml文件、修补这些项目(例如,设置为当前测试创建的命名空间)以及创建它们。这目前用于为每个测试重新部署CSI驱动程序,这些驱动程序来自完全相同的.yaml文件,这些文件也用于通过kubectl进行部署。如果CSI驱动程序支持以不同的名称运行,则测试完全独立并且可以并行运行。
但是,重新部署驱动程序会降低测试执行速度,并且不会涵盖针对驱动程序的并发操作。更现实的测试场景是在启动测试集群时部署驱动程序一次,然后针对该部署运行所有测试。最终,Kubernetes E2E测试将转移到该模型,一旦更清楚如何扩展测试集群的启动,包括安装CSI驱动程序等其他实体。
Kubernetes 1.14推出的增强功能
重用存储测试
能够使用Kubernetes之外的框架可以构建自定义测试套件。但是没有测试的测试套件仍然没用。一些现有的测试,特别是用于存储的测试,可以应用于树外组件。感谢Masaki Kimura所做的工作,Kubernetes 1.13中的存储测试被定义为可以针对不同的驱动程序多次实例化它们。
但历史有重复的习惯。与供应商程序一样,定义这些测试的程序包也提取了所有树内存储后端的驱动程序定义,这反过来又拉取了比所需更多的附加程序包。这在Kubernetes 1.14进行了修复。
跳过不支持的测试
某些存储测试依赖于群集的功能(如在支持XFS的主机上运行)或驱动程序(如支持块卷)。在测试运行时检查这些条件,导致在不满意时跳过测试。好的是这记录解释了为什么测试没有运行。
开始测试很慢,特别是当它必须首先部署CSI驱动程序时,在其他情况下也差不多。在快速集群上测量为测试创建命名空间的时间为5秒,并且会产生大量噪声测试输出。本来可以解决这个问题,通过跳过不支持的测试的定义,然后报告为什么测试甚至不是测试套件的一部分变得棘手。这种方法已不被考虑,而是采用重新组织存储测试套件的方式,以便在进行更昂贵的测试设置步骤之前首先检查条件。
更易读的测试定义
同样的PR还将测试重写,接近传统的Ginkgo测试,测试用例及其局部变量在一个函数中。
测试外部驱动程序
构建自定义E2E测试套件仍然是相当多的工作。将在Kubernetes 1.14测试档案中分发的e2e.test二进制文件将能够测试已安装的存储驱动程序,而无需重建测试套件。有关详细说明,请参阅本自述文件。
E2E测试套件HOWTO
测试套件初始化
第一步是设置定义测试套件的必要样板代码。在Kubernetes E2E中,这是在e2e.go和e2e_test.go文件中完成的。它也可以在e2e_test.go文件中完成。Kubernetes在e2e_test.go中导入所有各种供应商程序、树内测试、Viper配置支持和bindata文件。e2e.go控制实际执行,包括一些集群准备和指标收集。
一个更简单的起点是来自PMEM-CSI的e2e_[test].go文件。它不使用任何供应商程序,没有Viper,没有bindata,只导入存储测试。
与PMEM-CSI一样,OIM会丢弃所有额外功能,但有点复杂,因为它将自定义集群启动直接集成到测试套件中,在这种情况下非常有用,因为一些额外的组件必须在主机端运行。通过直接在E2E二进制文件中运行它们,使用dlv进行交互式调试变得更加容易。
这两个CSI驱动程序都遵循Kubernetes示例,并使用test/e2e目录作为其测试套件,但也可以使用任何其他目录和其他文件名。
添加E2E存储测试
测试由导入测试套件的包定义。E2E测试唯一特有的是,它们使用framework.NewDefaultFramework实例化一个framework.Framework指针(通常称为f)。此变量在每个测试的BeforeEach中重新初始化,并在AfterEach中释放。它在运行时有一个f.ClientSet和f.Namespace(并且只在运行时!),可以由测试使用。
PMEM-CSI存储测试导Kubernetes存储测试套件,并为必须已安装在测试集群中的PMEM-CSI驱动程序设置一个供应测试实例。存储测试套件更改存储类以使用不同的文件系统类型运行测试。由于此要求,存储类是从.yaml文件创建的。
解释框架中可用的所有各种实用方法超出了本博文的范围。阅读现有测试和框架的源代码是一个很好的入门方法。
提供代码
即使消除了许多不必要的依赖关系,提供Kubernetes代码仍然不是一件容易的事。k8s.io/kubernetes并不意味着包含在其他项目中,也没有以dep等工具理解的方式定义其依赖关系。其他k8s.io包应包含在内,但不遵循语义版本控制或不标记任何版本(k8s.io/kube-openapi,k8s.io/utils)。
PMEM-CSI使用dep。它的Gopkg.toml文件是一个很好的起点。它启用了修剪(默认情况下未在dep中启用)并将某些项目锁定到与所使用的Kubernetes版本兼容的版本上。当dep没有选择兼容的版本时,检查Kubernetes的Godeps.json有助于确定哪个版本可能是正确的版本。
编译并运行测试套件
go test ./test/e2e -args -help是测试测试套件编译的最快方法。
一旦编译完成并且已经设置了集群,go test -timeout=0 -v ./test/e2e -ginkgo.v命令将运行所有测试。要并行运行测试,请使用ginkgo -p ./test/e2e命令。
如何参与
Kubernetes E2E框架由测试SIG的testing-commons子项目所有。请参阅该页面以获取联系信息。
有各种任务,包括但不限于:
- 将test/e2e/framework移动到staging仓库并重组它以使其更加模块化(#74352)。
- 通过将更多代码移入test/e2e/framework(#74353)来简化e2e.go。
- 从Kubernetes E2E测试套件中删除特定于供应商程序的代码(#70194)。
鸣谢
特别感谢本文的审阅者:
- Olev Kartau(https://github.com/okartau)
- Mary Camp(https://github.com/MCamp859)
KubeCon + CloudNativeCon + Open Source Summit大会日期:
- 会议日程通告日期:2019 年 4 月 10 日
- 会议活动举办日期:2019 年 6 月 24 至 26 日
KubeCon + CloudNativeCon + Open Source Summit赞助方案
KubeCon + CloudNativeCon + Open Source Summit多元化奖学金现正接受申请
KubeCon + CloudNativeCon和Open Source Summit即将首次合体落地中国
KubeCon + CloudNativeCon + Open Source Summit购票窗口,立即购票!
CNCF邀请你加入最终用户社区