用nginx-ingress实现金丝雀发布

436 阅读4分钟

参考1:www.xubaojin.com/post/391.ht… 参考2:www.cnblogs.com/klvchen/art…

关于nginx-ingress注解

我们通过给 Ingress 资源指定 Nginx Ingress 所支持的一些 annotation 可以实现金丝雀发布,需要给服务创建两个 Ingress,一个正常的 Ingress,另一个是带 nginx.ingress.kubernetes.io/canary: "true" 这个固定的 annotation 的 Ingress,我们姑且称它为 Canary Ingress,一般代表新版本的服务,结合另外针对流量切分策略的 annotation 一起配置即可实现多种场景的金丝雀发布,以下对这些 annotation 详细介绍下:

nginx.ingress.kubernetes.io/canary-by-header: 表示如果请求头中包含这里指定的 header 名称,并且值为 always 的话,就将该请求转发给该 Ingress 定义的对应后端服务;如果值为 never 就不转发,可以用于回滚到旧版;如果是其它值则忽略该 annotation。

nginx.ingress.kubernetes.io/canary-by-header-value: 这个可以作为 canary-by-header的补充,允许指定请求头的值可以自定义成其它值,不再只能是 always 或 never;当请求头的值命中这里的自定义值时,请求将会转发给该 Ingress 定义的对应后端服务,如果是其它值则将会忽略该 annotation。

nginx.ingress.kubernetes.io/canary-by-header-pattern: 这个与上面的 canary-by-header-value 类似,唯一的区别是它是用正则表达式对来匹配请求头的值,而不是只固定某一个值;需要注意的是,如果它与 canary-by-header-value 同时存在,这个 annotation 将会被忽略。

nginx.ingress.kubernetes.io/canary-by-cookie: 这个与 canary-by-header 类似,只是这个用于 cookie,同样也是只支持 always 和 never 的值。

nginx.ingress.kubernetes.io/canary-weight: 表示 Canary Ingress 所分配流量的比例的百分比,取值范围 [0-100],比如设置为 10,意思是分配 10% 的流量给 Canary Ingress 对应的后端服务。

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

注意: 当 Ingress 被标记为 Canary Ingress 时,除了nginx.ingress.kubernetes.io/load-balance和 nginx.ingress.kubernetes.io/upstream-hash-by 之外,所有其他非 Canary 注释都将被忽略。

实践总结:

虽然我们使用 Nginx Ingress 实现了几种不同姿势的金丝雀发布,但还存在一些缺陷:

  1. 相同服务的 Canary Ingress 只能定义一个,所以后端服务最多支持两个版本。
  2. Ingress 里必须配置域名,否则不会有效果。
  3. 即便流量完全切到了 Canary Ingress 上,旧版服务也还是必须存在,不然会报错。

实现

Ingress-Nginx 在 0.21 版本引入了 Canary 功能,可以为网关入口配置多个版本的应用程序,使用annotation来控制多个后端服务的流量分配。

如果想启用Canary功能,要先设置 nginx.ingress.kubernetes.io/canary: "true",然后可以启用以下注释来配置Canary

nginx.ingress.kubernetes.io/canary-weight 请求到 Canary ingress 中指定的服务的请求百分比,值为 0-100 的整数,根据设置的值来决定大概有百分之多少的流量会分配Canary Ingress中指定的后端服务。

先说测试结论

可以实现金丝雀发布,但是是入口网关级别的,只能针对配置了 ingress nginx 的那个 svc 服务,不是接入流量的普通 K8S svc 并适用。

测试

mkdir -p /data/yaml/echoserver && cd /data/yaml/echoserver

cat echoserverv1.yaml 
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
  labels:
    app: echoserverv1
  name: echoserverv1
  namespace: echoserver
spec:
  rules:
  - host: echo.chulinx.com
    http:
      paths:
      - pathType: Prefix
        path: /
        backend:
          service:
            name: echoserverv1
            port: 
              number: 8080
---
kind: Service
apiVersion: v1
metadata:
  name:  echoserverv1
  namespace: echoserver
spec:
  selector:
    name:  echoserverv1
  type:  ClusterIP
  ports:
  - name:  echoserverv1
    port:  8080
    targetPort:  8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name:  echoserverv1
  namespace: echoserver
  labels:
    name: echoserverv1
spec:
  replicas: 1
  selector:
     matchLabels:
         name: echoserverv1
  template:
    metadata:
      labels:
        name: echoserverv1
    spec:
      containers:
      - image:  mirrorgooglecontainers/echoserver:1.10
        name:  echoserverv1 
        ports:
        - containerPort:  8080
          name:  echoserverv1

cat echoserverv2.yaml 
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/canary: "true"              # 开启canary功能
    nginx.ingress.kubernetes.io/canary-weight: "50"         # 将v2版本的权重设置为50%,这个百分比并不能精确的将请求平均分配到两个版本的服务,而是在50%上下浮动
  labels:
    app: echoserverv2
  name: echoserverv2
  namespace: echoserver
spec:
  rules:
  - host: echo.chulinx.com
    http:
      paths:
      - pathType: Prefix
        path: /
        backend:
          service:
            name: echoserverv2
            port: 
              number: 8080
---
kind: Service
apiVersion: v1
metadata:
  name:  echoserverv2
  namespace: echoserver
spec:
  selector:
    name:  echoserverv2
  type:  ClusterIP
  ports:
  - name:  echoserverv2
    port:  8080
    targetPort:  8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name:  echoserverv2
  namespace: echoserver
  labels:
    name: echoserverv2
spec:
  replicas: 1
  selector:
     matchLabels:
         name: echoserverv2
  template:
    metadata:
      labels:
        name: echoserverv2
    spec:
      containers:
      - image:  mirrorgooglecontainers/echoserver:1.10
        name:  echoserverv2 
        ports:
        - containerPort:  8080
          name:  echoserverv2


kubctl apply -f echoserverv1.yaml -f echoserverv2.yaml

hhhhhh.png

jjjjjjj.png