概述
本文将介绍如何应用 Nginx Ingress 实现金丝雀公布,从应用场景剖析,到用法详解,再到上手实际。
前提条件
集群中须要部署 Nginx Ingress 作为 Ingress Controller,并且对外裸露了对立的流量入口,参考 在 TKE 上部署 Nginx Ingress。
Nginx Ingress 能够用在哪些公布场景 ?
应用 Nginx Ingress 来实现金丝雀公布,能够用在哪些场景呢?这个次要看应用什么策略进行流量切分,目前 Nginx Ingress 反对基于 Header、Cookie 和服务权重这 3 种流量切分的策略,基于它们能够实现以下两种公布场景。
场景一: 将新版本灰度给局部用户
假如线上运行了一套对外提供 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 Ingress 所反对的一些 annotation 能够实现金丝雀公布,须要给服务创立两个 Ingress,一个失常的 Ingress,另一个是带 nginx.ingress.kubernetes.io/canary: "true"
这个固定的 annotation 的 Ingress,咱们权且称它为 Canary Ingress,个别代表新版本的服务,联合另外针对流量切分策略的 annotation 一起配置即可实现多种场景的金丝雀公布,以下对这些 annotation 具体介绍下:
nginx.ingress.kubernetes.io/canary-by-header
: 示意如果申请头中蕴含这里指定的 header 名称,并且值为always
的话,就将该申请转发给该 Ingress 定义的对应后端服务;如果值为never
就不转发,能够用于回滚到旧版;如果是其它值则疏忽该 annotation。nginx.ingress.kubernetes.io/canary-by-header-value
: 这个能够作为canary-by-header
的补充,容许指定申请头的值能够自定义成其它值,不再只能是always
或never
;当申请头的值命中这里的自定义值时,申请将会转发给该 Ingress 定义的对应后端服务,如果是其它值则将会疏忽该 annotation。nginx.ingress.kubernetes.io/canary-by-header-pattern
: 这个与下面的canary-by-header-value
相似,惟一的区别是它是用正则表达式对来匹配申请头的值,而不是只固定某一个值;须要留神的是,如果它与canary-by-header-value
同时存在,这个 annotation 将会被疏忽。nginx.ingress.kubernetes.io/canary-by-cookie
: 这个与canary-by-header
相似,只是这个用于 cookie,同样也是只反对always
和never
的值。nginx.ingress.kubernetes.io/canary-weight
: 示意 Canary Ingress 所调配流量的比例的百分比,取值范畴 [0-100],比方设置为 10,意思是调配 10% 的流量给 Canary Ingress 对应的后端服务。
下面的规定会按优先程序进行评估,优先程序如下:canary-by-header -> canary-by-cookie -> canary-weight
留神: 当 Ingress 被标记为 Canary Ingress 时,除了 nginx.ingress.kubernetes.io/load-balance
和 nginx.ingress.kubernetes.io/upstream-hash-by
之外,所有其余非 Canary 正文都将被疏忽。
上手实际
上面咱们给出一些例子,让你疾速上手 Nginx Ingress 的金丝雀公布,环境为 TKE 集群。
应用 YAML 创立资源
本文的示例将应用 yaml 的形式部署工作负载和创立 Service,有两种操作形式。
形式一:在 TKE 或 EKS 控制台右上角点击 YAML 创立资源
,而后将本文示例的 yaml 粘贴进去:
形式二:将示例的 yaml 保留成文件,而后应用 kubectl 指定 yaml 文件来创立,如: kubectl apply -f xx.yaml
。
部署两个版本的服务
这里以简略的 nginx 为例,先部署一个 v1 版本:
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"
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
再部署一个 v2 版本:
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"
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
能够在控制台看到部署的状况:
再创立一个 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: /
拜访验证一下:
$ curl -H "Host: canary.example.com" http://EXTERNAL-IP # EXTERNAL-IP 替换为 Nginx Ingress 本身对外裸露的 IP
nginx-v1
基于 Header 的流量切分
创立 Canary Ingress,指定 v2 版本的后端服务,且加上一些 annotation,实现仅将带有名为 Region 且值为 cd 或 sz 的申请头的申请转发给以后 Canary Ingress,模仿灰度新版本给成都和深圳地区的用户:
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: /
测试拜访:
$ curl -H "Host: canary.example.com" -H "Region: cd" http://EXTERNAL-IP # EXTERNAL-IP 替换为 Nginx Ingress 本身对外裸露的 IP
nginx-v2
$ curl -H "Host: canary.example.com" -H "Region: bj" http://EXTERNAL-IP
nginx-v1
$ curl -H "Host: canary.example.com" -H "Region: cd" http://EXTERNAL-IP
nginx-v2
$ curl -H "Host: canary.example.com" http://EXTERNAL-IP
nginx-v1
能够看到,只有 header Region
为 cd 或 sz 的申请才由 v2 版本服务响应。
基于 Cookie 的流量切分
与后面 Header 相似,不过应用 Cookie 就无奈自定义 value 了,这里以模仿灰度成都地区用户为例,仅将带有名为 user_from_cd
的 cookie 的申请转发给以后 Canary Ingress。先删除后面基于 Header 的流量切分的 Canary Ingress,而后创立上面新的 Canary Ingress:
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: /
测试拜访:
$ curl -s -H "Host: canary.example.com" --cookie "user_from_cd=always" http://EXTERNAL-IP # EXTERNAL-IP 替换为 Nginx Ingress 本身对外裸露的 IP
nginx-v2
$ curl -s -H "Host: canary.example.com" --cookie "user_from_bj=always" http://EXTERNAL-IP
nginx-v1
$ curl -s -H "Host: canary.example.com" http://EXTERNAL-IP
nginx-v1
能够看到,只有 cookie user_from_cd
为 always
的申请才由 v2 版本的服务响应。
基于服务权重的流量切分
基于服务权重的 Canary Ingress 就简略了,间接定义须要导入的流量比例,这里以导入 10% 流量到 v2 版本为例 (如果有,先删除之前的 Canary Ingress):
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: /
测试拜访:
$ for i in {1..10}; do curl -H "Host: canary.example.com" http://EXTERNAL-IP; done;
nginx-v1
nginx-v1
nginx-v1
nginx-v1
nginx-v1
nginx-v1
nginx-v2
nginx-v1
nginx-v1
nginx-v1
能够看到,大略只有十分之一的几率由 v2 版本的服务响应,合乎 10% 服务权重的设置。
存在的缺点
尽管咱们应用 Nginx Ingress 实现了几种不同姿态的金丝雀公布,但还存在一些缺点:
- 雷同服务的 Canary Ingress 只能定义一个,所以后端服务最多反对两个版本。
- Ingress 里必须配置域名,否则不会有成果。
- 即使流量齐全切到了 Canary Ingress 上,旧版服务也还是必须存在,不然会报错。
总结
本文全方位总结了 Nginx Ingress 的金丝雀公布用法,尽管 Nginx Ingress 在金丝雀公布这方面的能力无限,并且还存在一些缺点,但根本也能笼罩一些常见的场景,如果集群中应用了 Nginx Ingress,并且公布的需要也不简单,能够思考应用这种计划。
参考资料
- Nginx Ingress 金丝雀注解官网文档: https://kubernetes.github.io/…
- 在 TKE 上部署 Nginx Ingress: https://cloud.tencent.com/doc…
【腾讯云原生】云说新品、云研新术、云游新活、云赏资讯,扫码关注同名公众号,及时获取更多干货!!