运用Kubernetes进行分布式负载测试
负载测试是开发后台基础架构的重要一环,它不但能够演示系统在真实需求面前的性能表现,还可以通过模拟用户与设备行为,在应用程序部署到生产环境前,找出并了解任何可能的系统瓶颈。
但是,专用的测试基础设施可能非常昂贵且难以维护,且此类设备一般是针对特定性能状况的一次性投资,初期投资后想要再对负载测试进行扩展就十分困难,还可能限制实验,从而导致开发团队的工作效率变低,应用在部署到生产环境前也无法得到充分有效的测试。
解决方案综述
分布式负载测试采用云计算手段,在各种测试场景中这种方案都很有吸引力。云平台使得基础设施平台的弹性得到高度扩展,想要通过大量模拟可产生流量的客户端进行应用和服务测试都十分容易。此外,云计算的定价模式与负载测试的弹性特质非常相符。
无需再运行完整的虚拟机实例了,容器提供的轻量级选择与虚拟客户端的快速扩展完美匹配。由于其轻量级、易于部署、快速可用并适合单一任务等特质,容器是取代运行测试客户端的优秀替代方案。
谷歌云平台是使用容器进行分布式负载测试的极优环境,该平台使用的谷歌容器引擎(Google Container Engine)以开源容器集 群管理器Kubernetes为动力,将容器作为一级对象对其提供支持。使用容器引擎可以快速搭建容器基础设施,并可用来管理应用部署与资源。
该解决方案演示了使用容器引擎部署分布式负载测试框架的方式。此框架使用多个容器,搭建了一个应用于简易REST-based API的负载测试通讯。尽 管这是用来测试简单Web应用的,但同样的模式可用于创建更为复杂的负载测试场景中,比如游戏或物联网应用中。该方案讨论了基于容器负载测试框架的通用架 构。请至本文末尾查看教程,逐步学习样例框架的构建。
本方案着重通过容器引擎来创建负载测试通讯,被测系统是一个简单的Web应用,使用了REST的API。借助现有的负载测试框架,塑造出下文中详细描述到的API互动模型。并在完成被测系统的部署后,运用容器引擎来部署分布式负载测试任务。
被测系统
在软件测试术语中,被测系统(System Under Test)指的是该测试设计时所针对的待评估系统。在该方案中,被测系统是一个部署到 Google App Engine的小型Web应用,该应用通过发布基本REST样式的端点来捕获接收的HTTP POST请求(接收数据并不连续)。 在真实场景中,Web应用可能会很复杂,并包含大量的附加组件及服务,如caching、messaging和persistence,此方案不考虑这些 复杂情况。更多在谷歌云平台上构建可扩展的Web应用相关信息,请查看 the Building Scalable and Resilient Web Applications方案 。
样例应用的源代码请见文末教程。
示例workload
许多物联网的部署中有类似示例应用模型中所使用的后端服务组件——设备首次注册服务后,开始报告指标或检测器读数,并定期重新进行服务的注册。
下图展示了一个常见的后端服务组件的交互。
该交互可以用 Locust 这种基于Python的分布式负载测试工具来建模,Locust可以向多个目标路径分发请求,比如向/login和/metrics目标路径分别发送请求;还有很多负载生成软件包也可根据项目需求选择使用,包括 JMeter 、, Gatling 和 Tsung 。
而workload将会取决于上面所说到的交互,在Locust会以一组 task 的模型出现。为了尽量模拟真正的客户端,比如同时有上千个客户端请求接入的情况,每个Locust task需经过加权。
基于容器的计算
从架构角度来看,部署该分布式负载测试方案有两个主要的组件:Locust容器image,还有容器编排及管理机制。
Locust的容器image是包含Locust软件的 Docker image,Dockerfile可以在相关的Github库中找到(见教程),而Dockerfile使用了基于Python的image,并使用一些脚本文件来启动Locust服务,执行task。
该方案利用谷歌容器引擎用作容器编排与管理机制。容器引擎是基于开源框架Kubernetes,集合了谷歌多年在容器部署方面运行、编排与管理的 经验。基于容器的计算允许开发人员专注于应用本身,无需将精力浪费在繁琐的托管环境部署与集成上。容器同时也使得负载测试更为轻便,通过容器整合后的应用 可以在多个云环境中运行。容器引擎与Kubernetes引入了针对容器编排与管理的若干概念。
容器集群
一个容器集群包含一组云计算引擎(Compute Engine)实例,为整个应用提供基础。在容器引擎及Kubernetes的文档中,这些实例被称为 节点。一个集群包含一个master节点和一到多个worker节点。master节点与worker节点都运行在Kubernetes上,因此容器集群 有时也被称为Kubernetes集群。更多集群相关信息请查看 容器引擎文档 。
Pods
Pod是一组应当被集中部署的紧密耦合容器,一些pod只包含单个容器,例如该案例中,每个Locust容器都运行在自己的pod中。但是通常情况下,pod会包含多个集中执行的容器,例如该案例中,Kubernetes使用了一个包含三个容器的pod提供DNS服务。
在一个容器中, SkyDNS 提供DNS服务功能。SkyDNS依赖于一个名叫etcd的键值存储,而它又被封装在另一个容器中。在pod的第三个容器中, kube2sky 担任了Kubernetes与SkyDNS之间的桥梁。
复制控制器
一个复制控制器确保特定数量的pod“副本”能够随时运行。如果数量过多,复制控制器会关掉其中一些;如果数量过少,复制控制器会启动一些新的。 该方案有三个复制控制器:一个确保DNS server的单个pod存续;另一个维持Locust的单个master pod;第三个则保证同时正好有 10个Locust的worker pod运行。
服务
特定pod可能会因为各种原因而消失,包括节点失效或因更新维护而主动进行的节点中断。也就是说,pod的IP地址没有为其提供可靠的接口。更为 有效的办法是使用该接口的抽象表示,即使底层pod消失,新的pod产生,IP地址发生变化,该抽象表示不会改变。容器引擎服务通过定义一组逻辑pod及 访问相关策略,提供这种类型的抽象接口。在该方案中,有一些代表pod或成组pod的服务。例如,一个服务代表DNS server,另一个代表 Locust master pod,还有一个代表那10个worker pod。
下图展示了master节点与worker节点所包含的内容:
部署被测系统
该方案使用谷歌应用引擎来运行被测系统。部署被测系统需注册可用的谷歌云平台帐号,以安装运行谷歌云平台SDK,之后通过一个命令就可以部署这个样例Web应用了,所需的源代码在文末教程中可以找到。
部署负载测试任务
部署负载测试任务,首先需要部署负载测试master,然后是一组10个的负载测试worker。有了这些工作负载测试,就可以根据测试目的来创建大量通讯了,但需要铭记:与外部系统产生过多通讯与拒绝服务攻击相类似,请务必回顾谷歌云平台的 服务条款 和谷歌云平台的 使用者协议 。
负载测试master
部署的第一个组件就是Locust的master,它是执行负载测试任务的入口。部署时将Locust master部署为只含单个副本的复制控制器,因为我们只需要一个master。一个复制控制器甚至在部署单个pod时都是有效的,因为它能确保高可用性。
复制控制器的配置指定了几个元素,包括控制器的名字(locust-master)、标签 (name: locust, role: master)、容器所需要发布的端口(Web接口用8089,与worker通讯用5051和5052)。 这些信息稍后会被用来配置Locust的worker控制器。下面的信息中包含端口的配置:
... ports: - name: locust-master-web containerPort: 8089 protocol: TCP - name: locust-master-port-1 containerPort: 5557 protocol: TCP - name: locust-master-port-2 containerPort: 5558 protocol: TCP
下一步,我们会部署一个Service,以确保发布的端口可以通过hostname访问其它pod:集群中的端口以及通过描述性端口名称的 referenceable。通过使用服务,即便在master失效,复制控制器又生成了新pod的情况下,我们也可以很容易地找到Locust的 worker,并可与master通讯。Locust的master服务也包含在集群层面创建外部转发规则的指令,提供访问集群资源的外部通讯能力。注 意:还需创建防火墙规则,以提供访问目标样例的完整入口。
在部署Locust master之后,就可以通过符合外部转发规则的公开IP地址来访问Web接口了。部署Locust worker之后,可开启模拟器并通过Locust Web接口来查看汇总统计。
负载测试worker
下一步部署的组件是Locust worker,用来执行负载压力测试。Locust worker是通过能生成10个pod的单个复制控制器来 部署的。这些pod分布在Kubernetes的集群中。每个pod通过环境变量来控制重要的配置信息,像是被测系统的hostname和 Locust master的hostname。worker的复制控制器配置方式请查看下面的教程。配置包含控制器的名称、locust- worker、标签(name: locust, role: worker),还有前面描述的环境变量。下面代码包含配置的名称、标签、副本数:
kind: ReplicationController apiVersion: v1 metadata: name: locust-worker labels: name: locust role: worker spec: replicas: 10 selector: name: locust role: worker ...
对于Locust worker来说就无需部署额外服务了,因为worker pod自身不需支持任何入站通讯——它们直接与Locust master pod相连。
下图展示了Locust master与Locust worker之间的关系。
在复制控制器部署Locust worker之后,就可以返回Locust master的Web接口来查看worker部署数量相应的slave数。
执行负载测试任务
开启负载测试
Locust的主Web接口允许执行针对被测系统的负载测试任务,见下图:
开启时指定模拟的用户数、用户应当产生的速率。下一步,点击Start开始模拟。随着时间流逝、用户产生,可以看到统计数据开始按模拟指数进行聚合,像是请求数、每秒请求数,如下图:
停止模拟只需点击Stop,测试就会终止。完整结果可以下载表格查看。
扩展客户端
按比例增加模拟用户会导致Locust worker pod数随之增长。在Locust的worker控制器中有详细说明,复制控制器部署10 个Locust的worker pod。通过复制控制器增加pod的数量,Kubernetes提供了不需重新部署即可调整控制器大小的能力。例如,通过 kubectl命令行工具可以调整worker pod的数量。下面的命令可以将Locust的worker pod数量增加到20:
$ kubectl scale --replicas=20 replicationcontrollers locust-worker
在发出扩容命令后,等待几分钟,所有pod在此时间段内完成部署并启动。所有pod启动后,回到Locust master的Web接口,重启负载测试。
资源与成本
这个解决方案使用了四个容器引擎节点,每个都受云计算引擎VM标准n1-standard-1类型的支持。你可以使用谷歌云平台的定价计算器估算运行容器集群的月开销。上文提到过,可以按需定制容器集群的大小。定价计算器可以协助你自定义集群特点,借此评估开销的增减。
下一步
现在可以查看如何使用容器引擎来创建简单Web应用的负载测试框架了。容器引擎允许你指定建立负载测试框架容器所需的节点数量。容器引擎还允许你将负载测试工作节点合并到pod中,并制定容器引擎运行时想要保持的pod数量。
使用同样的模式来创建不同环境变量与应用的负载测试框架。例如,使用该模式创建信息系统、数据流管理系统与数据库系统的负载测试框架。创建新的Locust任务,甚至是不同的负载测试框架。
扩展框架的另一办法是自定义收集到的指数。例如,你可能想要测量每秒的请求数,或者监听负载增加后的响应延迟情况,或是查看响应失败率与错误类型。有多种可选的监控方式,包括 谷歌云监控 (Google Cloud Monitoring)。
教程
完整教程包含使用说明与源代码,请点击 下载 查看。
英文原文: Distributed Load Testing Using Kubernetes (译者/孙薇 审校/朱正贵 责编/仲浩)