热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

通过Ingressnginx实现灰度发布分度发布(22)

1.通过Ingress-nginx实现灰度发布场景一:将新版本灰度给部分用户假设线上运行了一套对外提供7层服务的ServiceA服务,后来开发了个新版本ServiceA’想要上线,

1.通过Ingress-nginx实现灰度发布

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

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


2.部署实例


2.1 部署2个版本的服务

# 1.node1和node3节点导入镜像
docker load -i openresty.tar.gz
Loaded image: openresty/openresty:centos
# 2.部署v1版本
[root@master header-ingress]# cat 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@master header-ingress]# kubectl apply -f v1.yaml
deployment.apps/nginx-v1 created
configmap/nginx-v1 created
service/nginx-v1 created
[root@master header-ingress]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-v1 ClusterIP 10.108.228.242 80/TCP 31s
tomcat ClusterIP 10.109.238.78 8080/TCP,8009/TCP 3h20m
[root@master header-ingress]# kubectl get configmap
NAME DATA AGE
nginx-v1 1 51s
[root@master header-ingress]# kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-v1 0/1 0 0 78s
[root@master header-ingress]# kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-v1-79bc94ff97-7bfg4 1/1 Running 0 55s
# 3.部署v2版本
[root@master header-ingress]# cat 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@master header-ingress]# kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-v1-79bc94ff97-7bfg4 1/1 Running 0 3m21s
nginx-v2-5f885975d5-4pnkq 1/1 Running 0 4s
[root@master header-ingress]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-v1 ClusterIP 10.108.228.242 80/TCP 7m13s
nginx-v2 ClusterIP 10.104.97.248 80/TCP 10s
# 4.再创建一个Ingress,对外暴露服务,指向v1版本服务
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@master header-ingress]# kubectl apply -f v1-ingress.yaml
Warning: extensions/v1beta1 Ingress is deprecated in v1.14+, unavailable in v1.22+; use networking.k8s.io/v1 Ingress
ingress.extensions/nginx created
[root@master header-ingress]# kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
nginx canary.example.com 192.168.10.11,192.168.10.13 80 30s
#访问验证结果
curl -H "Host: canary.example.com" http://EXTERNAL-IP
[root@master header-ingress]# curl -H "Host: canary.example.com" http://192.168.10.11
nginx-v1
# EXTERNAL-IP替换为nginx Ingress自身对外暴露的IP

2.2 Ingress-controller基于请求头和地域流量代理


2.2.1 基于 Header 的流量切分:

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

[root@master header-ingress]# cat 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@master header-ingress]# kubectl apply -f v2-ingress.yaml
Warning: extensions/v1beta1 Ingress is deprecated in v1.14+, unavailable in v1.22+; use networking.k8s.io/v1 Ingress
ingress.extensions/nginx-canary created
[root@master header-ingress]# kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
nginx canary.example.com 192.168.10.11,192.168.10.13 80 11m
nginx-canary canary.example.com 192.168.10.11,192.168.10.13 80 47s
测试访问:
curl -H "Host: canary.example.com" -H "Region: cd" http://EXTERNAL-IP # EXTERNAL-IP 替换为 Nginx Ingress 自身对外暴露的 IP
[root@master header-ingress]# curl -H "Host:canary.example.com" -H "Region:cd" http://192.168.10.11
nginx-v2
[root@master header-ingress]# curl -H "Host:canary.example.com" -H "Region:sh" http://192.168.10.11
nginx-v1
[root@master header-ingress]# curl -H "Host:canary.example.com" -H "Region:cd" http://192.168.10.199
nginx-v2

  [root@master header-ingress]# curl -H "Host:canary.example.com" -H "Region:sz" http://192.168.10.199
  nginx-v2

[root@master header-ingress]# curl -H "host:canary.example.com" http://192.168.10.11
nginx-v1

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

2.2.2 基于COOKIE的流量切分

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

[root@master header-ingress]# kubectl delete -f v2-ingress.yaml
[root@master header-ingress]# cat 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@master header-ingress]# kubectl apply -f v1-COOKIE.yaml
Warning: extensions/v1beta1 Ingress is deprecated in v1.14+, unavailable in v1.22+; use networking.k8s.io/v1 Ingress
ingress.extensions/nginx-canary created
[root@master header-ingress]# kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
nginx-canary canary.example.com 192.168.10.11,192.168.10.13 80 30s
测试访问:
[root@master header-ingress]# curl -s -H "Host:canary.example.com" --COOKIE "user_from_cd=always" http://192.168.10.199
nginx-v2
[root@master header-ingress]# curl -s -H "Host:canary.example.com" --COOKIE "user_from_sh=always" http://192.168.10.199
nginx-v1
[root@master header-ingress]# curl -s -H "Host:canary.example.com" http://192.168.10.199
nginx-v1
可以看到,只有 COOKIE user_from_cd 为 always 的请求才由 v2 版本的服务响应。

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

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

[root@master header-ingress]# kubectl delete -f v1-COOKIE.yaml
[root@master header-ingress]# cat 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@master header-ingress]# kubectl apply -f v1-weight.yaml
Warning: extensions/v1beta1 Ingress is deprecated in v1.14+, unavailable in v1.22+; use networking.k8s.io/v1 Ingress
ingress.extensions/nginx-canary created
[root@master header-ingress]# kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
nginx canary.example.com 192.168.10.11,192.168.10.13 80 28m
nginx-canary canary.example.com 80 13s
[root@master header-ingress]# for i in {1..10}; do curl -H "Host: canary.example.com" http://192.168.10.199;done;
nginx-v1
nginx-v2
nginx-v1
nginx-v1
nginx-v1
nginx-v1
nginx-v1
nginx-v1
nginx-v2
nginx-v1
可以看到,大概只有十分之一的几率由 v2 版本的服务响应,符合 10% 服务权重的设置,目前只有10个所以看到有2个v2

 



推荐阅读
  • 本文介绍了利用ARMA模型对平稳非白噪声序列进行建模的步骤及代码实现。首先对观察值序列进行样本自相关系数和样本偏自相关系数的计算,然后根据这些系数的性质选择适当的ARMA模型进行拟合,并估计模型中的位置参数。接着进行模型的有效性检验,如果不通过则重新选择模型再拟合,如果通过则进行模型优化。最后利用拟合模型预测序列的未来走势。文章还介绍了绘制时序图、平稳性检验、白噪声检验、确定ARMA阶数和预测未来走势的代码实现。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • Oracle 和 mysql的9点区别【MySQL】
    数据库|mysql教程oracle,Oracle,money,mysql,coun数据库-mysql教程1.组函数用法规则mysql中组函数在select语句中可以随意使用,但在o ... [详细]
  • PHP输出缓冲控制Output Control系列函数详解【PHP】
    后端开发|php教程PHP,输出缓冲,Output,Control后端开发-php教程概述全景网页源码,vscode如何打开c,ubuntu强制解锁,sts启动tomcat慢,sq ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • Webmin远程命令执行漏洞复现及防护方法
    本文介绍了Webmin远程命令执行漏洞CVE-2019-15107的漏洞详情和复现方法,同时提供了防护方法。漏洞存在于Webmin的找回密码页面中,攻击者无需权限即可注入命令并执行任意系统命令。文章还提供了相关参考链接和搭建靶场的步骤。此外,还指出了参考链接中的数据包不准确的问题,并解释了漏洞触发的条件。最后,给出了防护方法以避免受到该漏洞的攻击。 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • 本文介绍了在Vue项目中如何结合Element UI解决连续上传多张图片及图片编辑的问题。作者强调了在编码前要明确需求和所需要的结果,并详细描述了自己的代码实现过程。 ... [详细]
  • 在IDEA中运行CAS服务器的配置方法
    本文介绍了在IDEA中运行CAS服务器的配置方法,包括下载CAS模板Overlay Template、解压并添加项目、配置tomcat、运行CAS服务器等步骤。通过本文的指导,读者可以轻松在IDEA中进行CAS服务器的运行和配置。 ... [详细]
  • 1Lock与ReadWriteLock1.1LockpublicinterfaceLock{voidlock();voidlockInterruptibl ... [详细]
  • {moduleinfo:{card_count:[{count_phone:1,count:1}],search_count:[{count_phone:4 ... [详细]
  • 1.脚本功能1)自动替换jar包中的配置文件。2)自动备份老版本的Jar包3)自动判断是初次启动还是更新服务2.脚本准备进入ho ... [详细]
  • (九)Docker常用安装
    一、总体步骤1、搜索镜像2、拉取镜像3、查看镜像4、启动镜像5、停止镜像6、移除镜像二、安装tomcat1、dockerhub上面查找tomcat镜像 dockersearchto ... [详细]
  • oracle avg row len,Oracle 估算数据库大小的方法
    一.说明一网友问我将一个查询的结果集存放到临时表里,如果估算临时表的大小,当时想的方法是通过统计block来计算。后来想,此方法的操作性也 ... [详细]
author-avatar
yvli心语
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有