【云原生 | Kubernetes 实战】20、K8s Ingress 实现业务灰度发布

news2024/11/13 15:40:50

目录

通过 Ingress-nginx 实现灰度发布

一、Ingress Controller 多种发布策略介绍

场景一:将新版本灰度给部分用户

场景二:切一定比例的流量给新版本 

二、模拟部署生产测试版本 Web 服务

2.1 部署一个 v1 版本:

2.2 再部署一个 v2 版本:

2.3 再创建一个 Ingress,对外暴露服务,指向 v1 版本的服务:

2.4 访问验证一下:

三、Ingress Controller 基于请求头、cookie 和权重发布业务

3.1 基于 Header 的流量切分:

3.2 基于 Cookie 的流量切分: 

3.3  基于服务权重的流量切分


通过 Ingress-nginx 实现灰度发布

一、Ingress Controller 多种发布策略介绍

场景一:将新版本灰度给部分用户

假设线上运行了一套对外提供 7 层服务的 Service A 服务,后来开发了个新版本 Service A’ 想要上线,但又不想直接替换掉原来的 Service A,希望先灰度一小部分用户,等运行一段时间足够稳定了再逐渐全量上线新版本,最后平滑下线旧版本。这个时候就可以利用 Nginx Ingress 基于 Header 或 Cookie 进行流量切分的策略来发布,业务使用 Header 或 Cookie 来标识不同类型的用户,我们通过配置 Ingress 来实现让带有指定 Header 或 Cookie 的请求被转发到新版本,其它的仍然转发到旧版本,从而实现将新版本灰度给部分用户:

场景二:切一定比例的流量给新版本 

        假设线上运行了一套对外提供 7 层服务的 Service B 服务,后来修复了一些问题,需要灰度上线一个新版本 Service B’,但又不想直接替换掉原来的 Service B,而是让先切 10% 的流量到新版本,等观察一段时间稳定后再逐渐加大新版本的流量比例直至完全替换旧版本,最后再滑下线旧版本,从而实现切一定比例的流量给新版本:

        Ingress-Nginx 是一个 K8S ingress 工具,支持配置 Ingress Annotations 来实现不同场景下的灰度发布和测试。 Nginx Annotations 支持以下几种 Canary 规则:

假设我们现在部署了两个版本的服务,老版本和 canary 版本。

  • nginx.ingress.kubernetes.io/canary-by-header:基于 Request Header 的流量切分,适用于灰度发布以及 A/B 测试。当 Request Header 设置为 always 时,请求将会被一直发送到 Canary 版本;当 Request Header 设置为 never 时,请求不会被发送到 Canary 入口。
  • nginx.ingress.kubernetes.io/canary-by-header-value:要匹配的 Request Header 的值,用于通知 Ingress 将请求路由到 Canary Ingress 中指定的服务。当 Request Header 设置为此值时,它将被路由到 Canary 入口。
  • nginx.ingress.kubernetes.io/canary-weight:基于服务权重的流量切分,适用于蓝绿部署,权重范围 0 - 100 按百分比将请求路由到 Canary Ingress 中指定的服务。权重为 0 意味着该金丝雀规则不会向 Canary 入口的服务发送任何请求。权重为 60 意味着 60% 流量转到 canary。权重为 100 意味着所有请求都将被发送到 Canary 入口。
  • nginx.ingress.kubernetes.io/canary-by-cookie:基于 Cookie 的流量切分,适用于灰度发布与 A/B 测试。用于通知 Ingress 将请求路由到 Canary Ingress 中指定的服务的 cookie。当 cookie 值设置为 always 时,它将被路由到 Canary 入口;当 cookie 值设置为 never 时,请求不会被发送到 Canary 入口。

二、模拟部署生产测试版本 Web 服务

相关资料下载地址:https://download.csdn.net/download/weixin_46560589/87356951

部署两个版本的服务,这里以简单的 nginx 为例。

2.1 部署一个 v1 版本:

# 上传镜像并解压
[root@k8s-node1 ~]# ctr -n=k8s.io images import openresty.tar.gz
[root@k8s-node2 ~]# ctr -n=k8s.io images import openresty.tar.gz

[root@k8s-master01 ~]# vim v1.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-v1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
      version: v1
  template:
    metadata:
      labels:
        app: nginx
        version: v1
    spec:
      containers:
      - name: nginx
        image: "openresty/openresty:centos"
        imagePullPolicy: IfNotPresent
        ports:
        - name: http
          protocol: TCP
          containerPort: 80
        volumeMounts:
        - mountPath: /usr/local/openresty/nginx/conf/nginx.conf
          name: config
          subPath: nginx.conf
      volumes:
      - name: config
        configMap:
          name: nginx-v1
---
apiVersion: v1
kind: ConfigMap
metadata:
  labels:
    app: nginx
    version: v1
  name: nginx-v1
data:
  nginx.conf: |-
    worker_processes  1;
    events {
        accept_mutex on;
        multi_accept on;
        use epoll;
        worker_connections  1024;
    }
    http {
        ignore_invalid_headers off;
        server {
            listen 80;
            location / {
                access_by_lua '
                    local header_str = ngx.say("nginx-v1")
                ';
            }
        }
    }
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-v1
spec:
  type: ClusterIP
  ports:
  - port: 80
    protocol: TCP
    name: http
  selector:
    app: nginx
    version: v1

[root@k8s-master01 ~]# kubectl apply -f v1.yaml 

[root@k8s-master01 ~]# kubectl get pods 
NAME                               READY   STATUS    RESTARTS       AGE
nginx-v1-6d9846d69d-zrfcq          1/1     Running   0              13s

2.2 再部署一个 v2 版本:

[root@k8s-master01 ~]# vim v2.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-v2
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
      version: v2
  template:
    metadata:
      labels:
        app: nginx
        version: v2
    spec:
      containers:
      - name: nginx
        image: "openresty/openresty:centos"
        imagePullPolicy: IfNotPresent
        ports:
        - name: http
          protocol: TCP
          containerPort: 80
        volumeMounts:
        - mountPath: /usr/local/openresty/nginx/conf/nginx.conf
          name: config
          subPath: nginx.conf
      volumes:
      - name: config
        configMap:
          name: nginx-v2
---
apiVersion: v1
kind: ConfigMap
metadata:
  labels:
    app: nginx
    version: v2
  name: nginx-v2
data:
  nginx.conf: |-
    worker_processes  1;
    events {
        accept_mutex on;
        multi_accept on;
        use epoll;
        worker_connections  1024;
    }
    http {
        ignore_invalid_headers off;
        server {
            listen 80;
            location / {
                access_by_lua '
                    local header_str = ngx.say("nginx-v2")
                ';
            }
        }
    }
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-v2
spec:
  type: ClusterIP
  ports:
  - port: 80
    protocol: TCP
    name: http
  selector:
    app: nginx
    version: v2

[root@k8s-master01 ~]# kubectl apply -f v2.yaml 

[root@k8s-master01 ~]# kubectl get pods 
NAME                               READY   STATUS    RESTARTS       AGE
nginx-v1-6d9846d69d-zrfcq          1/1     Running   0              2m5s
nginx-v2-7cd8cfb784-fztvw          1/1     Running   0              19s

2.3 再创建一个 Ingress,对外暴露服务,指向 v1 版本的服务:

[root@k8s-master01 ~]# vim v1-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx
  annotations:
    kubernetes.io/ingress.class: nginx
spec:
  rules:
  - host: canary.example.com
    http:
      paths:
      - path: /           # 配置访问路径,如果通过 url 进行转发,需要修改;默认为访问的路径为"/"
        pathType:  Prefix
        backend:          # 配置后端服务
         service:
           name: nginx-v1
           port:
            number: 80

[root@k8s-master01 ~]# kubectl apply -f v1-ingress.yaml 

2.4 访问验证一下:

# 工作节点物理机 ip
[root@k8s-master01 ~]# curl -H "Host: canary.example.com" http://192.168.78.131
nginx-v1
[root@k8s-master01 ~]# curl -H "Host: canary.example.com" http://192.168.78.132
nginx-v1

# 虚拟 ip
[root@k8s-master01 ~]# curl -H "Host: canary.example.com" http://192.168.78.199
nginx-v1

三、Ingress Controller 基于请求头、cookie 和权重发布业务

3.1 基于 Header 的流量切分:

        创建 Canary Ingress,指定 v2 版本的后端服务,且加上一些 annotation,实现仅将带有名为 Region 且值为 cd 或 sz 的请求头的请求转发给当前 Canary Ingress,模拟灰度新版本给成都和深圳地域的用户:

[root@k8s-master01 ~]# vim v2-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-by-header: "Region"
    nginx.ingress.kubernetes.io/canary-by-header-pattern: "cd|sz"
  name: nginx-canary
spec:
  rules:
  - host: canary.example.com
    http:
      paths:
      - path: /           # 配置访问路径,如果通过 url 进行转发,需要修改;默认为访问的路径为"/"
        pathType:  Prefix
        backend:          # 配置后端服务
         service:
           name: nginx-v2
           port:
            number: 80

[root@k8s-master01 ~]# kubectl apply -f v2-ingress.yaml 

# 测试访问:
[root@k8s-master01 ~]# curl -H "Host: canary.example.com" -H "Region: cd" http://192.168.78.199
nginx-v2

[root@k8s-master01 ~]# curl -H "Host: canary.example.com" -H "Region: sz" http://192.168.78.199
nginx-v2

[root@k8s-master01 ~]# curl -H "Host: canary.example.com" -H "Region: bj" http://192.168.78.199
nginx-v1

[root@k8s-master01 ~]# curl -H "Host: canary.example.com" http://192.168.78.199
nginx-v1

可以看到,只有 header Region 为 cd 或 sz 的请求才由 v2 版本服务响应。

3.2 基于 Cookie 的流量切分: 

        与前面 Header 类似,不过使用 Cookie 就无法自定义 value 了,这里以模拟灰度成都地域用户为例,仅将带有名为 user_from_cd 的 cookie 的请求转发给当前 Canary Ingress 。先删除前面基于 Header 的流量切分的 Canary Ingress,然后创建下面新的 Canary Ingress:

[root@k8s-master01 ~]# kubectl delete -f v2-ingress.yaml 

[root@k8s-master01 ~]# vim v1-cookie.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-by-cookie: "user_from_cd"
  name: nginx-canary
spec:
  rules:
  - host: canary.example.com
    http:
      paths:
      - path: /              # 配置访问路径,如果通过url进行转发,需要修改;默认为访问的路径为"/"
        pathType:  Prefix
        backend:             # 配置后端服务
         service:
           name: nginx-v2
           port:
            number: 80

[root@k8s-master01 ~]# kubectl apply -f v1-cookie.yaml 

# 测试访问:
[root@k8s-master01 ~]# curl -s -H "Host: canary.example.com" --cookie "user_from_cd=always" http://192.168.78.199
nginx-v2

[root@k8s-master01 ~]# curl -s -H "Host: canary.example.com" --cookie "user_from_bj=always" http://192.168.78.199
nginx-v1

[root@k8s-master01 ~]# curl -s -H "Host: canary.example.com" http://192.168.78.199
nginx-v1

可以看到,只有 cookie user_from_cd 为 always 的请求才由 v2 版本的服务响应。

3.3  基于服务权重的流量切分

        基于服务权重的 Canary Ingress 就简单了,直接定义需要导入的流量比例,这里以导入 10% 流量到 v2 版本为例 (先删除之前的 Canary Ingress):

[root@k8s-master01 ~]# kubectl delete -f v1-cookie.yaml 
[root@k8s-master01 ~]# vim v1-weight.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-weight: "10"
  name: nginx-canary
spec:
  rules:
  - host: canary.example.com
    http:
      paths:
      - path: /              # 配置访问路径,如果通过url进行转发,需要修改;默认为访问的路径为"/"
        pathType:  Prefix
        backend:             # 配置后端服务
         service:
           name: nginx-v2
           port:
            number: 80

[root@k8s-master01 ~]# kubectl apply -f v1-weight.yaml 

# 测试访问
[root@k8s-master01 ~]# for i in {1..10}; do curl -H "Host: canary.example.com" http://192.168.78.199; done;
nginx-v1
nginx-v2
nginx-v1
nginx-v1
nginx-v1
nginx-v1
nginx-v1
nginx-v1
nginx-v2
nginx-v1

可以看到,大概只有十分之一的几率由 v2 版本的服务响应,符合 10% 服务权重的设置。

上一篇文章:【云原生 | Kubernetes 实战】19、K8s Ingress-Controller 高可用方案_Stars.Sky的博客-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/133101.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

论文投稿指南——中文核心期刊推荐(化学)

【前言】 🚀 想发论文怎么办?手把手教你论文如何投稿!那么,首先要搞懂投稿目标——论文期刊 🎄 在期刊论文的分布中,存在一种普遍现象:即对于某一特定的学科或专业来说,少数期刊所含…

本地方法接口

什么是本地方法 简单地讲,一个Native Methodt是一个Java调用非Java代码的接囗。一个Native Method是这样一个Java方法:该方法的实现由非Java语言实现,比如C。这个特征并非Java所特有,很多其它的编程语言都有这一机制,…

AQS核心原理之

AQS系列 1、AQS核心原理之 2、ReentrantLock 原理及示例 文章目录AQS系列一、什么是AQS?二、AQS特性三、AQS内部维护 state四、队列4.1 同步等待队列4.2 条件等待队列5、总结一、什么是AQS? AQS全称是 AbstractQueuedSynchronizer(抽象对了同步器)&am…

Cento6从零开始用Nginx+mysql+php搭建Discuz在线论坛系统

首先我的liunx使用版本是 centos6.5 32位 discuz版本是Discuz_X3.2_SC_UTF8.zip Xshell版本是6 xftp是7版本 mysql也是是这个版本:mysql这里写目录标题1.yum安装php2.安装nginx包3.安装php-fpm4.安装php-mysql5. 安装Discuz在线论坛系统由于目前yum源已经无法使用需…

力扣 2037. 使每位学生都有座位的最少移动次数

题目 一个房间里有 n 个座位和 n 名学生,房间用一个数轴表示。给你一个长度为 n 的数组 seats ,其中 seats[i] 是第 i 个座位的位置。同时给你一个长度为 n 的数组 students ,其中 students[j] 是第 j 位学生的位置。 你可以执行以下操作任…

unidbg案例-爱库存app之sig和sign分析

新年的第一篇文章,新的一年继续加油,奥利给!冲冲冲。 今天分析的app是爱库存,版本号6.1.6,这次还是使用unidbg分析该样本,加密参数有很多,不过只关注sig和sign两个参数。 老规矩,上来先抓个包。 1.抓包 可以看到上面👆🏻的sign,就是本次研究的重点。 2.jadx静…

A* 算法详解(超级详细讲解,附有大图)

目录 引入 一.基本概念 二.算法原理 ①用宽度优先搜索 ②狄克斯特拉算法 ③A*算法 三.需要注意 四.c伪代码 最后 引入 今天想跟大家聊的,是我们经常用到,但是却让大家觉得十分神秘的那个算法:A* 。 这是一个远古而又非常经典的游戏…

【C++ STL】-- 用一棵红黑树的插入实现同时封装map与set

用一棵红黑树同时封装map与set的意义:所谓的 “用一棵红黑树同时封装map与set” 只是在程序员的角度,通过一系列手段,以一个红黑树同时满足map与set。但是在编译器的角度,实际上并不是一颗树实现的,程序员所写的只是一…

机器学习 10:激活函数大全

虽称为激活函数大全,但也不敢太过自满,如有遗漏与错误,还请指正 文章目录线性激活函数Sigmoid 函数LogSigmoidSwishTanh / 双曲正切激活函数TanhShrinkSoftsignReLU 函数BReLULeaky ReLUPReLURReLUELUSELUCELUGELUSoftmax 函数Maxout 函数Sof…

Android 实现多语言

工具下载连接 链接:https://pan.baidu.com/s/1Wq9DTzhP2fkHXLEbOQFr9A?pwdlmcz 提取码:lmcz 1.将你需要的翻译的strings放到exe目录下 2.双击执行xml转xls.exe 英文 日文 韩文(使用空格分割)回车,会在当前目录下生…

jvm学习的开端(一)----类的加载(类加载子系统)

文章目录1.Loading(加载阶段)2.Linking(链接阶段)2.lnitialization(初始化阶段)来自 百度百科: 类加载器子系统负责从文件系统或者网络中加载class文件,class文件在文件开头有特定的…

【java项目】飞机大战

文章目录项目-飞机大战窗口的创建背景图片的添加/点击事件的启动游戏物体父类的编写背景的移动双缓存技术--解决文字闪动背景图片循环出现我方战斗机的添加和鼠标控制添加首颗子弹批量添加子弹敌方飞机的批量添加功能我方子弹与敌方飞机的碰撞检测我方子弹与敌方飞机碰撞时的处…

单片机基础之单片机中断、定时器中断、PWM及SG90舵机的初识认知

目录 一、初探单片机中断 二、定时器中断相关寄存器 1、中断寄存器 2、中断结构 3、用定时器中断方式控制LED,代码编程测试 三、初识PWM 1、什么是占空比 2、如何输出PWM信号 四、SG90舵机基本认知 1、什么是舵机 2、怎么控制舵机 3、舵机编程实战 一、…

Jetson nano 入手系列之2—板载摄像头IMX219启动

Jetson nano 入手系列之2—板载摄像头IMX219启动1.亚克力板安装2.摄像头启动3.nvgstcapture常用命令3.1 Set sensor orientation3.2 Get Image Capture Resolution3.3 Capture3.4 quit参考文献Jetson nano 入手系列: Jetson nano 入手系列之1—如何SSH远程登录 Jets…

Redis学习(一)

Redis入门 Redis是一个基于内存的key-value结构数据库,读写性能较高 Redis数据类型 Redis存储的是key-value结构的数据,其中key是字符串类型,value有5种数据类型: 1.字符串 string 2.哈希 hash 3.列表 list 4.集合 set 5.有序集…

Altium Designer 20 凡亿教育视频学习-01

课程视频:第1课 课程介绍.mp4_哔哩哔哩_bilibili 第一部分学习 学习方法 工程具备文件 一定需要先建立工程,再来创建原理图库、原理图等文件 栅格大小改变 栅格的大小我们常在绘制原理图的时候改变,因为有时候我们需要画一个细线&#…

【PCB专题】什么是通孔、盲孔、埋孔?

PCB板是由基板和PP叠加而成的。不同层上走了各种信号线和电源,这些信号和电源在不同的电路层之间切换时需要依靠过孔(通孔、盲孔和埋孔)连接。如下图所示的6层板,使用了2阶HDI方案:有机械孔和激光孔。 过孔的作用就像是水管一样,连接了不同的平面。PCB板上的过孔作用就是…

函数的定义和调用 与 this指向

1、函数的定义和调用 1.1、函数的定义方式 函数声明方式 function 关键字 (命名函数)函数表达式 (匿名函数)new Function() var fn new Function(参数1,参数2..., 函数体)(1)Function 里面参数都必须是字符串格式 (2)第三种方式…

已解决+ CategoryInfo: SecurityError: (:) [ ].ParentContainsErrorRecordException

已解决无法加载文件 E:\day_01\Scripts\activate.ps1,因为在此系统上禁止运行脚本。有关详细信息,请参阅 https:/go.microsoft.com/fwlink/?LinkID135170 中的about_Execution_Policies。 CategoryInfo: SecurityError: (:) [ ]…

[LeetCode周赛复盘] 第 326 场周赛20230101

[LeetCode周赛复盘] 第 326 场周赛20230101 一、本周周赛总结二、 [Easy] 6278. 统计能整除数字的位数1. 题目描述2. 思路分析3. 代码实现三、[Medium] 6279. 数组乘积中的不同质因数数目1. 题目描述2. 思路分析3. 代码实现四、[Medium] 6196. 将字符串分割成值不超过 K 的子字…