你居然只知道蓝绿发布?今天教你全链路灰度~

本文涉及的产品
应用型负载均衡 ALB,每月750个小时 15LCU
传统型负载均衡 CLB,每月750个小时 15LCU
网络型负载均衡 NLB,每月750个小时 15LCU
简介: 在我上家公司其实只有小型的灰度发布,可能会有人跳起来喊:那你们怎么发布服务的?通过k8s的**滚动发布**来实现无缝上线,如果上线失败呢,通过旧release分支构回去。这其实是灰度里头的**流量比**负载。

前言


Q:谈到灰度发布,你会想起什么

🙋‍♂️:在我上家公司其实只有小型的灰度发布,可能会有人跳起来喊:那你们怎么发布服务的?

通过k8s的滚动发布来实现无缝上线,如果上线失败呢,通过旧release分支构回去。这其实是灰度里头的流量比负载。

谈谈我认知里的灰度发布
  • 灰度发布是什么?

灰度发布(Gray release)是指在黑与白之间,能够平滑过渡的一种发布方式。大白话,就是在新旧版本里面可以丝滑的切换,一看就是德芙吃多了😁

  • 灰度有哪些形式?
灰度方案 长什么样 优缺点
蓝绿发布 有两套一毛一样的环境,在上线的时候,通过切换不同环境,来达到丝滑上线 优点:快捷。缺点:浪费资源
AB Test 通过不同的角色或者用户来进入不同逻辑,进行验证 优点:粒度更小
金丝雀发布 通过流量比将流量负载到不同机器 缺点:灰度粒度比较粗
  • 完善的灰度长什么样子?

把所有优点拿过,拿来主义。蓝绿+AB Test,归纳一下,其实就是流量比+tag

蓝绿跟金丝雀,通过流量比来切换,AB Test通过标识来切换。

全链路灰度建设


全链路灰度是什么?

顾名思义,就是整条调用链路,你想访问哪就访问哪,指哪打哪。我们可以设想想,蓝绿发布是所有应用的灰度,一刀切AB Test是部分用户,部分切全链路发布,其实是需要细化到服务、接口级别灰度,而且贯穿整条链路~

全链路灰度怎么打造

全链路灰度由什么构成


  1. 网关流量染色
  2. 网关层负载均衡
  3. rpc层负载均衡
  4. 上下文透传
  5. 数据隔离(看情况)

流量染色


流量染色,就是我们对符合条件的流量进行标记。那么它有几种方式呢?两种:主动染色、被动染色。

  • 主动染色

比如说,只要你有100W就是土豪,每个人进来的时候进行搜查,你满足条件给你打上土豪tag。

  • 被动染色

这个更好理解了,小明是老赖,我看见小明就知道他是老赖,不需要去翻他资料,对吧。

流量染色怎么实现?

其实很简单,首先我们需要一套规则体系,rule,然后在网关层进行筛选流量,进行打标。技术手段就是往gateway exchange 设置Attributes,然后在网关flux链路传递。

这里我们踩过的坑,为啥不用threadlocal去传递这个标识?

gateway采用flux异步非阻塞实现的,也就是说他们根本不在一个线程去处理。官方推荐就是塞入exchange里头。

网关层负载均衡


关注我的就知道,之前我写过类似的文章,大家可以阅读下。

原理是什么?

我们将染色的流量,路由到对应的服务。技术方案是:在网关流量染色之后,通过Nacos注册中心,改写负载均衡算法,进行路由到对应的服务。

这里的服务概念就比较模糊了,可以是特定版本的服务,比如说V1、V2。也可以是,服务起来之后打对应tag标签,这一类的服务是灰度标识,灰度流量走这边~

RPC层负载均衡


我们常见的RPC有哪些?Feign、RestTemplate、WebClient

其中能负载的是那种根据serviceId来查询服务,如果你通过host、url那种来请求,拜拜了,干不了。(PS:其实也可以改写host,但是不够优雅

怎么改写负载均衡呢?

在高版本,Feign已经不再依赖Ribbon实现负载了,通过loadBalance来实现负载均衡。

重写ReactorServiceInstanceLoadBalancer,实现Feign、RestTemplate负载均衡

Q:怎么实现服务级别的灰度呢?

🙋‍♂️:我们通过Nacos naminServer拿到不同环境namespace的服务列表,你想路由到哪里都行了。

我们可以参考NacosLoadBalancer

image.png

上下文透传


这个其实是APM的内容,比如说上面的Feign,怎么将这个染色的标识传到下一个节点呢?大部分中间件、框架都有拦截器,我们只需要把它打到对应的header头。

Feign

@Bean
public RequestInterceptor headerInterceptor() {
    return template -> {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (null != attributes) {
            HttpServletRequest request = attributes.getRequest();
            Enumeration<String> headerNames = request.getHeaderNames();
            if (headerNames != null) {
                while (headerNames.hasMoreElements()) {
                    String name = headerNames.nextElement();
                    String values = request.getHeader(name);
                    // 跳过 content-length,防止报错Feign报错feign.RetryableException: too many bytes written executing
                    if (name.equals("content-length")) {
                        continue;
                    }
                    template.header(name, values);
                }
            }
        }
    };
}

RestTemplate


import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Enumeration;

@Component
public class RestTemplateInterceptor implements ClientHttpRequestInterceptor {

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
            throws IOException {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (null != attributes) {
            HttpServletRequest httpServletRequest = attributes.getRequest();
            Enumeration<String> headerNames = httpServletRequest.getHeaderNames();
            if (headerNames != null) {
                while (headerNames.hasMoreElements()) {
                    String name = headerNames.nextElement();
                    String values = httpServletRequest.getHeader(name);
                    // 跳过 content-length,防止报错Feign报错feign.RetryableException: too many bytes written executing
                    if (name.equals("content-length")) {
                        continue;
                    }
                    request.getHeaders().add(name, values);
                }
            }
        }
        return execution.execute(request, body);
    }

}

@LoadBalanced
@Bean
public RestTemplate getRestTemplate(RestTemplateInterceptor restTemplateInterceptor) {
    RestTemplate restTemplate = new RestTemplate();
    restTemplate.setInterceptors(List.of(restTemplateInterceptor));
    return restTemplate;
}

当然这只是一部分场景,还有多线程,线程池、Hystrix等等场景。

数据隔离


这个看情况,如果做测试数据灰度的,需要做影子表、影子库进行隔离,重写数据库连接。

这个方案有没有缺点?


答案是肯定的,既然是全链路灰度,网关层如何做灰度?

有人会反驳:网关层一般是稳定的。

没错,网关正常情况下是稳定的,但是在前期会有变动,比如说网关接入权限系统做验证鉴权。

对于开源中间件Discovery也是存在这个问题

# 基于gateway灰度方案,如果网关需要更新新功能,怎么动态更新网关?

image.png

它山之石可以攻玉

我们来看阿里是怎么实现全链路灰度的,其实原理都差不多。

image.png

下一篇:多泳道建设


下一篇我会讲述多泳道建设,它采用的技术跟这个很像,大家敬请期待~

相关实践学习
SLB负载均衡实践
本场景通过使用阿里云负载均衡 SLB 以及对负载均衡 SLB 后端服务器 ECS 的权重进行修改,快速解决服务器响应速度慢的问题
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
相关文章
|
开发框架 Cloud Native Java
深入剖析全链路灰度技术内幕
本文将会揭开全链路灰度的神秘面纱,深入剖析全链路灰度技术内幕,引出两种不同的实现方案,并对实现方案的技术细节进行深入探讨,最后通过实践环节来展示全链路灰度在实际业务中的使用场景。
深入剖析全链路灰度技术内幕
|
Kubernetes Cloud Native Dubbo
全链路灰度的挑战、实现思路与解决方案
全链路灰度的挑战、实现思路与解决方案
1245 21
|
负载均衡 前端开发 Java
聊聊 Spring Cloud 全链路灰度发布 方案~
聊聊 Spring Cloud 全链路灰度发布 方案~
|
9月前
|
Kubernetes Cloud Native 测试技术
使用ASM流量泳道的全链路灰度发布实践
服务网格ASM实现全链路灰度发布:通过流量泳道隔离不同版本环境,配置虚拟服务实现灰度比例控制。从创建泳道、打标签、部署新版本到灰度切流、最终上线及下线旧版。
|
运维 Kubernetes Java
浅析微服务全链路灰度解决方案
帮助应用发布版本过程中更精细化,提高了发布过程中的稳定性。服务转移⾄请求链路上进行流量控制,有效保证了多个亲密关系的服务顺利安全发布以及服务多版本并⾏开发,进⼀步促进业务的快速发展。
浅析微服务全链路灰度解决方案
如何设计可靠的灰度方案
一个较大的业务或系统改动,往往会影响整个产品的用户体验或操作流程。为了控制影响面,可以选取一批特定用户、流程、单据等,只允许这一部分用户或数据按照变更后的新逻辑在系统中流转,而另一部分用户仍然执行变更前的老逻辑。这一步是线上系统灰度方案的起点。
如何设计可靠的灰度方案
|
Kubernetes Cloud Native Java
灰度发布、蓝绿部署、金丝雀都是啥?
在滚动部署中,应用的新版本逐步替换旧版本。实际的部署发生在一段时间内。在此期间,新旧版本会共存,而不会影响功能和用户体验。这个过程可以更轻易的回滚和旧组件不兼容的任何新组件。
灰度发布、蓝绿部署、金丝雀都是啥?
|
Kubernetes 测试技术 微服务
Kruise Rollout 全链路灰度实践
OpenKruise 是一个基于 Kubernetes 的扩展套件,主要聚焦于云原生应用的自动化,比如部署、发布、运维以及可用性防护。本文介绍通过 OpenKruise 构建自动化运维的方式实现基于 Istio 的全链路灰度功能。
48169 252
|
6月前
|
Kubernetes 监控 Java
发布策略:蓝绿部署、金丝雀发布(灰度发布)、AB测试、滚动发布、红黑部署的概念与区别
发布策略:蓝绿部署、金丝雀发布(灰度发布)、AB测试、滚动发布、红黑部署的概念与区别
883 0
|
设计模式 Kubernetes 测试技术
干货分享|使用 Istio 实现灰度发布
Kubernetes 作为基础平台,提供了强大的容器编排能力。但是在其上部署业务和服务治理上,仍然会面对一些复杂性和局限性。在服务治理上,已经有许多成熟的 ServiceMesh 框架用于扩充其能力,如 Istio、Linkerd、Dapr 等。本文将主要介绍如何使用 Istio 扩充 Kubernetes 灰度发布的能力。
干货分享|使用 Istio 实现灰度发布

热门文章

最新文章