作者: 赵炳堃(秉钧)
在传统的微服务体系中,Spring Cloud Alibaba 和 Zuul 常被用作配合 Spring Cloud 使用的微服务网关。然而,这些传统的 Java 网关在面对大规模流量的场景下仍存在种种问题。例如 Zuul 由于采用了非异步 IO 的架构,导致了其在面对高流量的情况下容易出现阻塞的现象,Spring Cloud Gateway 也会在流量很大的情况下产生 Full GC 的情况,导致请求 RT 变长,影响用户体验和业务稳定性。因此我们需要寻找一个新的选项,来替代这些传统的微服务网关。
Higress 是阿里巴巴开源的一款下一代云原生微服务网关。Higress 可以对接多种注册中心,包括Nacos/Zookeeper/Eureka 等,能够无缝集成 Spring Cloud 应用,对 Dubbo/Sentinel/OpenSergo 等微服务生态也有着深度的集成。与此同时,Higress 采用 C++内核,相比于传统的 Java 网关来说性能更高,更稳定,对比Spring Cloud Gateway 和 Zuul 来说,性能可以提升至2-4倍。另外,Higress 还天然兼容 K8s 的Ingress/Gateway API 标准,是一款更符合云原生时代标准的微服务网关。
更多性能压测试验,请参考:https://mp.weixin.qq.com/s/45ZAc5CGfND46Ao3lbHefQ
在现代软件架构逐渐走向微服务化、云原生化的过程中,应用的更新和迭代的频率变得越来越快,如何在尽可能保证用户体验不受影响的情况下完成应用的迭代发布就显得至关重要。目前业界普遍采用的几种典型的应用发布策略包括蓝绿发布、金丝雀发布、A/B Testing发布等。接下来本文将介绍如何使用Higress来实现Spring Cloud Alibaba应用发布的最佳实践。
Higress支持将Nacos,Spring Cloud应用部署于K8s集群内,或者独立于K8s进行部署。为了演示方便,本文将Higress,Nacos,Spring Cloud应用都部署在本地K8s集群。
apiVersion: apps/v1
kind: Deployment
metadata:
name: spring-cloud-demo-v1
spec:
replicas: 1
selector:
matchLabels:
app: spring-cloud-demo
template:
metadata:
labels:
app: spring-cloud-demo
spec:
containers:
- name: server
image: higress-registry.cn-hangzhou.cr.aliyuncs.com/samples/spring-cloud-demo:v1
imagePullPolicy: IfNotPresent
env:
# 注册到的nacos的地址
- name: NACOS_REGISTRY_ADDRESS
value: nacos-server.default.svc.cluster.local
# 注册时携带的version元信息
- name: SPRING_CLOUD_NACOS_DEMO_VERSION
value: v1
我们在k8s集群中部署如上Deployment,其中通过NACOS_REGISTRY_ADDRESS和SPRING_CLOUD_NACOS_DEMO_VERSION两个环境变量指定了Nacos的地址以及注册时携带的version元信息。SpringCloud应用的application.properties配置会读取这两个环境变量,如下所示:
spring.cloud.nacos.discovery.server-addr=${NACOS_REGISTRY_ADDRESS}:8848
spring.cloud.nacos.discovery.metadata.version=${SPRING_CLOUD_NACOS_DEMO_VERSION}
Higress支持多种服务来源,包括Nacos/Zookeeper/DNS/固定IP,通过创建Nacos服务来源,Higress就可以发现注册到Nacos上的服务,从而完成转发请求到这些服务上。
进入Higress控制台(http://console.higress.io/),点击 服务来源-创建服务来源 以创建服务来源。这里选择Nacos 2.X,然后填写注册中心的地址,端口,命名空间,服务分组等信息。注册中心的地址可以填写ip或者域名,本文将Nacos部署在本地K8s中,通过K8s service暴露Nacos端口,因此这里填写对应的service域名。
Higress 控制台:http://console.higress.io/
配置好Nacos服务来源后,我们可以在服务列表中看到我们刚刚部署好的应用。
在Higress控制台上点击域名管理-创建域名,创建一条demo.springcloud.com域名用于后续的访问。
点击路由配置-创建路由,创建一条名为demo的路由,域名选择我们刚刚创建好的demo.springcloud.com,目标服务选择我们在1.2中看到的Spring Cloud应用,path配置为/version。
接下来我们就可以用配置好的路由来访问SpringCloud应用了,在请求时需要将demo.springcloud.com域名解析到本地ip,如下所示,可以成功得到返回结果。
注:如果您将Higress的80和443端口通过LoadBalancer的方式暴露出来,这里需要将本地ip替换为对应LoadBalancer的ip,详见Higress快速开始文档。
在蓝绿发布中,有两套相同的运行环境,一套是当前正在使用的生产环境(蓝色环境),另一套是新版本的测试环境(绿色环境)。新版本的代码只在绿色环境中运行,测试通过后,直接将流量切换到绿色环境中,从而完成新版本的上线。与此同时蓝色环境作为热备环境,当绿色环境出现问题需要回滚时,也可以直接将流量全部再切换回蓝色环境。
在本地K8s集群中apply如下资源,以部署v2版本的SpringCloud应用。
apiVersion: apps/v1
kind: Deployment
metadata:
name: spring-cloud-demo-v2
spec:
replicas: 1
selector:
matchLabels:
app: spring-cloud-demo
template:
metadata:
labels:
app: spring-cloud-demo
spec:
containers:
- name: server
image: higress-registry.cn-hangzhou.cr.aliyuncs.com/samples/spring-cloud-demo:v2
imagePullPolicy: IfNotPresent
env:
- name: NACOS_REGISTRY_ADDRESS
value: nacos-server.default.svc.cluster.local
- name: SPRING_CLOUD_NACOS_DEMO_VERSION
value: v2
部署完毕后,我们可以在Higress控制台的服务列表中看到应用已经有两个endpoint了,如下图所示:
部署完v2版本的应用后,我们可以在Nacos控制台(http://localhost:8848/nacos)上看到service-provider这个服务有两个ip,它们的metadata中的version字段分别为v1和v2。Higress可以根据服务的元信息将服务划分为不同的子集(subset),从而将请求转发到新版本或者老版本的应用中去。
在本地K8s集群中apply如下资源,从而根据应用元信息中的version字段将服务划分为v1和v2两个子集。
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: demo
namespace: higress-system
spec:
host: service-provider.DEFAULT-GROUP.public.nacos
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
新版本应用上线后,我们需要把流量全部切到新版本应用中去,这时只需要简单地修改一下我们在1.3中创建的路由即可。我们可以在本地K8s集群中找到如下ingress资源,这对应了我们在1.3中创建的那条路由。
我们直接编辑这条ingress资源,将higress.io/destination这条annotation的value改为service-provider.DEFAULT-GROUP.public.nacos v2,即可将路由的目标服务修改为v2子集。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
higress.io/destination: service-provider.DEFAULT-GROUP.public.nacos v2
higress.io/ignore-path-case: "false"
labels:
higress.io/domain_demo.springcloud.com: "true"
higress.io/resource-definer: higress
name: demo
namespace: higress-system
spec:
ingressClassName: higress
rules:
- host: demo.springcloud.com
http:
paths:
- backend:
resource:
apiGroup: networking.higress.io
kind: McpBridge
name: default
path: /version
pathType: Prefix
我们再发送请求,可以看到此时得到的是v2版本应用的返回结果,如此便实现了新版本的上线发布。
如果发现已上线的新版本出现问题需要回滚,只需要修改ingress路由中的higress.io/destination,将值更改为service-provider.DEFAULT-GROUP.public.nacos v1即可完成回滚。
金丝雀发布是将少量的请求引流到新版本上,因此部署新版本服务只需极小数的实例。验证新版本符合预期后,逐步调整流量权重比例,使得流量慢慢从老版本迁移至新版本,期间可以根据设置的流量比例,对新版本服务进行扩容,同时对老版本服务进行缩容,使得底层资源得到最大化利用。
Higress可以通过一条Ingress注解轻松完成应用的金丝雀发布。我们编辑2.3中的ingress资源,将ingress中的higress.io/destination注解按如下方式进行修改:
metadata:
annotations:
higress.io/destination: |
80% service-provider.DEFAULT-GROUP.public.nacos v1
20% service-provider.DEFAULT-GROUP.public.nacos v2
这样Higress就可以把80%的流量转发到v1版本的应用,将20%的流量转发到v2版本的应用。
连续发送20条请求,可以看到v1和v2的比例符合我们在ingress中配置的比例。随着灰度的进行,可以逐渐调大v2版本的流量比例,最终完成新版本的平滑上线。
A/B测试基于用户请求的元信息将流量路由到新版本,这是一种基于请求内容匹配的灰度发布策略。只有匹配特定规则的请求才会被引流到新版本,常见的做法包括基于HTTP Header和Cookie。基于HTTP Header方式,例如User-Agent的值为Android的请求 (来自安卓系统的请求)可以访问新版本,其他系统仍然访问旧版本。基于Cookie方式,Cookie中通常包含具有业务语义的用户信息,例如普通用户可以访问新版本,VIP用户仍然访问旧版本。
在本示例中,我们通过HTTP header中的User-Agent对流量进行区分,将Android系统的流量转发到v2版本,其他系统的流量仍保持v1版本。首先修改2.3中名叫demo的ingress资源,将higress.io/destination修改为v1版本,代表目前线上的流量全部会打到原来的v1版本:
metadata:
annotations:
higress.io/destination: service-provider.DEFAULT-GROUP.public.nacos v1
当新版本部署完成后,再新建一条如下所示的ingress路由。这里采用正则匹配的方式,当User-Agent中含有Android时,将请求转发到v2版本的服务。
kind: Ingress
metadata:
annotations:
higress.io/destination: service-provider.DEFAULT-GROUP.public.nacos v2
higress.io/canary: "true"
higress.io/canary-by-header: "User-Agent"
higress.io/canary-by-header-pattern: ".*Android.*"
higress.io/ignore-path-case: "false"
labels:
higress.io/domain_demo.springcloud.com: "true"
higress.io/resource-definer: higress
name: demo-ab
namespace: higress-system
spec:
ingressClassName: higress
rules:
- host: demo.springcloud.com
http:
paths:
- backend:
resource:
apiGroup: networking.higress.io
kind: McpBridge
name: default
path: /version
pathType: Prefix
可以看到来自安卓系统的请求被转发到了v2版本,其余系统仍访问v1版本。
当新版本验证完毕需要全量上线时,只需要将demo路由的higress.io/destination注解修改为v2版本,并删除demo-ab路由,这样所有流量就都会访问v2版本了。
Spring Cloud Alibaba社区交流群:钉钉群号2415000986
Higress社区交流群:钉钉群号7685011250
Higress 社区交流钉钉群中有历次 Higress 社区周会录屏,包括本文中提到的结合 Spring Cloud 应用发布的完整实操视频。