Nginx Ingress 支持基于注解(Annotation)实现灰度发布和蓝绿发布。本文为您介绍如何使用 Nginx Ingress 实现蓝绿发布和灰度发布。
灰度发布,也称为金丝雀发布(Canary Release),是一种软件发布的策略,它允许开发者逐步地将新版本软件部署到生产环境中,而不是一次性发布。这样做的目的是为了降低软件发布时的风险,通过在小范围内先行发布来观察软件的表现,确保新版本稳定之后再全面推广。
灰度发布的主要特点包括:
灰度发布对于确保软件质量和用户体验非常重要,特别是在大规模和复杂的系统中。它使得团队能够更加灵活和安全地管理软件发布过程,减少潜在的风险。
Nginx Ingress 支持通过配置 Annotation 实现不同场景下的业务发布,包括:灰度发布、蓝绿发布、A/B 测试等。发布流程如下:
nginx.ingress.kubernetes.io/canary: "true"
注解的 Ingress,被称为 Canary Nginx Ingress。Nginx Ingress 支持通过配置以下 Annotation,实现灰度发布:
nginx.ingress.kubernetes.io/canary-by-header
:基于 Header 的流量切分策略,适用于灰度发布。如果 Header 中包含指定的名称,并且值为 “always”,就将该请求转发给 Canary Nginx Ingress 指定的后端服务。nginx.ingress.kubernetes.io/canary-by-header-value
:与canary-by-header
搭配使用,用于 Header 的取值,包含但不限于“always”
或“never”
。当 Header 的值命中自定义值时,请求将会转发给 Canary Nginx Ingress 指定的后端服务。nginx.ingress.kubernetes.io/canary-by-header-pattern
:与canary-by-header-value
使用方法类似,区别为该 Annotation 用正则表达式匹配 Header 的值,而不是固定值。说明
如果该 Annotation 与canary-by-header-value
同时存在,则该Annotation
将被忽略。
nginx.ingress.kubernetes.io/canary-by-cookie
:基于 Cookie 的流量切分策略,适用于灰度发布。如果 Header 中 Cookie 包含指定的名称,且值为“always”
或“never”
,就将该请求转发给 Canary Nginx Ingress 指定的后端服务。Cookie 流量切分策略无法自定义取值。nginx.ingress.kubernetes.io/canary-weight
:基于服务权重的流量切分策略,适用于蓝绿发布。表示 Canary Nginx Ingress 所分配流量的百分比,取值范围为 0~100。当设置为 100 时,表示所有流量都将转发给 Canary Nginx Ingress 指定的后端服务。说明
canary-by-header
> canary-by-cookie
> canary-weight
。nginx.ingress.kubernetes.io/load-balance
和nginx.ingress.kubernetes.io/upstream-hash-by
外,其他非Canary
的注解都会被忽略。apiVersion: apps/v1 kind: Deployment metadata: name: nginx-old spec: replicas: 1 selector: matchLabels: app: nginx-old template: metadata: labels: app: nginx-old spec: containers: - name: nginx image: doc-cn-beijing.cr.volces.com/vke/nginx-demo:v2.0 ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: service-old spec: selector: app: nginx-old ports: - name: rule protocol: TCP port: 80 targetPort: 80 type: NodePort
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-new spec: replicas: 1 selector: matchLabels: app: nginx-new template: metadata: labels: app: nginx-new spec: containers: - name: nginx image: doc-cn-beijing.cr.volces.com/vke/nginx-demo:v3.0 ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: service-new spec: selector: app: nginx-new ports: - name: rule protocol: TCP port: 80 targetPort: 80 type: NodePort
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: ingress-old # 路由规则的名称 spec: ingressClassName: nginx # 指定 Ingress Controller rules: - host: example.com # 需要对外提供访问的域名 http: paths: - pathType: Prefix # 路径匹配规则,默认为 Prefix(前缀匹配) path: / # 请求匹配的路径 backend: service: name: service-old # 需要对接的服务名称 port: number: 80 # 需要对接服务的端口号
<EXTERNAL_IP>
为 Nginx Ingress 对外暴露的 IP 地址。curl -H "Host: example.com" http://<EXTERNAL_IP>
预期结果如下,访问到了后端旧版本的应用。
This is the old version of nginx
Nginx Ingress 支持基于 Header、Cookie 和服务权重三种流量切分策略,实现灰度发布。
注意
Region
且值为beijing
时,请求转发到新版本服务。apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: ingress-new # 路由规则的名称 annotations: nginx.ingress.kubernetes.io/canary: "true" # 启用 Canary nginx.ingress.kubernetes.io/canary-by-header: "Region" # 配置基于 Header 的流量切分策略 nginx.ingress.kubernetes.io/canary-by-header-pattern: "beijing" # Header 中包含 Region 且值为 beijing 的请求进行转发 spec: ingressClassName: nginx # 指定 Ingress Controller rules: - host: example.com # 需要对外提供访问的域名 http: paths: - pathType: Prefix # 路径匹配规则,默认为 Prefix(前缀匹配) path: / # 请求匹配的路径 backend: service: name: service-new # 需要对接的服务名称 port: number: 80 # 需要对接服务的端口号
Region
不存在时,转发到旧版本服务。curl -H "Host: example.com" http://<EXTERNAL_IP> This is the old version of nginx
Region
存在且值不为beijing
时,转发到旧版本服务。curl -H "Host: example.com" -H "Region: shanghai" http://<EXTERNAL_IP> This is the old version of nginx
Region
存在且值为beijing
时,转发到新版本服务。curl -H "Host: example.com" -H "Region: beijing" http://<EXTERNAL_IP> This is the new version of nginx
Cookie
的值为beijing
时,请求转发到新版本服务。apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: ingress-new # 路由规则的名称 annotations: nginx.ingress.kubernetes.io/canary: "true" # 启用 Canary nginx.ingress.kubernetes.io/canary-by-cookie: "beijing" # 配置基于 Cookie 的流量切分策略 spec: ingressClassName: nginx # 指定 Ingress Controller rules: - host: example.com # 需要对外提供访问的域名 http: paths: - pathType: Prefix # 路径匹配规则,默认为 Prefix(前缀匹配) path: / # 请求匹配的路径 backend: service: name: service-new # 需要对接的服务名称 port: number: 80 # 需要对接服务的端口号
Cookie
不存在时,转发到旧版本服务。curl -H "Host: example.com" http://<EXTERNAL_IP> This is the old version of nginx
Cookie
存在且不为beijing
时,转发到旧版本服务。curl -s -H "Host: example.com" --cookie "shanghai=always" http://<EXTERNAL_IP> This is the old version of nginx
Cookie
存在且为beijing
时,转发到新版本服务。curl -s -H "Host: example.com" --cookie "beijing=always" http://<EXTERNAL_IP> This is the new version of nginx
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: ingress-new # 路由规则的名称 annotations: nginx.ingress.kubernetes.io/canary: "true" # 启用 Canary nginx.ingress.kubernetes.io/canary-weight: "25" # 配置基于服务权重的流量切分策略,转发权重为 25,表示将 25% 的流量转发到新版本服务 spec: ingressClassName: nginx # 指定 Ingress Controller rules: - host: example.com # 需要对外提供访问的域名 http: paths: - pathType: Prefix # 路径匹配规则,默认为 Prefix(前缀匹配) path: / # 请求匹配的路径 backend: service: name: service-new # 需要对接的服务名称 port: number: 80 # 需要对接服务的端口号
for i in {1..20}; do curl -H "Host: example.com" http://<EXTERNAL_IP>; done;
预期结果如下,有 25% 的流量转发到了新版本服务。
This is the old version of nginx This is the new version of nginx This is the old version of nginx This is the new version of nginx This is the old version of nginx This is the old version of nginx This is the old version of nginx This is the new version of nginx This is the old version of nginx This is the old version of nginx This is the old version of nginx This is the old version of nginx This is the old version of nginx This is the new version of nginx This is the old version of nginx This is the old version of nginx This is the old version of nginx This is the old version of nginx This is the old version of nginx This is the new version of nginx
说明
由于样本量较小(20 次),实际验证时可能会出现局部的数据偏差,属于正常情况。您可以多验证几次,或扩大样本数量。
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: ingress-new # 路由规则的名称 annotations: nginx.ingress.kubernetes.io/canary: "true" # 启用 Canary nginx.ingress.kubernetes.io/canary-weight: "100" # 配置新服务权重为 100%,表示蓝绿发布 spec: ingressClassName: nginx # 指定 Ingress Controller rules: - host: example.com # 需要对外提供访问的域名 http: paths: - pathType: Prefix # 路径匹配规则,默认为 Prefix(前缀匹配) path: / # 请求匹配的路径 backend: service: name: service-new # 需要对接的服务名称 port: number: 80 # 需要对接服务的端口号
for i in {1..20}; do curl -H "Host: example.com" http://<EXTERNAL_IP>; done;
预期结果如下。所有的的流量均转发到了新版本服务,同时保留了旧版本服务,可以根据需求进行流量切换和回滚。
This is the new version of nginx This is the new version of nginx This is the new version of nginx This is the new version of nginx This is the new version of nginx This is the new version of nginx This is the new version of nginx This is the new version of nginx This is the new version of nginx This is the new version of nginx This is the new version of nginx This is the new version of nginx This is the new version of nginx This is the new version of nginx This is the new version of nginx This is the new version of nginx This is the new version of nginx This is the new version of nginx This is the new version of nginx This is the new version of nginx