Apache APISIX Ingress Controller 中的流量切分

Chao Zhang

更新时间 3/27/2021

流量切分(traffic split)是指将流量按照定义好的规则和比例分摊到多个后端服务,像常见的 API 网关产品(例如 Apache APISIXTraefik)、服务网格 Sidecar Proxy(例如 Envoylinkerd2-proxy),都提供了流量切分的功能,以此来实现细粒度的 金丝雀发布蓝绿部署 等功能。

作为 Kubernetes 集群流量入口,Ingress Controller 自然也需要支持流量切分的功能,在后端应用需要发布时,能够提供逐步切流,回滚的能力,降低应用发布带来的风险。本文先后介绍了 Ingress NginxKong Ingress Controller 中提供的流量切分功能(有时也称为金丝雀发布),之后介绍了流量切分在 Apache APISIX Ingress Controller 中的实现。

(注:为了描述方便,下文用术语 “灰度应用” 表示命中金丝雀发布规则后对应的后端应用和术语“稳定应用”表示金丝雀发布规则未命中时对应的后端应用。例如,在下图中,灰度应用是 “foo-canary”,稳定应用是 “foo”。)

1.png

Ingress Nginx

Ingress Nginx 提供了金丝雀发布的功能,我们可以为 Ingress 资源添加 nginx.ingress.kubernetes.io/canary: “true” 注解来启用该功能,Ingress Nginx 支持使用以下几个注解来自定义金丝雀发布的规则。

  • nginx.ingress.kubernetes.io/canary-by-header

通过某个请求头的值来判断流量应该被转发到灰度应用(值为 always)还是稳定应用(值为 never)。

  • nginx.ingress.kubernetes.io/canary-by-header-value

该注解扩展了 nginx.ingress.kubernetes.io/canary-by-header,通过判断指定请求头的值是否与该注解的值匹配,来决定流量的去向(匹配则转发到灰度应用,否则转发到稳定应用)。

  • nginx.ingress.kubernetes.io/canary-by-header-pattern

该注解和 nginx.ingress.kubernetes.io/canary-by-header 类似,只是匹配采用了 PCRE 兼容的正则表达式。

  • nginx.ingress.kubernetes.io/canary-by-cookie

通过 Cookie 中某个字段的值来判断流量应该被转发到灰度应用(值为 always)还是稳定应用(值为 never)。

  • nginx.ingress.kubernetes.io/canary-weight

为灰度应用设定一个大小位于 [0, 100] 的权重,流量将按照权重在灰度应用和稳定应用之间分配。权重为 0 则所有流量都会被转发到稳定应用;权重为 100 则所有流量都会被转发到灰度应用。

下图的例子将携带 User-Agent 头部匹配 “.Mozilla.” 模版,URI path 前缀为 /get 的请求转发到灰度应用 foo-canary。

1apiVersion: networking.k8s.io/v1beta1
2kind: Ingress
3metadata:
4  annotations:
5      kubernetes.io/ingress.class: nginx
6      nginx.ingress.kubernetes.io/canary: "true"
7      nginx.ingress.kubernetes.io/canary-by-header: "User-Agent"
8      nginx.ingress.kubernetes.io/canary-by-header-pattern: 
9".*Mozilla.*"
10  name: ingress-v1beta1

Kong

Kong 提供了金丝雀发布的插件,并且通过 KongPlugin 这个 CRD 资源将该功能暴露到了 Kong Ingress Controller 中。管理员/用户首先需要创建一个 KongPlugin 对象,填入金丝雀发布的规则,然后在目标 Kubernetes Service 中加入注解 konghq.com/plugins 并赋予该对象的名称;亦或是创建一个 KongClusterPlugin 对象,进而使得该插件在集群内生效。

1apiVersion: configuration.konghq.com/v1
2kind: KongPlugin
3metadata:
4  name: foo-canary
5config: 
6  percentage: 30
7  upstream_host: foo.com
8  upstream_fallback: false
9  upstream_port: 80
10plugin: canary
11---
12apiVersion: v1
13kind: Service
14metadata:
15  name: foo-canary
16  labels:
17    app: foo
18  annotations:
19    konghq.com/plugins: foo-canary
20spec:
21  ports:
22  - port: 80
23    targetPort: 80
24    protocol: TCP
25    name: http
26  selector:
27      app: foo
28      canary: true

上述例子将 foo-canary 这个服务标记为灰度应用,并为其建立了一条金丝雀发布规则,要求 30% 的流量转发到该应用。

Apache APISIX

Apache APISIX 提供的 traffic-split 插件支持配置自定义规则进行流量切分。Apache APISIX Ingress Controller 在此基础之上,结合 ApisixRoute 灵活的路由规则配置,将流量切分实现为了 ApisixRoute 中的第一类功能(无须通过注解定义)。

基于权重

基于权重的流量切分可以通过为单条路由规则配置多个 Kubernetes Service 后端来实现,如:

1apiVersion: apisix.apache.org/v2alpha1
2kind: ApisixRoute
3metadata:
4  name: foo-route
5spec:
6  http:
7  - name: rule1
8    match:
9      hosts:
10      - foo.org
11      paths:
12      - /get*
13    backends:
14    - serviceName: foo-canary
15      servicePort: 80
16      weight: 10
17    - serviceName: foo
18      servicePort: 80
19      weight: 5

上述示例将 ⅔ 的满足 Host 为 foo.org,URI path 前缀为 /get 的请求转发到了 foo-canary 这个 service,剩下 ⅓ 的请求将被路由到 foo。

在实际应用中,可以为灰度应用设定较小的权重,进行小规模的验证,确认没有问题后修改 ApisixRoute 资源,逐步放大其权重,最终将流量全部转发到该灰度应用,完成发布。

基于规则

ApisixRoute 资源允许用户添加路由匹配表达式 - Exprs 字段,来自定义路由匹配;此外,单个 ApisixRoute 资源允许插入多条路由规则,因此基于规则的流量切分被 Apache APISIX Ingress Controller 以一种无缝的方式集成。

1apiVersion: apisix.apache.org/v2alpha1
2kind: ApisixRoute
3metadata:
4  name: foo-route
5spec:
6  http:
7  - name: rule1
8    priority: 1
9    match:
10      hosts:
11      - foo.org
12      paths:
13      - /get*
14    backends:
15    - serviceName: foo
16      servicePort: 80
17  - name: rule2
18    priority: 2
19    match:
20      hosts:
21      - foo.org
22      paths:
23      - /get*
24      exprs:
25      - subject:
26          scope: Query
27          name: id
28        op: In
29        set:
30        - "3"
31        - "13"
32        - "23"
33        - "33"
34    backends:
35    - serviceName: foo-canary
36      servicePort: 80

上述示例,将满足 Host 为 foo.org,URI path 前缀为 /get 的请求,分为两部分:

  • id 参数是 3、13、23、33 其中之一,这部分请求将命中路由规则 rule2,从而被转发到 foo-canary 这一服务;

  • 其他请求将命中路由规则 rule1,从而被转发到 foo 这一服务。

总结

Ingress Nginx 支持基于权重和基于 Header 规则的金丝雀发布,但是需要通过 annotations 的方式进行配置,语义不强;而 Kong 的方案仅支持基于权重进行金丝雀发布,某些场景下无法满足使用需求,且需要多处配置;Apache APISIX Ingress Controller 则较好地同时支持了两种使用场景,并且其提供的路由规则灵活多变,配置简单且易于理解。