ingres-nginx annotations高级配置(灰度和金丝雀发布)


nginx-ingress作为K8S集群对外的流量入口,充当K8S集群内各个service的反向代理。日常工作中我们经常需要对服务进行版本更新升级,为此我们经常使用到的发布方式有滚动升级、分批暂停发布、蓝绿发布以及灰度发布等不同的发布操作。所以下面介绍下,通过配置nginx annotations来实现不同场景下的发布和测试。

发布场景

场景一

假设当前线上环境我们已经有一套服务Service A对外提供7层服务,此时我们新开发了一些新的特性,需要灰度发布上线一个新的版本Service A',但是我们暂时又不希望简单地直接替换掉Service A服务,而是希望将请求头中包含foo=bar或者cookie中包含foo=bar的客户端请求转发到Service A'服务中,待运行一段时间稳定,将所有的流量切换到Service A'服务中后,再平滑地下线掉Service A服务:

场景二

假设当前线上环境我们已经有一套服务Service B对外提供7层服务,此时我们修复了一些问题,需要灰度发布上线一个新的版本Service B',但是我们又不希望简单直接地将所有客户端流量切换到新版本Service B'中,而是希望仅仅切换20%的流量到新版本Service B'中,待运行一段时间稳定,将所有的流量切换到Service B'服务中后,再平滑地下线掉Service B服务:

针对以上多种不同的应用发布需求,nginx Ingress 支持了多种流量切分方式:

  1. 基于Request Header的流量切分,适用于灰度发布以及AB测试场景
  2. 基于Cookie的流量切分,适用于灰度发布以及AB测试场景
  3. 基于服务权重的流量切分,适用于蓝绿发布场景

注解说明

K8S Ingress Controller通过下列Annotation来支持应用服务的不同发布机制,首先需要设置

nginx.ingress.kubernetes.io/canary: "true"

然后根据需要求设定对应的配置

基于Request Header的流量切分

nginx.ingress.kubernetes.io/canary-by-header:用于通知Ingress将请求路由到Canary Ingress中指定的服务的标头。当请求标头设置always为时,它将被路由到Canary。当标头设置never为时,它将永远不会被路由到Canary。对于任何其他值,将忽略标头,并通过优先级将请求与其他Canary规则进行比较。

nginx.ingress.kubernetes.io/canary-by-header-value:要匹配的标头值,用于通知Ingress将请求路由到Canary Ingress中指定的服务。当请求标头设置为此值时,它将被路由到Canary。对于任何其他标头值,标头将被忽略,并且请求与其他Canary规则的优先级进行比较。此注释必须与canary-by-header一起使用。注释是nginx.ingress.kubernetes.io/canary-by-header允许自定义标头值而不是使用硬编码值的扩展。如果nginx.ingress.kubernetes.io/canary-by-header未定义注释,则没有任何效果。

例1:使用canary-by-header

---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-by-header: "new"
  labels:
    app: demo
  name: demo-ingress
  namespace: demo-canary
spec:
  rules:
  - host: canary.example.com
    http:
      paths:
      - backend:
          serviceName: demo-canary
          servicePort: 80
        path: /
---

上面这个示例,当你请求头加入new: always的时候就会访问demo-canary,当标头设置never为时,则不会访问。例如下面的这个curl

curl -s -H "new: always"  http://canary.example.com

下面这个示例使用的是自定义的标头值

例2:使用canary-by-header-value

---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-by-header: "new"
    nginx.ingress.kubernetes.io/canary-by-header-value:"xxx"
  labels:
    app: demo
  name: demo-ingress
  namespace: demo-canary
spec:
  rules:
  - host: canary.example.com
    http:
      paths:
      - backend:
          serviceName: demo-canary
          servicePort: 80
        path: /
---

使用如下请求可以正常访问demo-canary

curl -s -H "new: xxx" http://canary.example.com

基于Cookie的流量切分

nginx.ingress.kubernetes.io/canary-by-cookie:用于通知Ingress将请求路由到Canary Ingress中指定的服务的cookie。当cookie值设置always为时,它将被路由到Canary。当cookie被设置never为时,它将永远不会被路由到Canary。对于任何其他值,cookie将被加入,并且请求与其他Canary规则的优先级进行比较。

---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-by-cookie: "use_under_30_feature"
  labels:
    app: demo
  name: demo-ingress
  namespace: demo-canary
spec:
  rules:
  - host: canary.example.com
    http:
      paths:
      - backend:
          serviceName: demo-canary
          servicePort: 80
        path: /
---

上面的这个示例,当cookie设置为use_under_30_feature为always的时候,则会访问demo-canary

基于服务权重的流量切分

nginx.ingress.kubernetes.io/canary-weight:将路由到金丝雀Ingress中指定的服务的随机请求的整数(0 - 100)百分比。权重为0意味着该Canary规则不会向Canary入口中的服务发送任何请求。权重为100意味着所有请求都将被发送到Ingress中指定的替代服务。示例如下(使用20%的权重):

---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-weight: "20"
  labels:
    app: demo
  name: demo-ingress
  namespace: demo-canary
spec:
  rules:
  - host: canary.example.com
    http:
      paths:
      - backend:
          serviceName: demo-canary
          servicePort: 80
        path: /
---

注意这里权重不是一个精确的百分比,使用过程当中,只是会看到一个近似分布。

三种注释的优先级和注意点

上面的规则按优先顺序进行评估。优先顺序如下:
canary-by-header -> canary-by-cookie -> canary-weight

注意 当您将入口标记为canary时,除了nginx.ingress.kubernetes.io/load-balance和之外,所有其他非canary注释都将被忽略(从相应的主入口继承)nginx.ingress.kubernetes.io/upstream-hash-by

已知的限制 目前,每个Ingress规则最多可以应用一个canary入口。