Bootstrap

INFRA-JOY微服务治理验证工程实践分享

背景

在技术日新月异的今天看微服务,已经是一个相对成熟的技术栈,被归类到技术栈中的晚期大众①,如何界定一项技术或技术栈是否足够成熟,我的评判标准是“该项技术是否在各大云厂商中实现服务化商品化规模化”, 也就是该项技术可以定性定量定价进行服务,按照这种粗浅的界定,无疑微服务领域的治理已经是技术上的老玩家,本次再谈微服务治理没有引入新的概念和理论,而是从另一个角度或者更贴近企业特性角度,将微服务治理的经验实践、落地、服务、推广等服务,最终也能像云厂商一样,赋能同时还做到定性定量,此类标准的验证工作对服务提供方也就是目前基础架构,提出了巨大的能力验证压力,从正确性断言到正确率、可用性等指标的度量工作是个指数级别的增长,于是我们思考如何立足用标准方案增加效率,用事件协同连接分工间隙,成立 项目,用实际项目来演练、验证、验收这其中的一系列方案的可行性问题,这些问题需要由基础架构所有成员参与建设。

起点

项目的创建半年有余,由MVP定位发起并逐步扩大验证范围,规划成为覆盖微服务全生命周期,定位于给微服务治理提供验证手段和机制,从打包到服务上下线,从开发环境到生产环境,参与角色由前后端开发工程师、前后端测试、移动端以及外部协调部门成员组成,整个项目运行在现有的公司微服务体系和K8s环境之下,现阶段的微服务形态相对比较稳定,掌门对应的微服务组成结构也大致如下:

图1 - 微服务架构图

项目并不深入到源码细节去剖析具体技术深度(源码层面的细节都由集成到该服务的微服务框架实现),而是建立一种标准验收结论性工程,项目原始源码来自(见过的同学应该不陌生),基于现有的轮子快速形成服务拓扑逻辑,其次将待验证的微服务治理相关的功能点、技术点、连接点植入进去,形成一个最初的待验证的项目原型。

图2 - Infra-Joy项目拓扑图

建设

验证工程由5个后端Java微服务,1个前端服务组成,以实现前后端自动化的功能发布验收+验证作为基础,延伸到如何将微服务治理建设涉及的功能点在这个微服务体系下二度定制并进行验证,在早期的建设中,我们优先进行了重要紧急部分的相关微服务治理场景或者用例的验收和验证,验证的目标尽量覆盖完整,而依赖不仅限于某一个团队,部分验证清单和结果大致如下:

实现一个相对完善的服务治理覆盖的内容远不止上述清单的内容,如何有效对治理内容进行定性定量定阶段(Ops周期),且每一个治理的功能都需要有一套完备的解决方案,这些又需要回到新版本的开发周期中(Dev周期),因此治理需要有一个较为明确的概念加以归类。

治理归类

按照持续交付将DevOps划分的8个阶段来讲,整个DevOps过程并没有绝对的起点和终点,而是形成一个双环结构。

按照左右环来划分治理可以将整个治理划分为线下、线上节点,分别关注不同的领域:

图3 - 治理领域图

线下

  • 需求管理

  • 项目管理

  • 测试用例管理

  • Bug管理

  • CI/CD Pipline

  • Ops Workflow

  • 软件/镜像版本

  • 代码仓库

  • ...

线上

  • 服务注册/配置中心

  • 主机日志管理

  • 服务日志

  • APM

  • ...

本次微服务工程治理早期会以纯线上治理环节作为突破口,而作为面向治理的功能验证更多会和微服务治理中后期形态一致,从线上到线下会覆盖到整个PAAS层建设,接下来我们通过从线下到线上的连接点,即Release+Deploy两个节点对微服务发布治理验证案例(灰度发布、无损发布)来介绍这其中的具体实践思路。

灰度治理

按照生产发布三板斧概念:可灰度、可观测、可回滚,同理,一次灰度发布治理的验证不仅是一次Deploy本身,而是整个Deploy周期,即发布、灰度、验证、回滚、全量整个过程的治理验证。

验证目标

灰度治理功能的验证目标:

使用自动化的流程,对提供的灰度功能和规则通过一系列的用例进行自动化回归并且有效验证流量的正确性。

为了达成这个目标,需要将目标细分成多个子目标:

  • 流程的自动化

  • 用例回归自动化

  • 用例流量正确性验证自动化(非用例自身的功能正确性,非API Response的正确性,而是验证API流量路径的正确性)

在微服务治理的若干场景下,每个场景的验证工作都可作为一个独立的Case存在,如何在若干个Case的验证后,再通过一个集成Case充分使用复杂场景下容易暴露问题的特点进行集成验证,这其中即需要一定的集成Case编排能力,还需要对若干依赖项有足够的搭建和掌控能力,这里我们采用半自动化(部分需要人工介入)的后端灰度验证作为集成案例进行设计。

图4 - 一次人工的灰度验证流程

流程制定

将一次生产灰度发布的流程包含打包、发布、验证、回滚或者全量发布,这里的内部过程比较复杂,就不逐一展开,这里通过状态图来阐述状态转换逻辑。

图5 - 灰度发布流程状态图 + 打包流程

用例设计

在一次灰度计划中,流程的走向大多按照语句覆盖(每个语句节点执行一次)的逻辑进行,但在一次回归验证过程中,我们必须按照最完整的路径覆盖、条件组合覆盖等更加完整的回归思路进行,做到每个环节均有对比,所以需要设计出N个流程,每个流程进行一次语句覆盖,每个流程尽量无状态化设计独立存在,每组用例验证该组领域内的逻辑,不涉及交叉验证,这里主要分为5个阶段:

  • 发布灰度实例前

  • 发布灰度实例后

  • 中止灰度后

  • 回滚执行后

  • 全量发布后

每个阶段都对应一个主流程,阶段和阶段通过系统事件通知能力进行调度,以第一个阶段发布灰度实例前为例:

图6 - 灰度发布实例前流程图

自动化用例编排

灰度用例的编排和回归验证是整个灰度治理的核心,随着灰度功能、规则的丰富,整个回归的规模体量成倍增加,回归的覆盖遗漏带来的风险也必然给未来的排障带来巨大障碍,所以整体用例编排按照一次搭建、增量维护的版本周期展开,不同于常规的API测试,回归测试中使用了70+个接口的元数据在数百个用例用使用了近千次调用,做到多种场景下的灰度回归的覆盖,以灰度规则的自动化回归为例:

图7 - 灰度规则验证部分用例节选截图 及 对应的功能截图

自动化流程编排

一次灰度发布流程是由若干个阶段的自动化用例的回归组成,使用CD/Pipline/回归平台的能力尽可能采用自动化的手段进行(目前受制于基础平台的开发进度制约,部分阶段的衔接依然使用人工介入方式进行),整体的结构如下:

  • 一次完整的灰度流程回归 = N次自动化流程编排 + 自动化流程衔接

  • 一次自动化流程编排 = N次自动化用例组 + 自动化用例衔接

  • 1次自动化用例组 = N个自动化用例

  • 1个自动化用例 = N个API接口

一次灰度发布流程

发布灰度实例前  → ...

↓

发布灰度实例后 → ...

↓

中止灰度后 → ...

↓

回滚执行后 → ...

↓

全量发布后 → ...

↓

Loop

观测断言

如果说回归用例的设计是灰度治理核心,灰度流量的断言则决定了整个验证环节的成败,灰度的流量我们可以通过监控进行检测,但每个接口的流量的断言则很繁琐。

图8 - 灰度流量监控图

在本次灰度的可观测性监控上,我们使用了Skywalking作为每次灰度流量监控的观测工具,针对每次请求返回TraceId作为ResponseHeader的一环,最终通过TraceId进行整个全链路流量的断言,断言标准为本次链路数据是否和灰度规则中设置的版本号+发布组相匹配。在实际操作环节中,Skywalking的链路信息会延迟不定的数秒达到,在实际配置时需要特别关注断言的延迟控制(比如延迟30s或者循环断言)。

图9-1 - 断言接口请求体截图

图9-2 - 断言接口结果校验截图

结论

以上5个阶段,在初期设计灰度治理验证中经历了大量的沙盘演练,在后期微服务灰度的迭代过程中,每次版本的发布都可以自动触发整个灰度自动化验证的发起,一次全量回归近千个API的调用和断言,让整个流程成为灰度版本质量的守护者。

在一次完整的验证流程回归中,涉及到自动化的流程衔接和自动化的用例衔接,推动打通CD/Pipline/自动化回归平台实现webhook能力将有助于实现整个流程的自动化程度。在生产环境灰度功能推进上,我们的种子用户1对1事业部的系统规模和复杂度则远远超过的设计预期,经过2个月的接入验收后,我们覆盖了大部分的灰度流量的前后端环节,但在距离真正意义上的全BU白天发布前依旧需要不断验证,如何将这部分经验抽象总结,从点到面,实现更加健壮的隔离环境能力。

无损发布

灰度发布是用精准流量对新的版本进行验证回归,实际上是想解决大流量发布的问题,即会在白天的大流量冲击下也能完成精准流量验证问题,但在白天发布会存在另外一个更加基础的问题,大流量下的服务上下线或者动态扩缩容会在发布时间窗口上出现很多意想不到的问题,我们通过以下这个压测模拟案例进行介绍。

图10 - 一次模拟无损发布压测逻辑图

原理分析

在进行案例分析时,我们先简单从原理分析下发布窗口中可能会存在哪些有损行为以及其背后的原因,再有针对性去优化并验证优化的结论。

图11 - 服务下线造成的流量损失异常截图

预设结论

  • 滚动发布时机 10s× / 30s × / 60s √ / 90s √ ( ×、√表示预设结论)

  • 完成目标:4000次 60s 无损发布次数

  • 注册发现同步

  • 一次服务下线同步时间公式

  • nacos [5s] 同步一次

  • 60s >=(ribbon [30s] + nacos(AP) + UDP)+ TaskDelay

流程验证

我们将上述流程通过压测平台+CD自助扩缩容对服务调用进行验证,并记录其中具体的时间,为了更好地对该流程进行有效验证和覆盖,我们分别通过人工自助介入方式和全自动脚本流程两种方式进行:

  • 人工自助:简单验证服务在上下线、扩缩容环节下是否存在流量损失,人工全程接入,并有效得到一个拉入拉出合适的时间范围。

  • 全自动脚本:根据得到的时间范围内取多个固定的时间,通过自动化脚本实现大量的上下线活动,通过数千次的上下线操作,取得一个可靠性最优的解。

观测验证

在本环节,我们着重进行人工自助的方式验证,通过压测平台输出一个稳定的10分钟流量模拟白天发布的高并发流量,结合压测平台的测试报告、Grafana面板请求Metrics、Skywalking API Metrics和链路明细4个观测点进行验证。

图12 - 压测配置截图

图13 - 压测结果描述

图14 - 接口压测RPM和错误RPM(分钟级)

图15 - 接口压测在链路中出现的异常截图

图16 - 接口压测在链路中出现的慢请求耗时截图

图17 - 接口压测在链路中出现的慢请求耗时截图

结论

经过上述的一系列验证,我们得出一些比较重要的信息,部分功能已经在目前的生产得到解决,但也存在其他急需解决的问题,同时在验证环节,为了应对未来更加复杂的场景,验证工具也需要得到进一步完善,做到更加精细颗粒度的观测,比如:

#服务
ribbon.eager-load.enabled=true
ribbon.eager-load.clients=xxx

#网关
zuul.ignored-services=*
zuul.ribbon.eager-load.enabled=true

关于断言的逻辑和实现,不同于灰度绝对路由,无损发布的断言是一种面向不确定性或者概率性的断言逻辑,在断言过程中,我们更多的是使用大量的重复行为增加概率出现的可能来实现0-1现象,这点也小不同于灰度的按比例流量,最后通过流量的绝对差值(比如1%)来断言按比例的逻辑。

未来

侧重定位成面向验证的架构设计方案而非是平台,在重标准轻设计(约定大于配置)的前提下,让更多角色关注并参与流程和质量的建设而非是具体平台的源码细节建设,在二期建设中,将会迎来更复杂的微服务治理验证场景,既有微服务引擎定位的网关治理、JOB服务,也有工具定位的分布式ID、DAO验证等等,更多话题,敬请期待。

①晚期大众:技术采用生命周期(Technology Adoption LifeCycle)是一个用来衡量用户对某项新技术接受程度的模型。其形状是一个钟形曲线。这一曲线将消费者采用新技术的过程分成五个阶段,分别包括创新者(2.5%)、早期采用者(13.5%)、早期大众(34%)、晚期大众(34%)与落后者(16%)。落后者可供借鉴经验很少,暂不予展示。