无灰度不发布

名词解释

  1. 灰度策略:用于将请求转发到不同节点、服务上的路由规则。
  2. 灰度追踪:随服务调用在请求上下文环境所传递的灰度路由信息。

一、说明

灰度部署可以抽象为函数target = gray_rules(request_context),其中request_context是服务调用的上下文环境,gray_rules实现了灰度路由规则,而target则是经过处理后的目标端地址。

常见的灰度发布策略:

  1. 将流量按比例转发到不同目标端。
  2. 将流量按策略转发到不同目标端。

目前希望通过灰度部署实现正式版部署前的线上验证问题,所以暂不实现按流量比例转发等AB Test场景。通常灰度部署涉及四个基本问题:

  1. 灰度流量识别:如何识别出请求流量是灰度流量还是正常流量。
  2. 灰度策略下发:如何下发灰度策略给服务,更进一步是发送给指定服务还是全部服务。
  3. 灰度策略执行:如何执行灰度策略。
  4. 灰度追踪:灰度流量标识如何在服务调用链中传递。

二、业界方案

2.1、Istio

image.png

2.2、Spring cloud gray

通过gray-admin下方灰度策略到全部服务,sdk实现灰度判断逻辑。
image.png

2.3、总结

  1. 灰度策略下发:灰度策略通过全局控制中心下发到全部节点或服务。
  2. 灰度追踪:灰度追踪需要自定义代码实现并且随着服务调用逐层传递。

三、我们的方案

3.1、灰度发布过程

  1. 联系运维预先建立好灰度部署所需要的域名、k8s service或mq topic。
  2. 联系运维在流量入口处配置灰度策略,例如需要对哪个服务、域名、mq topic进行灰度部署。
  3. 在风火轮中部署灰度服务。
  4. 灰度版本验证。
  5. 灰度版本下线,如果对mq topic进行灰度需要将消息全部消费完后在下线。
  6. 联系运维在流量入口处清除灰度策略。
  7. 风火轮中部署正式版本。

3.2、实现方案

这里我们解决上文提到的灰度部署四个基本问题中的三个:灰度流量识别、灰度策略下发、灰度追踪,而灰度策略执行则由下文“流量层灰度控制”、“数据层灰度控制”介绍。

3.2.1、灰度流量识别

在流量入口处判断request context中是否带有灰度标识grayid,grayid用于标识唯一用户身份,考虑到非登录场景,建议grayid选择设备id等唯一标识符,通过grayid的值来识别流量是否是灰度流量。

3.2.2、灰度策略下发

有别于istio、spring cloud gray的方案,在我们的方案中,灰度策略不进行全局下发而是携带在灰度追踪中。假定本次请求的灰度追踪中含有{type=”dubbo”, name = “serviced”, version = “gray” },当调用ServiceD时则调用ServiceD的gray版本。
灰度策略协议:结合公司目前技术栈,灰度部署支持服务、域名、kafka topic三个层面的灰度部署。现阶段version字段取值只有gray,考虑扩展性增加了version字段,方便后续能够部署多个灰度版本。

  1. [
  2. {
  3. "type": "dubbo",
  4. "name": "${name}", // dubbo application name
  5. "version": "${version}"
  6. },
  7. {
  8. "type": "http",
  9. "name": "${url}", // 域名
  10. "version": "${version}"
  11. },
  12. {
  13. "type": "kafka",
  14. "name": "${topic1}", // Topic名称
  15. "version": "${version}"
  16. },
  17. {
  18. "type": "kafka",
  19. "name": "${topic2}", // Topic名称
  20. "version": "${version}"
  21. },
  22. // ...
  23. ]

3.2.3、灰度追踪

在流量入口处通过grayid识别出灰度流量后,添加灰度策略到request context中,随服务调用逐层传递。
以公司目前访问链路为例解释一下上述过程。假设已经部署服务D的灰度版本, 前端请求的http header中带有灰度标识grayid,kong作为流量第一入口点判断流量是否是灰度流量,如果是灰度流量则设置灰度策略{type=”dubbo”, name = “serviced”, version = “gray” },并在转发请求时添加到http header的grayrules中。服务B接收到请求后,如果在本次请求上下文中调用服务D,则根据灰度策略调用服务D的gray版本。

image.png

3.2.4、流量层灰度控制

如下图,目前公司在用的技术栈及访问链路是:

  1. 大部分服务部署在k8s集群内,少量服务部署在vm中。
  2. http服务与dubbo服务并存。
  3. dubbo目前是2.x版本,dubbo调用不经过k8s service。
  4. k8s service由运维手工配置。
  5. mq使用kafka。

image.png

结合现有系统访问路径,可以抽象出以下几种场景:

  1. kong转发流量
  2. dubbo调用
  3. k8s service转发流量
  4. 其他七层负载转发请求
  5. kafka传递消息

3.2.4.1、Kong转发流量

获取http header中grayid,如果grayid的值等于预定值则注入灰度策略。如果对上游服务进行灰度则转发到灰度上游,否则仍然转发到默认上游。
灰度域名命名规则${domain}-gray.abite.com

3.2.4.2、Dubbo调用

利用dubbo tag机制实现灰度发布。如上图所示,服务B通过dubbo调用服务D,假定服务D部署一个正式版本和一个灰度版本,灰度部署详细过程如下:

  1. 启动服务D灰度版本时设置dubbo tag=gray。
  2. 当服务B调用服务D时,如果request context中带有灰度追踪{ type=”dubbo”, name = “serviced”, version = “gray” }则调用服务D的gray版本。

3.2.4.3、K8s service转发流量

如上图所示,服务B通过k8s service调用服C,假定服务C部署一个正式版本和一个灰度版本。当服务B调用服务C时,如果context request中带有灰度追踪{ type=”http”, name = “servicec”, version = “gray” }则调用服务C的gray版本。

3.2.4.4、其他七层负载转发请求

实现原理、方式同“k8s service转发流量”。

3.2.5、数据层灰度控制

3.2.5.1、Kafka

目前采用同集群多Topic方式进行灰度,Kong&风火轮需要配置相应的灰度Topic和灰度版本号
风火轮需要下发的配置如下:
GRAY_KAFKA_TOPICS=,

生产者
灰度追踪通过Http、Dubbo、Kafka等载体传递并保存在请求上下文的ThreadLocal中,如果当前发送消息对应的Topic在灰度追踪中则在原Topic名称后面加上灰度版本号,即消息会发送到对应的灰度Topic中。同时,生产者在发送灰度消息前需要将灰度追踪注入消息Header中,透传给下游应用。

消费者
消费者应用启动时,根据风火轮配置的灰度Topic和灰度版本号,在消费者订阅原Topic时在原Topic后面加上灰度版本号,即订阅的是灰度Topic。同时,灰度消费者接收到灰度消息时将灰度追踪注入约定好的ThreadLocal变量中。

3.2.5.2、MySQL

暂不统一实现,业务系统根据自身特点实现。

3.2.5.3、ElasticSearch

暂不统一实现,业务系统根据自身特点实现。

3.2.5.4、Redis

暂不统一实现,业务系统根据自身特点实现。

3.3、流量切换

通常灰度版本验证无误后可以在运行时进行流量切换变为正式版本,考虑到尽快实现灰度部署,暂时不实现这个功能。灰度版本切换到正式版本,关键技术点:

  1. 移除dubbo tag变为正式版本。
  2. 修改kafka消费者消费topic到正式topic。
  3. 修改k8s service或nginx指向。

四、挑战

  1. 理解Reqeust Context传递对研发有挑战。
  2. Reqeust Context的全链路改造需要一段时间。
  3. 数据层兼容需研发实现。