Ingress-Nginx是一个K8S ingress工具,支持配置Ingress Annotations来实现不同场景下的灰度发布和测试。NginxAnnotations 支持以下几种Canary规则:
假设我们现在部署了两个版本的服务,老版本和canary版本
nginx.ingress.kubernetes.io/canary-by-header:基于RequestHeader的流量切分,适用于灰度发布以及 A/B 测试。当RequestHeader 设置为 always时,请求将会被一直发送到 Canary 版本;当 Request Header 设置为 never时,请求不会被发送到 Canary 入口。
nginx.ingress.kubernetes.io/canary-by-header-value:要匹配的 RequestHeader 的值,用于通知 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 版本:
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 替换为 NginxIngress 自身对外暴露的 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% 服务权重的设置
END
作者微信:luckylucky421302
精彩文章推荐
K8s 常见问题
k8s超详细解读
K8s 超详细总结!
K8S 常见面试题总结
k8s控制器Deployment详细介绍:资源清单编写技巧
k8s安装隐患如何优化?从以下维度分享
使用k3s部署轻量Kubernetes集群快速教程
基于Jenkins和k8s构建企业级DevOps容器云平台
k8s原生的CI/CD工具tekton
K8S二次开发-自定义CRD资源
基于Jenkins+git+harbor+Helm+k8s+Istio构建DevOps流水线
基于k8s+Prometheus+Alertmanager+Grafana构建企业级监控告警系统
k8s开启临时容器ephemeral进行debug调试
k8s更新策略-系列文章第一篇:蓝绿发布
k8s更新策略-系列文章第二篇:滚动更新
k8s更新策略-系列文章第三篇:灰度发布