灰度发布设计
无灰度不发布
名词解释
灰度策略:用于将请求转发到不同节点、服务上的路由规则。
灰度追踪:随服务调用在请求上下文环境所传递的灰度路由信息。
一、说明
灰度部署可以抽象为函数 target = gray_rules(request_context),其中 request_context 是服务调用的上下文环境,gray_rules 实现了灰度路由规则,而 target 则是经过处理后的目标端地址。
常见的灰度发布策略:
将流量按比例转发到不同目标端。
将流量按策略转发到不同目标端。
目前希望通过灰度部署实现正式版部署前的线上验证问题,所以暂不实现按流量比例转发等 AB Test 场景。通常灰度部署涉及四个基本问题:
灰度流量识别:如何识别出请求流量是灰度流量还是正常流量。
灰度策略下发:如何下发灰度策略给服务,更进一步是发送给指定服务还是全部服务。
灰度策略执行:如何执行灰度策略。
灰度追踪:灰度流量标识如何在服务调用链中传递。
二、业界方案
2.1、Istio
2.2、Spring cloud gray
通过 gray-admin 下方灰度策略到全部服务,sdk 实现灰度判断逻辑。
2.3、总结
灰度策略下发:灰度策略通过全局控制中心下发到全部节点或服务。
灰度追踪:灰度追踪需要自定义代码实现并且随着服务调用逐层传递。
三、我们的方案
3.1、灰度发布过程
联系运维预先建立好灰度部署所需要的域名、k8s service 或 mq topic。
联系运维在流量入口处配置灰度策略,例如需要对哪个服务、域名、mq topic 进行灰度部署。
在风火轮中部署灰度服务。
灰度版本验证。
灰度版本下线,如果对 mq topic 进行灰度需要将消息全部消费完后在下线。
联系运维在流量入口处清除灰度策略。
风火轮中部署正式版本。
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 字段,方便后续能够部署多个灰度版本。
3.2.3、灰度追踪
在流量入口处通过 grayid 识别出灰度流量后,添加灰度策略到 request context 中,随服务调用逐层传递。 以公司目前访问链路为例解释一下上述过程。假设已经部署服务 D 的灰度版本, 前端请求的 http header 中带有灰度标识 grayid,kong 作为流量第一入口点判断流量是否是灰度流量,如果是灰度流量则设置灰度策略{type="dubbo", name = "serviced", version = "gray" },并在转发请求时添加到 http header 的 grayrules 中。服务 B 接收到请求后,如果在本次请求上下文中调用服务 D,则根据灰度策略调用服务 D 的 gray 版本。
3.2.4、流量层灰度控制
如下图,目前公司在用的技术栈及访问链路是:
大部分服务部署在 k8s 集群内,少量服务部署在 vm 中。
http 服务与 dubbo 服务并存。
dubbo 目前是 2.x 版本,dubbo 调用不经过 k8s service。
k8s service 由运维手工配置。
mq 使用 kafka。
结合现有系统访问路径,可以抽象出以下几种场景:
kong 转发流量
dubbo 调用
k8s service 转发流量
其他七层负载转发请求
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 部署一个正式版本和一个灰度版本,灰度部署详细过程如下:
启动服务 D 灰度版本时设置 dubbo tag=gray。
当服务 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、流量切换
通常灰度版本验证无误后可以在运行时进行流量切换变为正式版本,考虑到尽快实现灰度部署,暂时不实现这个功能。灰度版本切换到正式版本,关键技术点:
移除 dubbo tag 变为正式版本。
修改 kafka 消费者消费 topic 到正式 topic。
修改 k8s service 或 nginx 指向。
四、挑战
理解 Reqeust Context 传递对研发有挑战。
Reqeust Context 的全链路改造需要一段时间。
数据层兼容需研发实现。
版权声明: 本文为 InfoQ 作者【星际行者】的原创文章。
原文链接:【http://xie.infoq.cn/article/8322c3e364d06bccd9b0a329f】。文章转载请联系作者。
评论