一、背景

上图是一个通用的研发流程。在这个流程当中工程效率更多关注的是质量和效率两个维度,这当中可以做的事情有很多,而用自动化的手段加速从代码到上线的速度,提升迭代效率,是我们一直努力的方向。

通过把研发流程搬到 kubernetes 之上,我们对测试集群的管理和使用效率的提升进行了一次有意义的落地探索,下面和大家探讨相关的细节内容。

首先,环境先行。

为什么环境这么重要?从日常测试环境使用角度来看,大家都对环境有一些实际的需求,比如对研发的同学来说,希望有一个稳定的环境用于代码的自测验证,同时也希望他调用的其他服务和业务环境稳定,所以他的诉求就是环境的稳定。在研发提测之后,QA 同学也希望当前这个测试环境只有我这次需求相关的功能发布,测试中不期望有其他的人再次发布其他的功能,导致我测试的不预期,很难定位清楚到底是环境的问题还是真正的缺陷。

作为一个业务服务来说,我们对其他的服务或者业务是有一些依赖的,我希望我使用环境的时候对方是稳定的,不要在我使用的时候依赖的服务或业务这边挂了,导致整个链路出现问题。

因此,大家对环境稳定和隔离都是有相同诉求的。那当前哪些痛点导致这些诉求不能满足呢?我们接着会分析当前环境使用中遇到的问题。

 工作中我们经常听到或看到一些聊天内容,比如说:

  • “这个测试环境我要单独用 2 天,请其他人不要在这个环境中做发布”;

  • “这个机器我独占用于发布 ×× 服务,或者这个端口我独占了,希望其他人不要在这个机器上发布其他服务”;

  • “这个物理机挂了,导致测试环境的某些服务挂了,我们整个环境不可用,影响我们测试的工作。”

对于七牛云来说,测试环境中也存在一些客观问题:

  • 当前的测试环境中有 200+ 台物理机,但这些物理机基于测试成本的考虑,机器型号比较老旧,可能是线上业务淘汰后的机器,配置较差,在稳定性上经常会出现一些问题;

  • 测试环境没有专职的 SRE 做测试机房的维护;

  • 服务部署比较混乱,不同业务线会共用部分物理机,会导致一些部署和使用冲突的问题;

  • 资源利用率不均衡,部分机器会频繁告警,负载较高,而部分机器比较空闲。

诸如上述等问题,导致了下述情况的出现:

  • 环境不稳定。测试 5 分钟,排查 2 小时;

  • 多人共用环境,互相冲突;

  • 随手改动配置不入库,消极对待,缺乏敬畏之心;

  • 功能不健全,导致验证不充分,遗留缺陷。

这些不仅仅是七牛存在过的一些问题,也是整个业界的测试环境使用的现状与痛点。那么测试环境的本质诉求是什么呢?总结下来,我们对于测试环境本质的诉求分成两个维度:

1)使用者角度:

  • 想用就有、不用等待,部署环境的心智负担低;

  • 低维护,从维护角度来看,用户只需要关心测试的需求,我不想做其他维护性工作,比如机器挂了导致环境不可用,这些不是用户考虑的;

  • 高稳定,我依赖的其他服务和业务要稳定,不要影响我测试;

2)企业角度:高效率,低成本。追求更高的迭代效率,但是测试成本是底线。

了解诉求后,解决的方案也可以分为两个方向:

  • 随起随用、一人一套、隔离&稳定;

  • 自动化,免运维;

通过这些特点与特性,总结下来可以发现云原生技术是我们天然的选择:自动化、可借鉴、可伸缩。

二、方案

云原生社区很早就意识到在云原生背景下,环境容器化以及代码到环境的姿势问题。业界有很多开源项目都致力于解决环境的云原生化,以及如何把代码更快地发布到云原生环境中。比如这些开源工具或平台(skaffold/draft/telepresence/devspace/kt-connect),甚至孵化了一些商业化的产品或平台(DevspaceCloud/loft.sh)。

上图是七牛整体整体方案的框架图。

 首先是用户层。我们关注更多的是代码和配置,用户在本地开发代码、修改配置。还有一个维度就是用户最终会把代码提交到代码仓库 GitHub 或 GitLab,产生一个 PR 或者 MR 分支。在代码仓库的这些操作,会有一系列的事件触发,基于这些事件我们可以做一些自动化 Job 的触发。

第二层是工具层。大家比较常见的就是 Docker,还有跟 Kubernetes 交互的 Kubectl,在本地做自动化 C1/CD 方案的 Devspace,以及 Helm 等 CLI 工具。当然我们也有 web 页面提供。

 第三层是服务平台。服务平台集成很多公共服务,比如我们最近落地的云原生 CI/CD 平台 Prow,传统的 Jenkins,还有镜像中心,Helm Charts Repo 中心等。 

第四层是基础服务。其中最关键的就是  Kubernetes,也就是容器化的调度平台。Ceph,用于给 Kubernetes 中的服务提供 PVC 缓存服务;七牛云云存储平台 KODO,用于存储 Job 持久化的日志等数据。

上图中右侧是我们公司的 Pandora 平台,用于整体方案中日志的收集、数据整理展示、监控告警。Rancher 也是云原生社区的平台,主要用来解决隔离和权限控制问题。

最下面的是物理层。主要是 Kubernetes 的物理机节点,同时我们针对网络也进行了优化,打通办公网络和 Kubernetes 的节点网络,在 office 就可以连接  Kubernetes 的 ID,不需要登录节点。

下面我们分步骤进行详细的讲解。

1)第一步:环境容器化,模板化,可编排

环境的容器化,第一步大家都可以想到的是单个服务的镜像化,同时我们也对镜像构建进行了优化,比如我们提供了 DockerFile.base 镜像,在这个 base 镜像里我们提供了一些基础的工具(例如常见的 curl、wget 等工具),方便后期在 Pod 中调试。而业务的同学只需要关注的是他的服务怎么放到这个 base 的镜像后面就行了。这样做的好处是 Kubernetes 调度的时候,base 镜像都是统一的、在节点是有缓存的,在节点上的镜像操作仅仅是增量更新,这样就加速了服务的启动速度。

当前大部分公司的服务框架都是微服务化的,在每一个服务都做了容器化之后,那要如何在 Kubernetes 中创建一套完整的微服务集群呢?我们是使用了 K8S 社区推荐的 Helm 工具,通过它把所有服务的配置模板化,同时通过 helm dependencies 来编排服务的启动依赖,这样就可以在 Kubernetes 中创建集群了。

2)落地姿势 —— 一键启环境

核心的落地姿势之一:一键启环境。我们通过封装过的 devspace run 命令,一键就可以把微服务的集群启动起来,最终效果是在 Kubernetes 里创建相应的服务的 Pod,通过 namespace 隔离了不同的环境。

整体使用非常简单,直接 devspace run 加上不同业务的集群名称,--namespace 代表用户将要部署的环境名称。我们大致统计了一下,大部分的业务线能够在 3-10 分钟就可以部署一套集群,每套环境都包含了独立的中间件,如 mongo,redis 等,我们对部署时间也在持续优化。

3)落地姿势 —— 一键增量更新环境

核心的落地姿势之二:一键增量更新环境。比如说研发同学对于某个服务做了代码的开发,想要在环境中做一些联调自测,那么如何把这些代码和配置部署到已经存在的环境中呢?

姿势也很简单,就是 devspace run 加上服务名称,直接把服务发布到指定的环境中,然后就可以进行自测了。整体的增量发布时间在 10s+,这对研发同学来说开发+联调效率非常高。

这种姿势是非常适合研发阶段,因为研发阶段代码的提交非常频繁,我们之前姿势是需要提交 PR 代码到 GitHub,然后走 CI/CD 的流程,把 PR 发布到测试环境,在研发阶段整个流程看起来是非常繁琐的。

4)调试更方便

当有了容器化集群,有了一键更新的机制,要怎么去做调试呢?

我们针对于调试做了一些优化的方案或者使用姿势的输出,首先是本地直连容器内部,通过本地直接登录容器里面查看日志;第二就是我们梳理了 CLI/Web 两种方式使用容器的姿势;第三点在物理层做了打通,本地直连 Kubernetes 中服务的 IP 和 Port,对于调试服务接口非常方便。

5)环境自我更新

怎么保证我每次部署的环境是稳定呢,功能是完备的呢?

这里需要借助一些自动化 Job 作为卡点来实现。当研发自测完成,提测并提交 PR 的时候,我们会触发一系列 Presubmit Job 检查,其中包含了对于环境部署的检查,如果不成功这个 PR 是不会通过的。当 PR 通过 review 和测试以后会合并到主分支,这里会再一次的触发对于环境部署的 Postsubmit Job 检查,当检查通过之后,基于当前的代码版本去更新相应的镜像版本,同时也对 Helm Chart 进行更新,然后上传 Helm Charts Repo。当研发同学想要再次部署新环境的时候,版本是基于最新的主分支代码,整个环境版本更新完全是自动化的,保证了稳定性和功能的完备性。

对于 QA 同学来说,研发提测之后,QA 会问研发 PR 是什么,拿到 PR 号码之后,基于 PR 代码部署一套环境用于本次需求测试,这时测试环境是非常独立的,不会受其他一些新的功能部署的影响。

6)隔离问题

如果环境没有隔离,很难谈稳定。硬件和网络很好理解,对于环境的使用者来说不需要关心底层的物理机是否有问题。在 Kubernetes 里面当一个节点挂了以后,服务会自动调度到可用的节点,Kubernetes 已经很好的解决了硬件隔离和网络隔离问题。

用户有多种角色,比如管理员有全局的权限,普通用户只有当前环境的权限,普通用户还存在业务或者团队属性,测试环境下这种隔离不需要很彻底,我们使用了 Rancher 对这些角色进行了区分。

通过 Rancher 平台,把用户划分在不同的业务线或团队,每一个同学只有本团队环境管理的权限,但是没有其他团队的环境的操作权限。

Kubernetes 可以按需申请资源,但这个申请不是无限制的,不合理的资源申请最终可能会导致整个集群的资源不可用,对于资源的限制我们使用 LimitRange 的机制,可以限制某些团队或者环境整体资源使用的上限,从而保证集群整体的稳定性。

7)资源使用率优化

环境容器化主要的副作用就是环境的泛滥,比如基于 PR 创建测试环境的方式,一个人一周可能就会创建多个环境,这样的环境泛滥下,会导致集群整体资源非常紧张。

其实 Kubernetes 本身会在调度层面做一些 QoS 机制,会做类似服务驱逐的方案,当资源紧张时驱逐一些优先级低的 Pod,但这种驱逐不是我们预期的,不能保证环境使用的稳定性。

为了最大限度使用资源,对 Kubernetes 节点做资源超卖也是一种选择。比如节点当前可以运行 110 个 Pod, 通过超卖我们可以调整到 160 左右,但是超卖也只是权宜之计,不能根本上解决资源优化利用的问题。

容器化测试环境非常推荐的姿势,前半句是想用就有,还有后半句 - 用完即删。因为创建环境的时间成本非常低。

使用姿势靠个人,那如果用户没有删环境的习惯怎么办呢?我们设想,如果用户在环境中的行为,资源使用等指标都能拿到,那么对于长时间未使用的环境,或者资源使用超过阀值的用户,我们可以做到进一步的精细化管理,Kubefree 就是基于这种需求孵化出来的新平台。

Kubefree 会监控和记录用户对容器化环境的使用行为,假如说某个用户七天没有对这个容器化环境做任何的操作,我们有一种释放资源的机制 - Auto Sleep/Delete,什么是 Auto Sleep 呢?就是环境不会被直接删掉,Kubefree 会把该环境的所有服务实例降低为 0,意味着对于 cpu, 内存等硬件使用为 0,但是环境的配置仍然保留在 Kubernetes 中,下次想用该环境时就把服务实例提到 1,不需要全新的部署环境,加速了环境再次使用的效率。第二种就是 Auto Delete 机制,如果用户两周没有用这个环境,Kubefree 就会自动 delete 该环境彻底释放资源,用户下次使用的时候,可以再次新建环境,我们新建环境的代价也是非常低的。

通过 Kubefree 的 Auto Sleep/Delete 机制,调整时间阀值,我们能够动态的管理整个集群的资源使用,自动化优化不合理的资源使用情况。

 三、收获

 1)支撑专项测试

 有了容器化环境之后,对于专项测试的落地是非常有帮助的。

举个例子,七牛云有云存储的业务,针对可靠性测试,我们希望在任何场景下,我们的数据都不会被丢失或破坏,最终可以下载,这个场景在传统的物理机环境是比较难做的,有三个痛点:

  • 环境数量少,可复制性差,没法兼顾更多的需求

  • 当环境脏了,数据乱了时,没法快速推倒重来

  • 基础设施不灵活,可观测性不够

环境云原生化之后,创建一套环境是非常方便的,几分钟之内就可以创建一个新的环境,有脏数据是也可以直接删除掉对应的环境,重建成本非常低。

同时我们针对 Kubernetes 中的容器化环境,设计了整套的日志收集加监控告警的方案,搭建了一套比较完善的可观测的体系,可以用来观察故障测试的结果。上面例子中我们通过混沌工程会对云服务做一些破坏性的操作,比如说随机杀掉服务,模拟高负载等,最终借助可观测体系,我们能比较方便的观测服务的 5xx 状态。

环境云原生化让我们获得了三个优势:

  • 灵活复制环境

  • 轻松创建环境

  • 完善的可观测体系

2)更高阶的玩法

前面介绍的一键更新环境姿势,会对本地的代码进行编译,构建镜像,替换容器环境中的镜像过程,这个过程还是不够极致,我们在落地研发流程云原生化的时候,其实也体验了一种更高阶的玩法 - 不发布服务也可以做随时联调。方法是在本地起一个服务,把 Kubernetes 中对应服务替换掉,可以把本地服务和替换后容器化环境看成一个整体,对于本地服务的调用,流量也会转到本地服务。

这是比较高阶的玩法,需要对 Kubernetes 的技术有一定的了解才可以玩。

Q&A

1、刚刚你说了七牛云自己研发的 Kubernetes,在七牛云上是不是相应 SaaS 服务,跟阿里云的云效类似?是七牛云在 Devspace 基础上面研发的吗?

刘磊:Devspace 是一个开源的工具,它可以在本地完成对代码进行编译,构建镜像,替换容器环境中的镜像等一系列过程,不需要提交 PR,本地就可以把这些过程串起来,并且把服务发布到 Kubernetes 容器化环境当中。我们是基于 Devspace 等云原生的技术的能力,结合自身的业务特点,落地了整个技术方案。

2、刚刚刘老师说对 Chaos Mesh 也有观测指标,想请您分享一下云原生下做 Chaos Mesh 的观测指标怎么衡量?对于基础设施、网络、磁盘包不包含在破坏性测试范围之内呢?

刘磊:从我举的七牛云云存储业务来说,我们要验证的是,对资源进行上传之后,在任何情况下最终是不是可下载的?通过可观测性体系,我们关注的是下载的结果,是不是有 5xx 等错误,如果有不正常的状态码,就说明异常场景下服务功能可能存在问题。测试过程中的日志都会收集到七牛云的 Pandora 大数据平台,方便后续针对问题的分析。此外,问题中提到的场景也都是在范围内的。