一、金丝雀发布的高级功能

如上只简单介绍了一些ingress开源默认支持的Annotation。

日常工作中基于开源ingress-nginx实线的高级功能:

通过修改 nginx.ingress.kubernetes.io/configuration-snippet配置,并且配置正则实现:

  • 当header头中有关键字(foo 或 new)字段的时候,自动将流量转发至new-nginx
  • nginx.ingress.kubernetes.io/configuration-snippet (用于插入 location 块代码段);
  • nginx.ingress.kubernetes.io/server-snippet (用于插入 server 块中的代码段);
#还原环境
[root@master01 6]# kaf ingress-canary.yaml
[root@master01 6]# k delete ing new-nginx-ingress nginx-ingress

#创建yaml文件
[root@master01 6]# vim ingress-canary-adv.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: new-nginx-ingress
  namespace: default
  annotations:
    nginx.ingress.kubernetes.io/configuration-snippet: |
      if ($http_name ~ "^.*foo$|^.*new$") {
        proxy_pass http://new-nginx.default:80;
        break;
      }
spec:
  ingressClassName: nginx
  rules:
  - host: nginx.zhang-qing.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: old-nginx
            port:
              number: 80
      - path: /
        pathType: Prefix
        backend:
          service:
            name: new-nginx
            port:
              number: 80

#重新应用
[root@master01 6]# kaf ingress-canary-adv.yaml

测试验证:

[root@master01 6]#curl -H "name: new" http://nginx.zhang-qing.com
new

[root@master01 6]#curl -H "name: foo" http://nginx.zhang-qing.com
new

[root@master01 6]#curl -H "Host: nginx.zhang-qing.com" http://nginx.zhang-qing.com
old

环境清理

[root@master01 6]#k delete -f ingress-canary-adv.yaml -f ingress-canary.yaml -f ingress-canary-new.yaml

二、阿里开源ingress-nginx实现

2.1 基础理论

Nginx Ingress Controller通过Annotation来支持应用服务的灰度发布机制。

nginx.ingress.kubernetes.io/service-match:该注解用来配置新版本服务的路由匹配规则。

nginx.ingress.kubernetes.io/service-match: |
    <service-name>: <match-rule>
# 参数说明:
# service-name:服务名称,满足match-rule的请求会被路由到该服务中。
# match-rule:路由匹配规则
#
# 路由匹配规则:
# 1. 支持的匹配类型
# - header:基于请求头,支持正则匹配和完整匹配。
# - cookie:基于cookie,支持正则匹配和完整匹配。
# - query:基于请求参数,支持正则匹配和完整匹配。
#
# 2. 匹配方式
# - 正则匹配格式:/{regular expression}/,//表明采用正则方式匹配。
# - 完整匹配格式:"{exact expression}",""表明采用完整方式匹配。

路由匹配规则配置示例:

# 请求头中满足foo正则匹配^bar$的请求被转发到新版本服务new-nginx中。
new-nginx: header("foo", /^bar$/)
# 请求头中满足foo完整匹配bar的请求被转发到新版本服务new-nginx中。
new-nginx: header("foo", "bar")
# cookie中满足foo正则匹配^sticky-.+$的请求被转发到新版本服务new-nginx中。
new-nginx: cookie("foo", /^sticky-.+$/)
# query param中满足foo完整匹配bar的请求被转发到新版本服务new-nginx中。
new-nginx: query("foo", "bar")

nginx.ingress.kubernetes.io/service-weight :该注解用来配置新旧版本服务的流量权重。

nginx.ingress.kubernetes.io/service-weight: |
    <new-svc-name>:<new-svc-weight>, <old-svc-name>:<old-svc-weight>
参数说明:
new-svc-name:新版本服务名称
new-svc-weight:新版本服务权重
old-svc-name:旧版本服务名称
old-svc-weight:旧版本服务权重

配置示例:
nginx.ingress.kubernetes.io/service-weight: |
    new-nginx: 20, old-nginx: 60

2.2 案例

创建老服务的:Deployment、Service。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: old-nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      app: old-nginx
  template:
    metadata:
      labels:
        app: old-nginx
        run: old-nginx
    spec:
      containers:
      - name: old-nginx
        image: registry.cn-hangzhou.aliyuncs.com/abroad_images/old-nginx:latest
        imagePullPolicy: Always
        ports:
        - containerPort: 80
          protocol: TCP
      restartPolicy: Always

---
apiVersion: v1
kind: Service
metadata:
  name: old-nginx
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: old-nginx
  sessionAffinity: None
  type: ClusterIP

Ingress资源创建:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: gray-release
  namespace: default
spec:
  ingressClassName: nginx
  rules:
  - host: nginx.zhang-qing.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: old-nginx
            port:
              number: 80

灰度发布新版本服务

发布一个新版本的Nginx服务并配置路由规则。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: new-nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: new-nginx
  template:
    metadata:
      labels:
        app: new-nginx
        run: new-nginx
    spec:
      containers:
      - name: new-nginx
        image: registry.cn-hangzhou.aliyuncs.com/abroad_images/new-nginx:latest
        imagePullPolicy: Always
        ports:
        - containerPort: 80
          protocol: TCP
      restartPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
  name: new-nginx
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: new-nginx
  sessionAffinity: None
  type: ClusterIP

设置满足特定规则的客户端才能访问新版本服务。以下示例仅请求头中满足 foo=bar 的客户端请求才能路由到新版本服务。

  • 修改如上步骤创建的Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: gray-release
  namespace: default
  annotations:
    nginx.ingress.kubernetes.io/service-match: |
      new-nginx: header("foo", /^bar$/)  # 正则匹配精确值 "bar"
spec:
  ingressClassName: nginx
  rules:
  - host: nginx.zhang-qing.com
    http:
      paths:
      - name: old-version
        path: /
        pathType: Prefix
        backend:
          service:
            name: old-nginx
            port:
              number: 80
      - name: new-version
        path: /
        pathType: Prefix
        backend:
          service:
            name: new-nginx
            port:
              number: 80

测试验证

执行以下命令,访问当前服务:

$ curl -H "Host: nginx.zhang-qing.com" http://nginx.zhang-qing.com
old

执行以下命令,请求头中满足 foo=bar 的客户端请求访问服务:

$ curl -H "Host: nginx.zhang-qing.com" -H "foo: bar"
http://nginx.zhang-qing.com
new