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

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介:

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

第一篇:《微服务治理解密》
第二篇:《微服务治理实践:服务查询》

前言

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

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

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

  • 这次改动的内容比较小,而且上线要求比较急,就不需要测试直接发布上线好了
  • 发布不需要走灰度流程,快速发布上线即可
  • 灰度发布没有什么用,就是一个流程而已,发布完就直接发布线上,不用等待观察
  • 虽然灰度发布很重要,但是灰度环境很难搭建,耗时耗力优先级并不高

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

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

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

灰度发布策略

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

  • 蓝绿发布(Blue-Green Deployment)

1

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

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

  • 金丝雀发布(Canary Release)

2

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

  • 滚动发布(Rolling Release)

3

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

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

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

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

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

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

Spring Cloud

有这么一个 Spring Cloud 服务调用场景:

4

  • 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 应用:

5

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

6

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

金丝雀发布

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

设置新版本号:

8

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

9

等待金丝雀发布完毕:
10

金丝雀发布结果验证

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

11

12

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

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

规则解释

Spring Cloud

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

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

14

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

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

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

URL: http://custom-provider/goods/query
Parameter: goodsId=JL110
Header: ENV=Prod

那么规则可以这样设置:

15

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=str
num1=1
num2=2
strArr=[1, 2, 3]
map={"1":"1", "2", "3"}
list=[0, 1, 2]
User=User{name=name, age=20}

那么规则可以这样设置:

16

参数 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" 这两个分组):

17

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

18

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

19

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

金丝雀发布后回滚

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

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

20

金丝雀发布实现细节

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

作者信息:

方剑(花名:洛夜)阿里云智能高级开发工程师,Spring Cloud Alibaba PMC,Apache RocketMQ Committer, Nacos Committer。主要负责 Spring Cloud Alibaba 开源项目及微服务商业化内容,目前关注 Spring Ecosystem, 微服务,云原生等领域。

相关实践学习
使用DAS实现数据库自动SQL优化
本场景介绍如何使用DAS实现数据库自动SQL优化。
SpringMVC框架入门
Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。在使用Spring进行WEB开发时,可以选择使用Spring的SpringMVC框架或集成其他MVC开发框架,如Struts2等。 相关的阿里云产品企业级分布式应用服务 EDAS:企业级分布式应用服务 EDAS(Enterprise Distributed Application Service)是一个应用托管和微服务管理的 PaaS 平台,提供应用开发、部署、监控、运维等全栈式解决方案,同时支持 Spring Cloud、Apache Dubbo(以下简称 Dubbo )等微服务运行环境,助力您的各类应用轻松上云。产品详情: https://www.aliyun.com/product/edas&nbsp;
相关文章
|
6天前
|
存储 监控 API
构建高效微服务架构:后端开发的现代实践
【5月更文挑战第9天】 在本文中,我们将深入探讨如何在后端开发中构建一个高效的微服务架构。通过分析不同的设计模式和最佳实践,我们将展示如何提升系统的可扩展性、弹性和维护性。我们还将讨论微服务架构在处理复杂业务逻辑和高并发场景下的优势。最后,我们将分享一些实用的工具和技术,以帮助开发者实现这一目标。
|
3天前
|
敏捷开发 监控 API
构建高效微服务架构:从理论到实践
【5月更文挑战第18天】 在当今快速发展的软件开发领域,微服务架构已经成为一种流行的设计模式,它通过将大型应用程序分解为一系列小型、独立的服务来提高系统的可伸缩性、弹性和维护性。本文旨在探讨如何从理论走向实践,构建一个高效的微服务架构。文章首先介绍微服务的基本概念和优势,然后详细讨论了在设计和部署微服务时需要考虑的关键因素,包括服务划分、通信机制、数据一致性、容错处理和监控策略。最后,结合具体案例分析,展示如何在现实世界中应用这些原则,确保微服务架构的高效运行。
|
3天前
|
测试技术 持续交付 API
构建高效的微服务架构:后端开发的现代实践
【5月更文挑战第18天】在数字化转型的浪潮中,微服务架构已成为企业追求敏捷、可扩展和容错能力的关键解决方案。本文将深入探讨微服务的核心概念,包括其设计原则、技术栈选择以及实施过程中的挑战与对策。通过对微服务架构实践的详细剖析,旨在为后端开发人员提供一套构建和维护高效微服务系统的实用指南。
|
6天前
|
消息中间件 Go API
基于Go语言的微服务架构实践
随着云计算和容器化技术的兴起,微服务架构成为了现代软件开发的主流趋势。Go语言,以其高效的性能、简洁的语法和强大的并发处理能力,成为了构建微服务应用的理想选择。本文将探讨基于Go语言的微服务架构实践,包括微服务的设计原则、服务间的通信机制、以及Go语言在微服务架构中的优势和应用案例。
|
6天前
|
监控 测试技术 持续交付
构建高效可靠的微服务架构:后端开发的现代实践
【5月更文挑战第14天】 随着数字化转型的浪潮,企业对于灵活、可扩展且高效的后端系统的需求日益增长。本文旨在探讨如何通过微服务架构来实现这些需求,涵盖微服务设计原则、开发流程以及持续集成和部署(CI/CD)的最佳实践。文中还将讨论监控、日志管理与容错机制,以确保系统的可靠性和性能。
|
6天前
|
Kubernetes Cloud Native 持续交付
构建高效稳定的云原生应用:容器编排与微服务治理实践
【5月更文挑战第14天】 随着企业数字化转型的深入,云原生技术以其弹性、敏捷和可扩展的特性成为现代应用开发的首选模式。本文将探讨如何通过容器编排工具如Kubernetes以及微服务架构的有效治理,构建和维护高效且稳定的云原生应用。我们将分析容器化技术的优势,并结合案例讨论在多云环境下实现持续集成、持续部署(CI/CD)的最佳实践,同时解决微服务带来的分布式复杂性问题。通过本文的阐述,读者将获得一套提升系统可靠性和业务连续性的策略框架。
8 0
|
6天前
|
监控 数据库 开发者
构建高效可靠的微服务架构:策略与实践
【5月更文挑战第11天】在当今软件开发的世界中,微服务架构已经成为构建可扩展、灵活且容错的系统的首选方法。本文深入探讨了设计、部署和维护微服务系统时面临的挑战,并提出了一系列实用的策略和最佳实践。我们将从服务的划分原则出发,讨论如何确保每个微服务的自治性,以及如何通过容器化和编排技术实现服务的高效运行。文章还将涉及监控、日志记录和故障恢复的策略,旨在帮助开发人员构建一个既高效又可靠的微服务环境。
16 1
|
6天前
|
缓存 负载均衡 API
微服务架构下的API网关性能优化实践
【5月更文挑战第10天】在微服务架构中,API网关作为前端和后端服务之间的关键枢纽,其性能直接影响到整个系统的响应速度和稳定性。本文将探讨在高并发场景下,如何通过缓存策略、负载均衡、异步处理等技术手段对API网关进行性能优化,以确保用户体验和服务的可靠性。
|
6天前
|
负载均衡 算法 NoSQL
探索微服务架构下的服务发现与治理
【5月更文挑战第9天】 在当今的软件开发领域,微服务架构已成为构建可伸缩、灵活且容错的系统的首选模式。随着服务的增多,如何有效地进行服务发现与治理成为了关键的挑战。本文将深入探讨微服务环境中服务发现的机制和治理策略,分析不同服务发现工具的优缺点,并提出一种基于一致性哈希和健康检查相结合的服务治理方案,旨在提高系统的可用性和性能。
|
6天前
|
监控 API 持续交付
构建高效可靠的微服务架构:策略与实践
【5月更文挑战第8天】在当今快速演进的软件开发领域,微服务架构已经成为实现敏捷开发、持续交付和系统弹性的关键模式。本文将探讨构建一个高效且可靠的微服务系统所必须的策略和最佳实践。我们将从服务的划分与设计原则出发,讨论如何通过容器化、服务发现、API网关以及断路器模式来优化系统的可伸缩性和鲁棒性。此外,我们还将涉及监控、日志管理以及CI/CD流程在确保微服务架构稳定运行中的作用。