新书推荐
京东自营链接
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 以及希望部署在哪个地域等等。研发人员把应用和声明的依赖项提交到云平台中,云平台会去找到符合要求的机器并把应用调度过去。如果应用所属的机器崩溃,云平台也会自动的把应用再调度到其他符合要求的可用节点进行部署。如此应用就与具体的某台机器解耦了,研发人员也就不需要知道应用具体运行在哪台机器上。这种可以让应用随意的在集群中进行移动的能力依赖两个非常重要的技术:
- 容器:只有把应用运行所需依赖打包进镜像后,云平台才可以通过镜像随意的在不同的机器中启动容器来部署应用。
- 声明式 API:与以往模式不同的是,应用可以不再与某台固定的机器进行绑定,所以在提交部署请求时,要求用户去声明部署应用所需要的资源(cpu,内存,存储等)和分布策略(部署地域,机房,分布模式)等信息。 云平台需要能解析这些声明信息并做出相应的处理。
当然云平台不仅如此,云原生架构也不仅如此,它的流行技术还包括了微服务和服务网格(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 集群性能测试场景外,云原生还有很多其他的测试类型。云原生与传统的软件设计方式截然不同,这也为测试人员带来了新的挑战,比如:
- 容器领域的知识储备:在国内容器技术在 2015 年崛起,我记得在那一年 Docker 成为了每个技术大会都在谈论的话题。但在当时行业内更多的把容器技术看做是运维领域要解决的问题,所以即便到了现在测试行业中对于容器技术的知识积累也仍然有限,想在市面上招聘一个善于容器领域的测试人员仍然十分困难。但在云原生越来越流行的今天,很多测试活动都必须在容器环境中执行,知识储备的不足已经成为测试人员非常大的问题。
- 高可用测试:传统软件迁移到云原生架构中往往需要做出比较大的改造,这些改造对于功能的影响往往比较小,但很多潜在的高可用问题比较难以被发现。每种云平台也都会为用户提供各种高可用的设计来帮助用户减少开发成本,而这些高可用的设计与传统软件的高可用设计也会碰撞出很多难以预测的现象。同时为容器注入故障也与传统方式有着很大的不同,这些都为测试人员带来了更多的挑战。
- 性能测试与对应的监控系统:在传统的性能测试中,测试人员更多关注的是服务的各项性能指标,基本不会关注分配给该服务的资源配额是否合理,或者说在有些传统的软件架构中,可能就没有配额这个概念。而在云原生领域,技术人员需要为每个以服务设置对应的资源配额,而且资源配额有很多中策略。测试人员除了要保证服务的性能符合预期外,还需要验证分配给该服务的资源配额是否合理,这也就开展出了一种新的测试类型—容量测试。同时云原生往往便随着微服务架构,一个系统中存在大量的服务数量,测试人员熟悉的传统的监控方式已经无法满足云原生的测试需要了(想象一下每次测试都需要收集几百个服务的所有资源占用情况,将是一个多么恐怖的事情)。所以这需要测试人员学习云原生时代最流行的监控系统,并开发对应的性能数据自动收集组件。
- 稳定性测试与对应的监控系统:云平台一般都有很强大的自愈能力,正因为运用了容器技术,服务在崩溃后云平台可以立即发现并将该服务重新调度到可用节点。很多时候服务中间时间很短,测试人员可能根本感知不到服务曾经出现过问题。这对于测试人员来说是不友好的,很多由于软件设计引起的崩溃问题往往会逃出测试人员的视线。尤其是在云平台中,大量的容器运行在同一台服务器中,很多团队也会选择各种超卖策略来提升资源利用率,这也会导致服务之间互相影响,让系统变得不稳定。所以如何让测试人员感知到这种瞬时的异常事件,并把定位排查的信息收集到一起的监控系统十分的重要。测试人员需要开发这样的监控系统并基于此来设计对应的稳定性测试场景。
- 边缘计算:边缘计算在近些年也开始慢慢流行,并且目前各个大厂都在开始探索边缘计算与云原生的结合方式,开源社区中也有对应的开源项目。测试人员也需要开始慢慢的了解该领域的测试方法。
- 持续集成与发布:持续集成与发布作为非常流行的软件实践活动,已经在各个公司都有了较为成熟的理念和实践方法。而在容器技术流行起来的今天,如何让它与云平台更好的集成也是需要测试人员学习的。
- 云原生与大数据:目前云领域早已经脱离了早年间大家对它的认知,越来越多的大数据和人工只能项目开始与各个云平台集成,云平台也慢慢的满足了分布式计算程序运行所需要的一切条件。在大数据日益普及的今天,测试人员也慢慢的开始积累相关的知识储备。
而以上所说的这些测试类型也在我写的书《云原生测试实战》中有了详细的讲解。这本书主要还是围绕在容器领域中主要的测试活动展开的,除了介绍 docker 与 k8s 比较关键的知识点外,也花了很多的篇幅讲述在这样的环境下如何开展对应的专项测试与测试工具的开发工作。 其中每一个案例都有相关的代码讲解。 这里面每一个案例都是在我工作中遇到的真实的场景,并非是凭空杜撰出来的。
结尾
嗯, 最后还是忍不住给自己的书打了个广告 ,请大家谅解哈(PS:人邮旗舰店卖的更便宜)。
京东自营链接
https://item.jd.com/14182314.html
人邮旗舰店链接
https://item.jd.com/10086973360931.html