金丝雀发布的本质

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了金丝雀发布的本质相关的知识,希望对你有一定的参考价值。

参考技术A 金丝雀发布在国内也经常被叫做灰度发布。下文将使用”金丝雀发布“这一术语。

金丝雀发布是发布模式的一种。“发布”是什么意思?发布:即宣布,发表。有向外公开的意思。

说到“发布”,就不得不说“部署”。不少人将“发布”与“部署”两个概念混淆。

“部署”又是什么意思?在软件工程领域,“部署”指的是将(编译)打包好的程序发送到目标服务器上,并启动执行。

就是说,部署了,并不一定代表着向用户发布。

如果把软件产品比喻成一舞台剧。部署是将舞台提前布置好,但是幕布是拉上的。而发布则是把观众放进剧场,然后拉开幕布。注意:只有真正“拉开幕布”,才称为发布。

那金丝雀发布又是什么?接着刚刚说的比喻,指的是你并不是一次性将所有的观众都放进剧场。只是有条件的让一部分人进场并拉开幕布。你可以通过这些观众对于舞台剧的评价对舞台剧进行调整改进。最后,再选择合适的时机向所有的人开放消费。

回到技术领域。金丝雀发布就是你已经将程序部署到生产环境了,然后根据流量比例、用户ID、用户地域、用户类型等不同维度的条件,允许用户使用部署到生产环境上的程序上的功能。这个过程中,你可以观察这些”特权“用户的数据,以判断你是否需要对功能进行改进。当数据足以支撑全量发布时,就可以进行全量发布。

这就是我们文章开头强调的:金丝雀发布是发布模式的一种。以下是根据流量比例进行金丝雀发布的图示(来自flagger.app):

Flagger是一种基于K8s的发布控制器。能以较低的成本实现金丝雀发布。本例中,它启动一个V2版本的程序的实例,并”放行“5%的用户请求进入V2版本的实例。

因为软件产品一次性全量发布后,你并不能确保它一定受大众喜爱,所以,一步步的试探用户的喜好的软件产品发布策略成为必然选择。

比起一次性全量发布,金丝雀是一种演进式的发布模式,也可以说是一种业务级别的决策。

说到”目的“,就不得不说与金丝雀发布容易混淆的”蓝绿部署“。

蓝绿部署也是一种发布模式。如下图。它的部署方式与金丝雀发布的部署方式几乎一样。

蓝绿部署与金丝雀发布之间存在两个区别。主要区别是”目的“。蓝绿部署的发布模式的目的是更安全的部署,金丝雀发布的目的是演进式的发布。

次要区别是决策维度的不同。蓝绿部署是技术维度的决策,而金丝雀是业务维度的决策。

如下图展示的是蓝绿部署的决策过程。如果V2版本的实例在生产环境经过多种验证方式验证过,即可把流量全部切到V2版本。在验证期间会保留V1实例,以保证可以随时回滚。

另,至于为什么是叫蓝绿部署(Blue-Green Deployments)而不是蓝绿发布。个人认为是因为从一开始蓝绿部署的出发点是零停机(Zero Downtime),而不是演进式的发布。当然,从名称上也体现了在蓝绿部署和金丝雀发布在”决策维度“上的区别。

参考Martin Fowler关于蓝绿部署的文章: https://martinfowler.com/bliki/BlueGreenDeployment.html

容易与金丝雀发布混淆的,还有”滚动更新“。它是一种将软件程序从一个版本平滑的升级到另一个的版本部署技术。如下图。属于技术决策。与业务无关。与金丝雀发布不是同一个维度的东西。

动态图来自: https://www.bluematador.com/blog/kubernetes-deployments-rolling-update-configuration

在厘清与金丝雀相关的各种概念定义之后,我们从设计者的角度思考金丝雀发布:如果让你设计一个金丝雀发布系统或者平台,你该如何实现?

笔者认为它至少要实现三个接口:

这三个接口与具体实现应该是无关的。比如你可以通过Prometheus实现指标的收集接口,也可以通过AWS的CloudWatch。

同时,金丝雀发布系统还需要一些用户体验性相关的功能,比如出现回滚时进行通知、滚动更新前进行人工审批、滚动更新的步骤大小等等。

金丝雀发布系统所需的接口后,我们发现,由于Service Mesh技术的兴起,让金丝雀发布的实现变得容易了很多。

因为Service Mesh技术天生就支持以上三个接口。所以,行业里一下就出现一些轻量级的发布系统,比如Argo Rollouts和Flagger。我们可以通过以下表格进行对比:

Flagger的三个接口的实现更丰富,几乎完胜Argo Rollouts。Argo Rollouts除了UI,几乎没有优势。

虽说金丝雀的好处是看得见的,但是并不代表,你的每一次发布都能使用它。我们需要清楚的认识到,执行金丝雀发布的过程中,程序存在一个中间状态:就是两个版本同时存在,有时甚至是多个版本。在生产环境,如果你的程序无法同时运行两个版本,你就不能采用金丝雀发布。这个风险需要开发在开发过程就确定的。

所以,我们认为采用金丝雀发布的前提是:开发人员开发出来的程序必须有同时运行多个版本的能力。

而这一能力,对程序员本身的能力也有要求。比如它要求程序员在设计接口和DB schema时考虑向前兼容。在程序员能力不足时,也无法采用金丝雀发布。

金丝雀发布的概念的理解程度,决定了团队是否能采用金丝雀发布,也决定了金丝雀发布系统的设计。

开源的金丝雀系统倾向于基于标准化的Kubernates平台,大概率是因为它更标准,更容易实现。而大多企业的金丝雀系统可能与企业内部系统耦合严重,无法开源。

微服务治理实践 | 金丝雀发布


本文是《微服务治理实践》系列篇的第三篇文章,主要分享 Spring Cloud & Dubbo 微服务框架下的金丝雀发布。


第一篇:
第二篇:



前言



阿里巴巴集团内部有不少故障是因为发布直接或间接引起。因此提升发布的质量,减少错误的发生,是有效减少线上故障的一个关键环节。

为什么大部分的故障和发布相关?因为发布是整个功能更新到线上的最后一个环节,一些研发过程中累计的问题,在最后发布环节才会触发。同时发布本身也是一个复杂的过程,在发布过程中,往往容易出现一些错误操作或者遗漏关键操作。

日常发布中,我们常常会有如下一些错误的想法:

  • 这次改动的内容比较小,而且上线要求比较急,就不需要测试直接发布上线好了

  • 发布不需要走灰度流程,快速发布上线即可

  • 灰度发布没有什么用,就是一个流程而已,发布完就直接发布线上,不用等待观察

  • 虽然灰度发布很重要,但是灰度环境很难搭建,耗时耗力优先级并不高


这些想法都可能让我们进行一次错误的发布。

阿里巴巴内部有安全生产三板斧概念:可灰度、可观测、可回滚。所有研发同学必须要掌握发布系统的灰度、观测和回滚功能如何使用。

今天我们来聊聊灰度发布。


灰度发布策略



灰度发布是发布整个过程中一个非常重要的环境。目前灰度发布策略有这几种:

蓝绿发布(Blue-Green Deployment)


通过部署两套环境来解决新老版本的发布问题。如果新版本( New Version )发生问题要进行回滚的时候,直接通过切流将流量全部切到老版本( Old Version )上。

优点: 升级切换和回退比发布回滚迅速。
缺点: 成本较高,需要部署两套环境。如果新版本中基础服务出现问题,会瞬间影响全网用户;如果新版本有问题也会影响全网用户。

金丝雀发布( Canary Release )

微服务治理实践 | 金丝雀发布

优点: 灵活,策略自定义,可以按照流量或具体的内容进行灰度(比如不同账号,不同参数),出现问题不会影响全网用户
缺点: 没有覆盖到所有的用户导致出现问题不好排查

滚动发布( Rolling Release )

微服务治理实践 | 金丝雀发布

金丝雀发布的一种变化。通过分批发布的方式进行多批发布(比如一共 9 个实例,分 3 批,每次 3 个实例发布),适合大规模应用发布

优点: 出现问题不会影响全网用户,适合大规模应用发布
缺点: 发布和回滚周期较长

本文将介绍 Spring Cloud & Dubbo 微服务框架下的金丝雀发布。


微服务金丝雀发布开源实现



微服务金丝雀发布的核心是服务路由,只要能控制服务路由的逻辑,就能确定流量的走向,确定流量走向也就意味着可以控制路由到任意节点。

目前 Spring Cloud 开源国内有 Nepxion Discovery 这款框架支持灰度发布,或是开发者自己实现 Ribbon 里对应的接口即可。Apache Dubbo 开发者实现 Dubbo 提供的 Router 或 LoadBalance 即可;另外 Dubbo Admin 界面提供了条件路由、标签路由功能也可以完成动态路由功能。

Spring Cloud

有这么一个 Spring Cloud 服务调用场景:
微服务治理实践 | 金丝雀发布

  • Query Parameter 中存在 name=jim 的请求,路由到 192.168.1.1 节点
  • Header 中存在 Test=1 的请求,路由到 192.168.1.2 节点
  • Cookie 中存在 lang=zh-cn 的请求,路由到 192.168.1.3 节点

这些不同类型的请求数据决定着 Spring Cloud 的服务路由逻辑。Spring Cloud 服务路由组件为 Netflix Ribbon(Spring Cloud Hoxton 引入了 Spring Cloud LoadBalancer 用于替换 Netflix Ribbon)。Ribbon 内部的 ILoadBalancer 接口用于获取实例(Server)列表信息,IRule 接口用于获取最终的确定的实例(Server)。因此,如何使用/扩展这两块接口是 Spring Cloud 动态路由的核心。

不论是 Netflix Ribbon 或者 Spring Cloud LoadBalancer,它们在路由的过程中都无法获取 Request 信息,Request 信息存储着用户请求内容中的所有数据。为了这个这个解决,需要引入 ThreadLocal 来透传 Request 数据。

Apache Dubbo

Apache Dubbo 不论是 Router 或者 LoadBalance,它们提供的方法内部都可以获取到 Invocation。有了 Invocation 之后可以获取调用的方法、调用的参数类型、调用的参数值、Attachment。

这些 Invocation 里的内容可以决定过滤掉哪些 Invoker,比如定义了一个 CanaryRouter 对于 getEnv 方法路由到灰度 Invoker,否则路由到其它 Invoker(每个节点注册的时候设置一个自定义的 parameter gray 到 url 中,表示这是一个灰度节点):


 
public class CanaryRouter extends AbstractRouter { @Override public <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException { List<Invoker> normal = new ArrayList<>(); List<Invoker> canary = new ArrayList<>(); for(Invoker invoker : invokers) { if(invoker.getUrl().getParameter("gray", false)) { canary.add(invoker); } else { normal.add(invoker); } } if(invocation.getMethodName().equals("getEnv")) { return canary; } else { return normal; } }}



EDAS 金丝雀发布实践


EDAS 金丝雀发布支持 Apache Dubbo 和 Spring Cloud 微服务框架。金丝雀发布的底层使用 Java Agent 技术通过字节码增强 Apache Dubbo 和 Spring Cloud 的路由逻辑。由于是使用 Agent 技术,而且在抽象类上进行了拦截,这使得对 Apache Dubbo 和 Spring Cloud 的 的版本以及注册中心都有了更全面的支持。

  • 版本: Apache Dubbo 支持 2.5.x, 2.6.x 以及 2.7.x;Spring Cloud 支持 Dalston、Edgware、Finchley 以及 Hoxton(暂不支持 Hoxton 里的 Spring Cloud LoadBalancer 负载均衡组件)
  • 注册中心: 支持 Zookeeper,Consul,Eureka,Nacos,Etcd 等注册中心

下面,我们开始体验 EDAS 上的金丝雀发布。

创建应用

创建 Provider 和 Consumer 应用:

微服务治理实践 | 金丝雀发布



Provider 请注意填写至少 2 个 Pod 数:


微服务治理实践 | 金丝雀发布

全部创建完毕后,调用 consumer 对外暴露的地址确认 consumer 可以顺利调用 provider 服务。

金丝雀发布


Provider 进行应用部署,选择金丝雀发布:

微服务治理实践 | 金丝雀发布

设置新版本号:

微服务治理实践 | 金丝雀发布

设置对应的规则(本文使用按比例灰度,90% 的流量进入灰度实例):

微服务治理实践 | 金丝雀发布

等待金丝雀发布完毕:

微服务治理实践 | 金丝雀发布


金丝雀发布结果验证


金丝雀发布完毕后,再次调用 consumer 对外暴露的地址确认金丝雀是否生效。或者可以在发布单页面中查看灰度监控数据:

微服务治理实践 | 金丝雀发布

微服务治理实践 | 金丝雀发布

这里查看了 3 分钟监控,89.89% 的流量进入了灰度实例(时间越久,比例越准确),基本满足按照比例的灰度部署。

当结果符合预期并进行全量发布时,发布单页面点击 "下一步" 即可:

微服务治理实践 | 金丝雀发布

规则解释

Spring Cloud

Spring Cloud 灰度规则目前支持两种类型:

  • 按比例灰度,可设置 0 - 100 之间的整数
  • 按内容灰度,可以根据参数的内容进行灰度

微服务治理实践 | 金丝雀发布

其中 path 表示请求路径(不填表示支持所有路径),参数类型支持选择 Cookie,Header 或 Parameter,条件支持 "=", ">", "<", ">=", "<=", "!=", 白名单, 取模。

条件列表中的每一项支持 "与" 或者 "或" 运算。

举个例子,这是 consumer 调用 provider 的一个请求:


 
URL: http://custom-provider/goods/queryParameter: goodsId=JL110Header: ENV=Prod

那么规则可以这样设置:

微服务治理实践 | 金丝雀发布

Apache Dubbo
Apache Dubbo 灰度规则目前支持两种类型:
  • 按比例灰度,可设置 0 - 100 之间的整数
  • 按内容灰度,可以根据接口参数的内容进行灰度

其中条件支持 "=", ">", "<", ">=", "<=", "!=", 白名单, 取模。

条件列表中的每一项支持 "与" 或者 "或" 运算。

举个例子,定义一个接口如下:
 
public interface CanaryService { String call(String str, int num1, Integer num2, String[] strArr, Map<String, String> map, List<String> list, User user);}


这是 consumer 调用 provider 的参数信息:
 
str=strnum1=1num2=2strArr=[1, 2, 3]map={"1":"1", "2", "3"}list=[0, 1, 2]User=User{name=name, age=20}


那么规则可以这样设置:
微服务治理实践 | 金丝雀发布

参数 0、1、2 是 String、int 和 Integer 对象,无需设置表达式,直接进行比较。

参数 3 表示一个 String[] 数组,表达式 [0] 表示取第 1 个元素,数组中的第 1 个元素为 1。

参数 4 表示一个 Map<String, String>,表达式 .get("1") 表示取 key 为 "1" 的元素,Map 中 key 为 "1" 的元素是 "1"。

参数 5 表示一个 List 集合,表达式 .get(1)表示取第 2 个元素,数组中的第 2 个元素为 1。

参数 6 表示一个自定义 User 对象(有 name 和 age 两个属性),表达式 .getName() 表示调用 getName() 方法,该方法返回 name 属性的值,这里传递的是 name。

ECS 集群使用

上述内容介绍的是在 K8s 集群下的操作。如若在 ECS 集群下使用,需要确保至少存在 2 个分组(如下截图存在 "默认分组" 和 "gray" 这两个分组):

微服务治理实践 | 金丝雀发布

部署应用,选择金丝雀发布:

微服务治理实践 | 金丝雀发布

上传新的部署包,设置新版本号,选择灰度分组:

微服务治理实践 | 金丝雀发布

后续规则的操作跟 K8s 集群一致。

金丝雀发布后回滚

当金丝雀发布后,发现有 Bug 存在,需要进行应用回滚。

发布单页面直接点击 "立即回滚":



金丝雀发布实现细节



Apache Dubbo 金丝雀底层实现的原理是通过 Java Agent 技术动态添加一个 Router,该 Router 内部使用了 Spring 的表达式 SPEL 进行参数,表达式解析结果判断是否需要对 Invoker 列表进行修改。如果是常规请求,删除灰度节点;如果是灰度请求,删除正常节点。



Spring Cloud 金丝雀底层的实现细节是对 ILoadBalancer 接口对应的实现类返回的 Server 列表进行修改。如果是常规请求,删除灰度节点;如果是灰度请求,删除正常节点。

这里如何判断哪些节点是灰度节点呢?这会跟应用发布流程绑定,当使用 ECS 集群需要选择灰度分组,K8s 集群需要填写灰度 Pod 个数。这些灰度实例或灰度 Pod 启动的时候会带上一个灰度标表示自己是一个灰度实例或 Pod(Spring Cloud 写入到 metadata 持久化到注册中心;Apache Dubbo 写入到 custom parameter 持久化到注册中心)。

另外一个问题: 灰度规则如何保存呢?我们在 Agent 里通过 Nacos Configuration 的监听去监听对应 dataId 的数据变化,这里的 dataId 跟每个应用 id 绑定(应用 id 也通过跟灰度标一样的机制持久化到注册中心)。每次应用发布都会在 id 对应的 dataId 中写入灰度规则,Agent 监听并在内存进行修改。


招贤纳士



我们 Dubbo / Spring Cloud 商业化团队正在招人,除了 EDAS,我们还有 ARMS (应用实时监控服务)、MSE(微服务引擎)、ACM(应用配置管理)、SAE(Serverless 应用引擎)等独立产品。我们在忙什么?用心打磨这些产品,就是我们的工作。团队的目标是将阿里巴巴在服务治理上的最佳实践通过产品化的形式输出给阿里云上的企业客户,帮助客户实现业务永远在线。

简历投递方式:fangjian.fj@alibaba-inc.com



关注我
获取更多
Java干货
原创文章

视频资料

技术交流群



以上是关于金丝雀发布的本质的主要内容,如果未能解决你的问题,请参考以下文章

一文搞懂蓝绿部署和金丝雀发布

干货|采用Istio实现灰度发布(金丝雀发布)

微服务治理实践 | 金丝雀发布

一文搞懂蓝绿部署和金丝雀发布

蓝绿部署滚动部署灰度发布金丝雀发布

various system release [online]