新书推荐

京东自营链接
https://item.jd.com/14182314.html
人邮旗舰店链接
https://item.jd.com/10086973360931.html

为什么要学习云原生

把时间倒回到 2016 年的 3 月份,当时的公司还处于初创期,满打满算也就 30 来号人。那里没有设立相关职位为大家解决环境治理的问题,所以我决定把环境承接下来并开始学习 docker 和 k8s。我想这应该就是我第一次接触云原生相关技术的时间点了(虽然那时候我还不知道云原生这三个字的存在)。 当时的我没有想到这么一个简单的决定成为了我职业生涯中最大的转折点。我后续慢慢开始从事大数据、云计算、边缘计算和人工智能产品的测试工作,这些在如今的软件行业大红大紫的领域都以云原生架构为基础蓬勃发展。并且从现在的发展来看,未来相当长的一段时间里,云原生架构仍然会是主流的软件研发架构。我很庆幸自己能在较早的时间点就开始积累这方面的知识,这让我在这些年的工作中如鱼得水。虽然我目前负责的几乎都是以人工智能为主的产品,但我不得不说 docker 和 k8s 在我的工作中起到了至关重要的作用。比如就在十一假期前刚刚完成的人工智能平台中边缘计算的测试工作。

在人工智能场景中,尤其是以计算机视觉方向为代表,边缘计算被大量应用在工业级应用中。不同于实验室场景中对于数据操作的简单便捷,工业级应用需要在数据的收集与计算方向花费巨额的成本。云计算已经开始慢慢无法满足越来越多的企业的要求,比如:

随着云计算的不足愈演愈烈,人们开始慢慢的把视线转移到了边缘计算上。那么边缘计算能为用户提供什么样的能力呢。简单来说如果云计算是一种集中式计算架构,那么边缘计算就是彻头彻尾的分散式架构。它把关键的服务和任务分散在更靠近用户和终端设备的地域进行调度部署。这种就近计算的设计可以有效解决因为数据传输而带来的性能和数据安全问题,企业把服务架设在距离用户和中心设备更近的机房中,这样便规避了复杂的网络环境。基于此设计,企业需要根据用户和终端设备的分布在不同的地域架设机房。并且从软件设计上让终端设备优先去请求离自己最近的机房进行计算:

我喜欢把这种架构简称为云边端,分别代表了在边缘计算中这 3 种不同的角色。在计算机视觉场景中,客户的设备可能存在于园区/工厂/学校/社区的摄像头或其他照相设备。这些设备更靠近终端的人而非中心集群,它们收集到图像数据后再经过一系列的处理(解码,抽帧,过滤等)再传输到边缘机房部署的模型服务中进行识别,比如识别人体的行为(打架,离岗,跌倒,野泳等),人体的属性(安全帽,手套,安全带等),又或者识别烟雾火灾人脸对比等等。

完成这样的架构是非常困难的,从物理设施角度看,通常终端设备分布在非常多的地域中,企业需要在这些地域架设大量的机房。而从软件角度看,产品的架构要能够完成所有边缘节点的统一管理,完成机器的区域划分,服务的统一下发以及网络的分发策略等等。 为了支持这种部署架构,人们寻求了很多的解决方案,尤其对当前最主流的云平台技术--K8S 中开发相关功能的呼声尤其强烈。 目前国内比较主流的边缘计算项目有 superedge、OpenYurt 和 KubeEdge,他们都是基于 k8s 扩展而来。 事实上人工智能产品需要的调度需求会更多,比如对于 GPU、FPGA、TPU 的调度,对于大型存储设备的调度,对于网络流量的调度等等。

所以对于一个人工智能产品来说,要把它很好的运行起来,需要大量的非人工智能技术来支持,而云原生毫无疑问是重中之重。这也是为什么有人问我要如何学习人工智能测试的时候,我会给他列一个较长的清单。这时他会问我,我想学人工智能方向的测试,为什么要推荐给我这么多不相关的技术。而我就会回答,等你真正接触到一个人工智能系统,你就会发现你期望的算法和模型只是这个系统中的一个组成部分,甚至可能只是一小部分,因为为了能让算法和模型运转起来,需要大量的基础设施的建设,如果我们眼里只有对着模型统计精准率和召回率,那我们永远无法突破现有的瓶颈,进入到下一个等级。 我始终坚信:如果想要学习一样技术,那就不能只学习这样技术本身。 大量的基础技能是需要我们每个人去慢慢积累的。

所以在边缘计算场景中会有独特的设计,比如分布式健康检查、流量闭环、按边缘机房的自动调度等等,这些都是需要测试人员进行仔细测试的。

什么是云原生

我这里就不咬文嚼字的列概念了,直接从用户最直观的感受来讲述。在多年之前,研发人员开发好一个应用后。为了能把应用进行部署,研发人员往往会申请一台虚拟机并安装好相关依赖,把代码编译打包后上传到虚拟机中进行启动。 此时这个应用是跟机器进行绑定的,如果机器崩溃了,那么这个应用也就无法存在了。而在云原生时代,我们的模式发生了变化。我们希望应用和机器是不绑定在一起的, 此时研发人员需要做的是声明清楚运行应用的硬要求,比如需要的 CPU,内存,GPU,存储设备,GPU 以及希望部署在哪个地域等等。研发人员把应用和声明的依赖项提交到云平台中,云平台会去找到符合要求的机器并把应用调度过去。如果应用所属的机器崩溃,云平台也会自动的把应用再调度到其他符合要求的可用节点进行部署。如此应用就与具体的某台机器解耦了,研发人员也就不需要知道应用具体运行在哪台机器上。这种可以让应用随意的在集群中进行移动的能力依赖两个非常重要的技术:

当然云平台不仅如此,云原生架构也不仅如此,它的流行技术还包括了微服务和服务网格(service mesh)等。不过云原生架构本身是为了能让程序在云环境中运行和迭代的更好而产生的一种设计思想。理论上它没有固定的架构实现,因为云环境的设计是各不相同的,每一种云环境都有适合它运行的设计思路。只不过在当今的软件行业,容器技术被最多的应用于云领域,所以现在提到云原生往往都绕不开 Docker、ContainerD 以及 Kubernetes 等。把程序部署在云中运行和让程序更好的在云中运行是两种完全不一样的设计,前者只需要把应用程序打包后放到云中执行即可,无法保证整个流程的高效和稳定,这只是做到了软件的运行基于云,而不是云原生。云原生的关键不是在哪里运行应用,而是如何构建应用。它需要在设计之初就考虑到云的设计并最大程度的利用其特点完善整个系统。可以理解为技术团队把云环境当做了软件的一部分,在最一开始就思考如何对接云平台中的网络、存储、安全、通信、调度等等特性,尽量利用云平台提供的特性来实现软件,并以此为背景总结出一套可以让产品高效迭代的流程和方法论。

基于这些强悍的调度特性,现在的云平台也开始向大数据和人工智能等领域发展,比如 spark on k8s 和 flink on k8s 已经开始慢慢取代传统的 hadoop 的地位,谷歌在几年前也发布了以 k8s 为基础的人工智能项目:kube-flow。k8s 的 device plugin 也可以支持各种型号的 GPU 卡的调度行为来支持人工智能的计算与推理。 云原生正在慢慢的成为各个系统的基础底座,所以不论我们是研发人员还是测试人员都应该好好掌握相关的技能,方能在工作中事半功倍。而基于云的特性,也演变出了一些特殊的测试场景。比如云平台自身的性能测试 -- 平台能够支撑多少台节点以及多少容器的运行。

如果按照传统的思路,测试团队需要申请大量的虚拟机来组建 K8S 集群,但这无疑是成本非常高的选择,因为在实际项目中我们往往需要测试数百甚至数千台机器规模的集群。所以换一种思路,看一下 K8S 的集群架构:

K8S 集群中每台节点中都会启动一个名为 kubelet 和 kubeproxy 的进程。kubelet 负责与 docker/containerD 通信,维护容器状态并周期性的向 APIServer(K8S 集群中的主要服务)上报节点和容器状态,而 kubeproxy 则负责容器网络规则维护。 而 APIServer 则会负责把用户创建容器的请求以及 kubelet 上报的容器和节点状态存储在 ETCD 中。可以看出来 API Server 是维护 K8S 集群状态最关键的服务,一旦 API Server 出现问题整个集群都会陷入异常状态。而通过上述两点的描述也可以知道随着集群内节点和 Pod 的增多,API Server 承受的压力也会越来越大。除此之外在商业产品中往往会由于各种需要自研许多组件与 API Server 进行交互,这些组件都会给 API Server 带来不小的压力。所以 API Server 的性能数据往往是测试中最关键的指标之一。业界也会推荐在 K8S 集群中的 etcd 要使用高性能固态硬盘进行部署,因为 etcd 的性能也是影响 API Server 最主要的因素。同时还需要注意的是除了 API Server 以外 K8S 还有一些其他组件也是随着 Pod 数量增长而增加压力的(比如调度器和用户自研 operator)。

所以随着节点和容器的增多,对集群的主要压力在于 etcd 和 apiserver 这些组件。只要我们模拟真实的 kubelet 来上报容器状态给 APIServer 增加压力,那其实是不是有一堆真实的节点和容器并不重要。我们构建一些虚假的 kubelet 就可以达到目标了。

kubemark 是一款针对 k8s 的性能测试工具,它能够使用少量的资源模拟出一个大规模 K8S 集群。为了演示这个工具需要一个待测试的 K8S 集群 A 以及一个装载 Kubemark 服务的 K8S 集群 B(也称 Kubemark 集群),当然也可以使用同一个集群装载 Kubemark,只不过在实践过程中我们习惯将待测试集群和 Kubemark 集群区别开来。这时在 B 集群中部署 Kubemark 的 Hollow Pod,这些 Pod 会主动向 A 集群注册并成为该 K8S 集群中的虚拟节点。也就是说在 B 集群中启动的 Hollow Pod 模拟了 K8S 的 kubelet 和 kube-proxy 并与 A 集群的 API Server 进行交互,让 A 集群误以为这些 Hollow Pod 是真实的节点。这些虚拟节点会完全模拟真实节点的行为,比如它会定时向 API Server 上报节点和 Pod 的状态,也会在用户发起创建 Pod 的请求时进行响应,只不过它不会真的去启动容器而已。也就是说虚拟节点除了不会真的启动容器外,其他的大部分功能都与真实节点差不多。这样设计的目的是为了能够模拟真实的节点对 API Server 等组件造成的压力。这样测试人员就可以避免耗费巨额的成本去搭建真实的集群就可以评估 K8S 的性能了。我们可以认为 kubemark 就像是一个复杂的 mock 服务一样,不必启动容器,但可模拟 kubelet 的行为。 如图:

当我们按照教程和自身集群特点将 kubemark 部署好后,就如下面的样子:

为了把 Pod 调度到这 3 个虚拟节点中,需要为它们标记对应的 Label,如:

kubectl label node hollow-node-b6gqb name=hollow-node
kubectl label node hollow-node-lhjtv name=hollow-node
kubectl label node hollow-node-nztb9 name=hollow-node


然后我们可以通过 node selector 把部署请求调度到虚拟节点中:

此时我们真实的部署请求就被调度到虚拟节点中了, 我们可以通过 k8s 的命令正常的针对这些 Pod 进行操作。 它们除了没有启动真正的容器外,其他的与 k8s 原本的行为是一致的。 通过这样的方法,也就可以针对 K8S 集群的性能进行测试了。

云原生的测试挑战

除了上述介绍的边缘计算和 k8s 集群性能测试场景外,云原生还有很多其他的测试类型。云原生与传统的软件设计方式截然不同,这也为测试人员带来了新的挑战,比如:

而以上所说的这些测试类型也在我写的书《云原生测试实战》中有了详细的讲解。这本书主要还是围绕在容器领域中主要的测试活动展开的,除了介绍 docker 与 k8s 比较关键的知识点外,也花了很多的篇幅讲述在这样的环境下如何开展对应的专项测试与测试工具的开发工作。 其中每一个案例都有相关的代码讲解。 这里面每一个案例都是在我工作中遇到的真实的场景,并非是凭空杜撰出来的。

结尾

嗯, 最后还是忍不住给自己的书打了个广告😂 ,请大家谅解哈(PS:人邮旗舰店卖的更便宜)。

京东自营链接
https://item.jd.com/14182314.html
人邮旗舰店链接
https://item.jd.com/10086973360931.html


↙↙↙阅读原文可查看相关链接,并与作者交流