k8s七层代理Ingress-nginx基于cookie、请求头、权重实现灰度发布

简介: k8s七层代理Ingress-nginx基于cookie、请求头、权重实现灰度发布

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

假设线上运行了一套对外提供 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 入口。

 

 

部署两个版本的服务

这里以简单的 nginx 为例,先部署一个 v1 版本:

[root@xianchaonode1~]# docker load -i openresty.tar.gz
[root@xianchaomaster1v1-v2]# 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@xianchaomaster1v1-v2]# kubectl apply -f v1.yaml

再部署一个 v2 版本:

[root@xianchaomaster1v1-v2]# 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@xianchaomaster1v1-v2]# kubectl apply -f v2.yaml
再创建一个 Ingress,对外暴露服务,指向 v1 版本的服务:
[root@xianchaomaster1v1-v2]# vim v1-ingress.yaml
apiVersion:extensions/v1beta1
kind:Ingress
metadata:
  name: nginx
  annotations:
    kubernetes.io/ingress.class: nginx
spec:
  rules:
  - host: canary.example.com
    http:
      paths:
      - backend:
          serviceName: nginx-v1
          servicePort: 80
        path: /
[root@xianchaomaster1v1-v2]# kubectl apply -f v1-ingress.yaml

访问验证一下:

$curl -H "Host: canary.example.com" http://EXTERNAL-IP # EXTERNAL-IP 替换为 Nginx Ingress 自身对外暴露的 IP

[root@xianchaomaster1v1-v2]# curl -H "Host: canary.example.com" http://192.168.40.181

返回结果如下:

nginx-v1

基于 Header 的流量切分:

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

[root@xianchaomaster1v1-v2]# vim v2-ingress.yaml
apiVersion:extensions/v1beta1
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:
      - backend:
          serviceName: nginx-v2
          servicePort: 80
        path: /
[root@xianchaomaster1v1-v2]# kubectl apply -f v2-ingress.yaml

测试访问:

$curl -H "Host: canary.example.com" -H "Region: cd"http://EXTERNAL-IP # EXTERNAL-IP 替换为 Nginx Ingress 自身对外暴露的IP

[root@xianchaomaster1ingress]# curl -H "Host: canary.example.com" -H "Region:cd" http://192.168.40.181:31878

返回结果如下:

nginx-v2

$curl -H "Host: canary.example.com" -H "Region: bj" http://EXTERNAL-IP

[root@xianchaomaster1v1-v2]# curl -H "Host: canary.example.com" -H "Region: bj"http://192.168.40.181:31878

返回结果如下:

nginx-v1

$curl -H "Host: canary.example.com" -H "Region: cd" http://EXTERNAL-IP

[root@xianchaomaster1v1-v2]# curl -H "Host: canary.example.com" -H "Region: cd" http://192.168.40.181:31878

返回结果如下:

nginx-v2

 

$curl -H "Host: canary.example.com" http://EXTERNAL-IP

$curl -H "Host: canary.example.com" http://192.168.40.181:31878

返回结果如下:

nginx-v1

 

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

 

基于 Cookie 的流量切分:

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

 

[root@xianchaomaster1v1-v2]# kubectl delete -f v2-ingress.yaml
[root@xianchaomaster1v1-v2]# vim v1-cookie.yaml
apiVersion:extensions/v1beta1
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:
      -backend:
          serviceName: nginx-v2
          servicePort: 80
        path: /
[root@xianchaomaster1v1-v2]# kubectl apply -f v1-cookie.yaml

测试访问:

$curl -s -H "Host: canary.example.com" --cookie"user_from_cd=always" http://EXTERNAL-IP # EXTERNAL-IP 替换为 Nginx Ingress 自身对外暴露的 IP

 

[root@xianchaomaster1v1-v2]#  curl -s -H "Host:canary.example.com" --cookie "user_from_cd=always" http://192.168.40.181:31878

返回的结果:

nginx-v2

 

$curl -s -H "Host: canary.example.com" --cookie"user_from_bj=always" http://EXTERNAL-IP

 

[root@xianchaomaster1v1-v2]# curl -s -H "Host: canary.example.com" --cookie"user_from_bj=always" http://192.168.40.181:31878

 

返回的结果:

nginx-v1

 

$curl -s -H "Host: canary.example.com" http://EXTERNAL-IP

[root@xianchaomaster1v1-v2]# curl -s -H "Host: canary.example.com" http://192.168.40.181:31878

返回的结果:

nginx-v1

 

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

 

基于服务权重的流量切分

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

[root@xianchaomaster1v1-v2]# kubectl delete -f v1-cookie.yaml
[root@xianchaomaster1v1-v2]# vim v1-weight.yaml
apiVersion:extensions/v1beta1
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:
      - backend:
          serviceName: nginx-v2
          servicePort: 80
        path: /
[root@xianchaomaster1v1-v2]# kubectl apply -f v1-weight.yaml

测试访问:

$for i in {1..10}; do curl -H "Host: canary.example.com"http://EXTERNAL-IP; done;

[root@xianchaomaster1v1-v2]#  for i in {1..10}; do curl -H"Host: canary.example.com" http://192.168.40.181:31878; done;

返回如下结果:

nginx-v1
nginx-v1
nginx-v1
nginx-v1
nginx-v1
nginx-v1
nginx-v2
nginx-v1
nginx-v1
nginx-v1

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

相关实践学习
深入解析Docker容器化技术
Docker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。Docker是世界领先的软件容器平台。开发人员利用Docker可以消除协作编码时“在我的机器上可正常工作”的问题。运维人员利用Docker可以在隔离容器中并行运行和管理应用,获得更好的计算密度。企业利用Docker可以构建敏捷的软件交付管道,以更快的速度、更高的安全性和可靠的信誉为Linux和Windows Server应用发布新功能。 在本套课程中,我们将全面的讲解Docker技术栈,从环境安装到容器、镜像操作以及生产环境如何部署开发的微服务应用。本课程由黑马程序员提供。     相关的阿里云产品:容器服务 ACK 容器服务 Kubernetes 版(简称 ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情: https://www.aliyun.com/product/kubernetes
相关文章
|
负载均衡 监控 应用服务中间件
配置Nginx反向代理时如何指定后端服务器的权重?
配置Nginx反向代理时如何指定后端服务器的权重?
900 61
|
存储 人工智能 物联网
ACK Gateway with AI Extension:大模型推理的模型灰度实践
本文介绍了如何使用 ACK Gateway with AI Extension 组件在云原生环境中实现大语言模型(LLM)推理服务的灰度发布和流量分发。该组件专为 LLM 推理场景设计,支持四层/七层流量路由,并提供基于模型服务器负载感知的智能负载均衡能力。通过自定义资源(CRD),如 InferencePool 和 InferenceModel,可以灵活配置推理服务的流量策略,包括模型灰度发布和流量镜像。
|
Kubernetes 安全 应用服务中间件
IngressNightmare:Ingress Nginx 再曝5个安全漏洞,可接管你的 K8s 集群
是否还记得 2022 年 K8s Ingress Nginx 披露了的 3 个高危安全漏洞(CVE-2021-25745, CVE-2021-25746, CVE-2021-25748),并在那一年宣布停止接收新功能 PR,专注修复并提升稳定性。
|
Kubernetes 应用服务中间件 nginx
k8s学习--YAML资源清单文件托管服务nginx
k8s学习--YAML资源清单文件托管服务nginx
708 2
k8s学习--YAML资源清单文件托管服务nginx
|
Kubernetes 监控 测试技术
k8s学习--基于Ingress-nginx实现灰度发布系统
k8s学习--基于Ingress-nginx实现灰度发布系统
1070 2
k8s学习--基于Ingress-nginx实现灰度发布系统
|
负载均衡 算法 Java
腾讯面试:说说6大Nginx负载均衡?手写一下权重轮询策略?
尼恩,一位资深架构师,分享了关于负载均衡及其策略的深入解析,特别是基于权重的负载均衡策略。文章不仅介绍了Nginx的五大负载均衡策略,如轮询、加权轮询、IP哈希、最少连接数等,还提供了手写加权轮询算法的Java实现示例。通过这些内容,尼恩帮助读者系统化理解负载均衡技术,提升面试竞争力,实现技术上的“肌肉展示”。此外,他还提供了丰富的技术资料和面试指导,助力求职者在大厂面试中脱颖而出。
腾讯面试:说说6大Nginx负载均衡?手写一下权重轮询策略?
|
Kubernetes 负载均衡 应用服务中间件
k8s学习--ingress详细解释与应用(nginx ingress controller))
k8s学习--ingress详细解释与应用(nginx ingress controller))
3144 0
|
Kubernetes 应用服务中间件 nginx
k8s基础使用--使用k8s部署nginx服务
本文介绍了Kubernetes中核心概念Deployment、Pod与Service的基本原理及应用。Pod作为最小调度单元,用于管理容器及其共享资源;Deployment则负责控制Pod副本数量,确保其符合预期状态;Service通过标签选择器实现Pod服务的负载均衡与暴露。此外,还提供了具体操作步骤,如通过`kubectl`命令创建Deployment和Service,以及如何验证其功能。实验环境包括一台master节点和两台worker节点,均已部署k8s-1.27。
1588 1
|
存储 C#
【Azure APIM】APIM 策略语句如何读取请求头中所携带的Cookie信息并保存为变量
【Azure APIM】APIM 策略语句如何读取请求头中所携带的Cookie信息并保存为变量
238 2
|
缓存 Kubernetes 应用服务中间件
使用Kubernetes中的Nginx来改善第三方服务的可靠性和延迟
使用Kubernetes中的Nginx来改善第三方服务的可靠性和延迟
323 3

热门文章

最新文章

推荐镜像

更多