APISIX 结合 Istio 实现全链路灰度

2023-09-29 10:11:50 浏览数 (1)

作者:苏万亮,中国移动云能力中心软件研发工程师,专注于云原生、微服务、算力网络等领域。

前言

近期团队想利用Istio对产品做全链路灰度,但是对于产品已经选择Apisix作为网关的场景,出于不改变产品原有架构的考虑,不想再引入Istio Ingress Gateway,而只依赖开源的Apisix又无法对产品做到全链路灰度,因此便调研能否将Apisix和Istio结合实现全链路灰度。本文就介绍一下两种可行的方案,与大家一起探讨学习。

问题介绍

上图是简化的一个产品服务调用关系图。其中,Apisix ingress承接外部流量,根据路由规则请求client服务,client服务还会请求provider服务。现在的目的就是对服务client和provider全部做灰度,实现全链路灰度。而Apisix只能对入口服务client进行灰度,做不到全链路灰度。因此考虑引入Istio来实现全链路灰度,为了不改变产品的原有架构,不引入Istio Ingress Gateway,依旧只使用Apisix ingress一种网关。结合Apisix和Istio实现全链路灰度,在配置具体流量规则时共有两种方案,下面对两种方案做详细介绍。

方案一:网关注入sidecar,重写host

对于Istio的灰度方式,我们知道若一个已注入sidecar的服务所配置的灰度规则(virtualservices和destinationrules)想生效,需要请求访问它的服务同样也注入sidecar(是一个istio envoy)。因此对于入口服务client,若引入Istio Ingress Gateway当然没有问题,Istio Ingress Gateway本身就是一个envoy,现不引入Istio Ingress Gateway的话,就需要对Apisix的数据面注入sidecar,如下图所示。

对于服务client,其正常灰度规则配置如下:

apiVersion: networking.istio.io/v1beta1 kind: DestinationRule metadata: name: client spec: host: client subsets: - labels: version: v0-0-1 name: v0-0-1 - labels: version: v0-0-2 name: v0-0-2 --- apiVersion: networking.istio.io/v1beta1 kind: VirtualService metadata: name: client spec: hosts: - client http: - match: - headers: client_version: exact: v0-0-2 route: - destination: host: client subset: v0-0-2 - route: - destination: host: client subset: v0-0-1

上述规则中,hosts的配置为client(kubernetes的完全限定域名),表示根据访问client服务的请求是否带请求头client_version:v0-0-2决定流量路由到某版本。而对于Apisix来说,一般配置规则为:hosts为www.xxx.com,匹配到某路径转发到client服务。这样Apisix和Istio的规则就匹配不上,导致灰度规则不生效。

因此若想服务client的灰度规则生效的话,则需要在外部流量进入Apisix网关后,重写其host。对于重写host,可使用Apisix的插件proxy-rewrite将请求host(www.xxx.com)改成k8s内部域名(test.ns.svc.cluster.local)。具体配置如下:

apiVersion: apisix.apache.org/v2 kind: ApisixRoute metadata: name: ingress-testclient spec: http: - backends: - serviceName: client servicePort: 8070 match: hosts: - www.xxx.com paths: - /client/* name: testclient-route-default plugins: - config: host: client.wgb.svc.cluster.local enable: true name: proxy-rewrite - config: burst: 1 key: remote_addr key_type: var nodelay: true rate: 1 rejected_code: 503 rejected_msg: limit-req-error enable: true name: limit-req

通过上述配置,外部流量经过Apisix后,其请求头host:www.xxx.com就被重写成host:client.wgb.svc.cluster.local,从而匹配到服务client的virtualservices中的host,灰度规则生效。

上述方案虽然可以实现client服务的灰度,但其很明显的问题就是若client服务本身会根据传入的host值来处理业务,那丢失原有的host,对业务带来的影响就很大。

方案二、各施其职,不重写host

对于Apisix来说,虽然不能对产品做到全链路灰度,但是其针对入口服务,即图中的client服务,是具备灰度发布能力的。因此第二个方案就是将入口服务交给Apisix来灰度,其余非入口服务交给Istio来灰度。

通常一个产品由多个微服务构成的话,其大部分服务都不是入口服务,因此都可以用Istio实现这些服务的灰度发布。Istio的灰度方式不做赘述,以provider服务为例,通常是会部署两个deployment对应其两个版本,一个service关联这两个版本的pod,再通过配置virtualservices和destinationrules即可实现灰度。对于整个产品全链路灰度,最好是能将灰度版本的服务部署方式统一起来,即对于入口服务client,也部署两个deployment对应其两个版本,一个service关联这两个版本的pod,但用Apisix的路由配置来实现灰度。具体方式就是利用ApisixRoute ApisixUpstream实现下发规则路由到不同版本的client服务。Apisix相关配置如下:

apiVersion: apisix.apache.org/v2 kind: ApisixUpstream metadata: name: client spec: subsets: - name: v0-0-2 labels: version: v0-0-2 - name: v0-0-1 labels: version: v0-0-1 --- apiVersion: apisix.apache.org/v2 kind: ApisixRoute metadata: name: ingress-testclient spec: http: - backends: - serviceName: client servicePort: 8070 subset: v0-0-2 match: exprs: - op: Equal subject: name: client_version scope: Header value: v0-0-2 hosts: - testclient.com paths: - /client/* name: route-v2 priority: 1 - backends: - serviceName: client servicePort: 8070 subset: v0-0-1 match: hosts: - testclient.com paths: - /client/* name: route-default

从上述配置可以看出,首先ApisixUpstream根据不同版本的pod的标签创建subset,配置规则与Istio中的destinationrules类似,其次ApisixRoute也只需要定义好匹配规则,选择路由到服务client的某个subset。整个配置同样只需要client服务存在两个deployment对应其两个版本,一个service关联这两个版本的pod。

需要注意的是虽然client服务是用Apisix做灰度,但其也需要注入sidecar,因为provider的灰度规则需要client同样是一个envoy。另外该方案仅针对服务的灰度发布能力,没有对Apisix的数据面注入sidecar,若产品还需要提供完整的服务拓扑或链路追踪功能,对Apisix的数据面注入sidecar即可。

总结

本文针对产品已使用Apisix作为网关,并想在不改变产品原有架构的前提下,对产品的所有服务实现全链路灰度,提出了两种可行的方案。两种方案各有利弊,方案一实现较简单,但需重写host,要考虑产品能否接受,不适用于所有产品;方案二无需重写host,产品都能接受,但灰度逻辑略复杂,配置也略复杂。因此,使用哪种方案需根据产品本身架构来决定。

0 人点赞