首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Ingress-nginx灰度发布功能详解

Ingress-nginx灰度发布功能详解

作者头像
用户5166556
发布2020-08-05 15:09:15
1.8K0
发布2020-08-05 15:09:15
举报

灰度发布使用背景

最近公司一直在推进DevOps,主要目标是减少对个人的依赖,降低团队之间的损耗,在保证质量的前提下,快速交付价值。在实际执行过程中表现出来的就是服务拆分粒度尽可能细,服务每次上线功能尽可能少,发布节奏尽可能快; 服务必须做到可灰度、可监控、可回滚。至于监控先暂且不聊,如何做到灰度发布升级以及回滚呢?整个PaaS平台是基于Kubernetes进行建设,Kubernetes资源对象Deployment可以做到滚动升级的功能,但并没有提供暂停点机制,即没有办法快捷方便的进行灰度功能的针对性测试。而灰度能力是业务快速发布过程中不可或缺的一种能力,如果出现问题,灰度能够保证其影响范围。

灰度发布概念介绍

灰度发布是指产品迭代过程中能够按照某种策略对新版本进行线上功能的测试,先推广给一部分人使用和测试,如果满足预期,平滑升级;如有问题,修改、回退....保证其影响范围及系统稳定性、降低生产环境引入新软件带来的风险。常见的有金丝雀发布、蓝绿部署和A/B测试等,可根据业务场景选择发布方式。

ingress-nginx灰度发布介绍

因为平台中暂未使用ServiceMesh,所以就Kubernetes入口控制器Ingress-Controller下手,寻找灰度发布的功能,

https://github.com/kubernetes/ingress-nginx/blob/master/Changelog.md

可以看出Ingress-nginx0.21.0版本开始支持金丝雀发布功能,具体如下图:

注解划分

官网介绍

https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/#canary

具体支持的发布功能如下所示:

名称

类型

解释

nginx.ingress.kubernetes.io/canary

"true","false"

开启灰度发布功能,如果没有开启此属性,则如下属性不生效。

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

string

基于 Request Header 的流量切分,适用于灰度发布以及 A/B 测试。当 Request Header 设置为 always时,请求将会被一直发送到 Canary 版本;当 Request Header 设置为 never时,请求不会被发送到 Canary 入口;对于任何其他 Header 值,将忽略 Header,并通过优先级将请求与其他金丝雀规则进行优先级的比较。

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

string

要匹配的 Request Header 的值,用于通知 Ingress 将请求路由到 Canary Ingress 中指定的服务。当 Request Header 设置为此值时,它将被路由到 Canary 入口。该规则允许用户自定义 Request Header 的值,必须与上一个 annotation (即canary-by-header)一起使用。

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

string

正则表达式匹配的 Request Header 的值,用于通知 Ingress 将请求路由到 Canary Ingress 中指定的服务。当 Request Header 设置为此值时,它将被路由到 Canary 入口。该规则允许用户自定义 Request Header 的值,必须与上一个 annotation (即canary-by-header)一起使用,如果已经使用canary-by-header-value,那么此属性将被忽略。

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

string

基于 Cookie 的流量切分,适用于灰度发布与 A/B 测试。用于通知 Ingress 将请求路由到 Canary Ingress 中指定的服务的cookie。当 cookie 值设置为 always时,它将被路由到 Canary 入口;当 cookie 值设置为 never时,请求不会被发送到 Canary 入口;对于任何其他值,将忽略 cookie 并将请求与其他金丝雀规则进行优先级的比较。

nginx.ingress.kubernetes.io/canary-weight

number

基于服务权重的流量切分,适用于蓝绿部署,权重范围 0 - 100 按百分比将请求路由到 Canary Ingress 中指定的服务。权重为 0 意味着该金丝雀规则不会向 Canary 入口的服务发送任何请求。权重为 100 意味着所有请求都将被发送到 Canary 入口。

注意:金丝雀规则按照如下优先级进行匹配:canary-by-header -> canary-by-cookie -> canary-weight

类型划分

如果按照类型可以划分为权重(一定百分比的流量路由到金丝雀版本)和用户自定义(根据用户自定义header/cookie路由到指定版本)两种金丝雀发布方式,具体如下图所示:

Ingress-nginx灰度功能测试

测试环境说明

  • ingress版本:nginx-ingress-controller:0.32.0 所有部署方式见:https://kubernetes.github.io/ingress-nginx/deploy/,部署方式非常简单。
  • Kubernetes版本: 1.15
  • 服务使用spring-boot,其中这两个版本的服务需要通过labeldeployment名称区分开,当然也可以使用命名空间进行严格隔离。

如下分别进行展示灰度发布的具体使用,其中灰度发布注解属性中的key可以为任意合法值,如下举例为canary,另外每次对资源文件修改后,需要kubectl apply重新加载资源。

金丝雀版本

  • 服务代码:
@GetMapping({ "/vets" })
    public @ResponseBody Vets showResourcesVetList() {
        // Here we are returning an object of type 'Vets' rather than a collection of Vet
        // objects so it is simpler for JSon/Object mapping
        Vets vets = new Vets();
        Vet vet = new Vet();
        vet.setId(1);
        vet.setFirstName("gray");
        vet.setLastName("canary");
        vets.getVetList().add(vet);
        return vets;
    }
  • svc编排文件
apiVersion: v1
kind: Service
metadata:
  name: spring-boot-service-gray
  labels:
    app: spring-boot-gray
spec:
  ports:
  - name: http
    port: 80
    targetPort: 8080
  selector:
    app: spring-boot-gray
  • ingress编排文件
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: spring-boot-gray
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/ssl-redirect: "false"
    nginx.ingress.kubernetes.io/use-forwarded-headers: "true"
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-by-header: "canary"
    nginx.ingress.kubernetes.io/canary-by-header-value: "abcde"
spec:
  tls:
    - hosts:
      - spring.boot.sc.com
      secretName: boot
  rules:
   - host: spring.boot.sc.com
     http:
      paths:
      - path: /
        backend:
          serviceName: spring-boot-service-gray
          servicePort: 80

production版本

  • 服务代码
 @GetMapping({ "/vets" })
    public @ResponseBody Vets showResourcesVetList() {
        Vets vets = new Vets();
        vets.getVetList().addAll(this.vets.findAll());
        return vets;
    }
  • svc编排文件
apiVersion: v1
kind: Service
metadata:
  name: spring-boot-service
  labels:
    app: spring-boot
spec:
  ports:
  - name: http
    port: 80
    targetPort: 8080
  selector:
    app: spring-boot
  • ingress编排文件
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: spring-boot
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/ssl-redirect: "false"
    nginx.ingress.kubernetes.io/use-forwarded-headers: "true"
spec:
  tls:
    - hosts:
      - spring.boot.sc.com
      secretName: boot
  rules:
   - host: spring.boot.sc.com
     http:
      paths:
      - path: /
        backend:
          serviceName: spring-boot-service
          servicePort: 80

按照请求header执行测试

正如上面表格中解释Request Header设置为neveralwayscurl -H'canary: always' http://spring.boot.sc.com/vets,请求将不会一直被发送到Canary 版本。

curl http://spring.boot.sc.com/vets

返回production环境数据:

{"vetList":[{"id":1,"firstName":"James","lastName":"Carter","specialties":[],"nrOfSpecialties":0,"new":false},{"id":2,"firstName":"Helen","lastName":"Leary","specialties":[{"id":1,"name":"radiology","new":false}],"nrOfSpecialties":1,"new":false},{"id":3,"firstName":"Linda","lastName":"Douglas","specialties":[{"id":3,"name":"dentistry","new":false},{"id":2,"name":"surgery","new":false}],"nrOfSpecialties":2,"new":false},{"id":4,"firstName":"Rafael","lastName":"Ortega","specialties":[{"id":2,"name":"surgery","new":false}],"nrOfSpecialties":1,"new":false},{"id":5,"firstName":"Henry","lastName":"Stevens","specialties":[{"id":1,"name":"radiology","new":false}],"nrOfSpecialties":1,"new":false},{"id":6,"firstName":"Sharon","lastName":"Jenkins","specialties":[],"nrOfSpecialties":0,"new":false}]}
curl -H'canary: abcde' http://spring.boot.sc.com/vets

返回金丝雀版本数据:{"vetList":[{"id":1,"firstName":"gray","lastName":"canary","specialties":[],"nrOfSpecialties":0,"new":false}]}

按照请求权重执行测试(按照流量进行灰度,这种方式尤为有用)

金丝雀Ingress编排文件annotations添加如下注解:

nginx.ingress.kubernetes.io/canary-weight: "50"

再次执行会发现按照半对半比例分别分配到金丝雀环境和production环境。

按照模糊匹配执行测试(A/B发布场景下,这种方式尤为有用)

金丝雀Ingress编排文件annotations添加如下注解,其中正则表达式使用的是PCRE库:

nginx.ingress.kubernetes.io/canary-by-header-pattern: "(?=13713876543|13713876544|13713876545)"

同时删除:

nginx.ingress.kubernetes.io/canary-by-header: "canary"

再次执行curl -H'canary: 13713876543' http://spring.boot.sc.com/vets,会按照正则表达式匹配内容路由到相应服务。

按照cookie匹配执行测试

与基于Request Headerannotation用法规则类似。例如在A/B测试场景 下,需要让地域为北京的用户访问Canary版本。那么当cookieannotation设置为nginx.ingress.kubernetes.io/canary-by-cookie: "user_Beijing",此时后台可对登录的用户请求进行检查,如果该用户访问源来自北京则设置cookie user_Beijing的值为always

总结

本文主要介绍了为什么使用灰度发布,如何使用Ingress-nginx进行灰度发布,最后通过示例详细介绍了Ingress-nginx annotation灰度发布配置和使用,如有问题,关注公众号,加我微信,拉你进群讨论。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-08-04 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 灰度发布使用背景
  • 灰度发布概念介绍
  • ingress-nginx灰度发布介绍
    • 注解划分
      • 类型划分
      • Ingress-nginx灰度功能测试
        • 测试环境说明
          • 金丝雀版本
            • production版本
              • 按照请求header执行测试
                • 按照请求权重执行测试(按照流量进行灰度,这种方式尤为有用)
                  • 按照模糊匹配执行测试(A/B发布场景下,这种方式尤为有用)
                    • 按照cookie匹配执行测试
                    • 总结
                    相关产品与服务
                    CODING DevOps
                    CODING DevOps 一站式研发管理平台,包括代码托管、项目管理、测试管理、持续集成、制品库等多款产品和服务,涵盖软件开发从构想到交付的一切所需,使研发团队在云端高效协同,实践敏捷开发与 DevOps,提升软件交付质量与速度。
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档