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 <none> 80/TCP 31s
tomcat ClusterIP 10.109.238.78 <none> 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 <none> 80/TCP 7m13s
nginx-v2 ClusterIP 10.104.97.248 <none> 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 <none> 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 <none> canary.example.com 192.168.10.11,192.168.10.13 80 11m
nginx-canary <none> 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 <none> 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 <none> canary.example.com 192.168.10.11,192.168.10.13 80 28m
nginx-canary <none> 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

通过Ingress-nginx实现灰度发布---灰度发布(22)的更多相关文章

  1. nginx+lua+redis实现灰度发布_test

    nginx+lua+redis实现灰度发布: 灰度发布是指在黑白之间能够平滑过渡的一种方式 AB test就是一种灰度发布方式,让一部分用户继续用A,一部分用户开始用B,如果用户对B没有什么反对意见, ...

  2. 蓝绿部署、金丝雀发布(灰度发布)、A/B测试

    本文转载自蓝绿部署.金丝雀发布(灰度发布).A/B测试的准确定义 概述 蓝绿部署.A/B测试.金丝雀发布,以及灰度发布.流量切分等,经常被混为一谈,影响沟通效率. 根本原因是这些名词经常出现,人们耳熟 ...

  3. nginx+uWSGI+django+virtualenv+supervisor发布web服务器

    nginx+uWSGI+django+virtualenv+supervisor发布web服务器   导论 WSGI是Web服务器网关接口.它是一个规范,描述了Web服务器如何与Web应用程序通信,以 ...

  4. Linux - nginx+uWSGI+django+virtualenv+supervisor发布web服务器

    目录 Linux - nginx+uWSGI+django+virtualenv+supervisor发布web服务器 crm django项目部署流程 使用supervisro启动uwsgi,退出虚 ...

  5. OpenCV2+入门系列(四):计算图像的直方图,平均灰度,灰度方差

    本篇懒得排版,直接在网页html编辑器编辑 在图像处理时,我们常常需要求出图像的直方图.灰度平均值.灰度的方差,这里给出一个opencv2+自带程序,实现这些功能. 直方图 对于直方图,使用cv::c ...

  6. Atitit 图像处理 灰度图片 灰度化的原理与实现

    Atitit 图像处理 灰度图片 灰度化的原理与实现 24位彩色图与8位灰度图 首先要先介绍一下24位彩色图像,在一个24位彩色图像中,每个像素由三个字节表示,通常表示为RGB.通常,许多24位彩色图 ...

  7. IIS上发布WCF发布服务,访问不到

    1 环境是IIS7,发布WCF发布服务,访问不到. 一种原因站点自动生成“程序应用池”和站点的Framwork版本不一致. 解决的办法:新建一个“程序应用池”,然后站点指向这个新建的“程序应用池”

  8. SQL2005 到 SQL2008R2 发布订阅----发布'xxxxx'的初始快照尚不可用。

    步骤略! SQL2005 到 SQL2008R2 发布订阅----发布'xxxxx'的初始快照尚不可用. 发布库快照已经创建完成为什么到订阅就快照不可用呢! 订阅通过日志读取代理解析! 查了下代理安全 ...

  9. 织梦DedeCMS信息发布员发布文章阅读权限不用审核自动开放亲测试通过!

    文章发布员在织梦dedecms后台添加文章时却要超级管理员审核,这无疑是增加了没必要的工作. 登录该账号发布文章你会发现该文章显示的是待审核稿件,且并没有生成静态文件,在前台是看不到这篇文章的,而多数 ...

  10. 织梦DedeCMS信息发布员发布文章默认自动审核更新并生成HTML页面

    织梦DedeCMS信息发布员发布文章默认自动审核更新并生成HTML页面 一直以为DEDECMS的信息发布员在后台发布文章后,非要管理员审核才能显示,今天一哥们问我这个问题.问:“能不能直接发布,并自动 ...

随机推荐

  1. 记录--Vue中前端导出word文件

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 很多时候在工作中会碰到完全由前端导出word文件的需求,因此特地记录一下比较常用的几种方式. 一.提供一个word模板 该方法提供一个wo ...

  2. 工作记录:Stylus基础教程及应用

    前言 传统CSS的缺陷 css的可重用性差.代码冗余量大.不支持语言特性如变量循环及方法等(虽然css也在慢慢支持,比如现在的css变量等,但明显这些远远不够). 三大预处理 于是预处理器出现了: 2 ...

  3. 记录--千万别让 console.log 上生产!用 Performance 和 Memory 告诉你为什么

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 很多前端都喜欢用 console.log 调试,先不谈调试效率怎么样,首先 console.log 有个致命的问题:会导致内存泄漏. 为什 ...

  4. 利用kali自带的msfvenom工具生成远程控制软件

    一.首先还是得打开postgresql service postgresql start 然后让我们看看它有哪些功能 部分参数 -p 选择一个载荷,或者说一个模块吧. -i 载荷列表 -f 生成的文件 ...

  5. Spring Cloud Alibaba服务的注册与发现之Nacos部署

    1.Nacos官网介绍 Nacos 致力于帮助您发现.配置和管理微服务.Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现.服务配置.服务元数据及流量管理.Nacos 帮助您更敏捷和容 ...

  6. KingbaseES使用kbbench计算连接耗时

    前言 本文讨论一下KingbaseES数据库中如何计算数据库连接耗时.有这样一个场景,不借助第三方工具,在数据库服务端计算1000个数据库连接的总耗时,并取得每个连接耗时的平均值.怎样实现呢?我们可以 ...

  7. C++一些例子

    虚析构 #include<iostream> class Base { public: Base() { std::cout << "base 构造" &l ...

  8. Unity 2022.3.20f1新功能,异步实例化预制体Object.InstantiateAsync

    今天查看Unity 2022.3.20f1更新日志,发现新增了个异步实例化的功能,这个功能解决了Unity历史上实例化预制体卡顿的痛点,简直不要太爽. 具体的API文档请点击跳转. 做了个简单的实例化 ...

  9. 14 CSS列表属性和display属性

    14 列表属性和display属性 列表属性 CSS中提供了一些列表属性可以用来: (1).设置不同的列表项标记为有序列表 (2).设置不同的列表项标记为无序列表 (3).设置列表项标记为图像 lis ...

  10. #Kruskal重构树,主席树,倍增#洛谷 4197 Peaks

    题目传送门 分析 首先第\(k\)大一般都是主席树 问题是困难值的限制有点束手无措 又是\(\text{Kruskal重构树}\) 将困难值为边权跑最小生成树,重新建树,实际上让困难值跳到尽量大的位置 ...