[TOC]
## 背景信息
灰度及藍綠發布是為新版本創建一個與老版本完全一致的生產環境,在不影響老版本的前提下,按照一定的規則把部分流量切換到新版本,當新版本試運行一段時間沒有問題后,將用戶的全量流量從老版本遷移至新版本。
其中AB測試就是一種灰度發布方式,一部分用戶繼續使用老版本的服務,將一部分用戶的流量切換到新版本,如果新版本運行穩定,則逐步將所有用戶遷移到新版本。
## 應用場景
使用Nginx Ingress實現灰度發布適用場景主要取決于業務流量切分的策略,目前Nginx Ingress支持基于Header、Cookie和服務權重三種流量切分的策略,基于這三種策略可實現以下兩種發布場景
- 切分部分用戶流量到新版本
假設線上已運行了一套對外提供七層服務的Service A,此時開發了一些新的特性,需要發布上線一個新的版本Service A',但又不想直接替換原有的Service A,而是期望將Header中包含foo=bar或者Cookie中包含foo=bar的用戶請求轉發到新版本Service A'中。待運行一段時間穩定后,再逐步全量上線新版本,平滑下線舊版本。
- 切分一定比例的流量到新版本
假設線上已運行了一套對外提供七層服務的Service B,此時修復了一些問題,需要發布上線一個新的版本Service B',但又不想直接替換原有的Service B,而是期望將20%的流量切換到新版本Service B'中。待運行一段時間穩定后,再將所有的流量從舊版本切換到新版本中,平滑下線舊版本。
## ingress注解說明
Nginx Ingress支持通過配置注解(Annotations)來實現不同場景下的發布和測試,可以滿足灰度發布、藍綠發布、A/B測試等業務場景。具體實現過程如下:為服務創建兩個Ingress,一個為常規Ingress,另一個為帶nginx.ingress.kubernetes.io/canary: "true"注解的Ingress,稱為Canary Ingress;為Canary Ingress配置流量切分策略Annotation,兩個Ingress相互配合,即可實現多種場景的發布和測試。Nginx Ingress的Annotation支持以下幾種規則:
- nginx.ingress.kubernetes.io/canary-by-header
基于Header的流量切分,適用于灰度發布。如果請求頭中包含指定的header名稱,并且值為“always”,就將該請求轉發給Canary Ingress定義的對應后端服務。如果值為“never”則不轉發,可用于回滾到舊版本。如果為其他值則忽略該annotation,并通過優先級將請求流量分配到其他規則。
- nginx.ingress.kubernetes.io/canary-by-header-value
必須與canary-by-header一起使用,可自定義請求頭的取值,包含但不限于“always”或“never”。當請求頭的值命中指定的自定義值時,請求將會轉發給Canary Ingress定義的對應后端服務,如果是其他值則忽略該annotation,并通過優先級將請求流量分配到其他規則。
- nginx.ingress.kubernetes.io/canary-by-header-pattern
與canary-by-header-value類似,唯一區別是該annotation用正則表達式匹配請求頭的值,而不是某一個固定值。如果該annotation與canary-by-header-value同時存在,該annotation將被忽略。
- nginx.ingress.kubernetes.io/canary-by-cookie
基于Cookie的流量切分,適用于灰度發布。與canary-by-header類似,該annotation用于cookie,僅支持“always”和“never”,無法自定義取值。
- nginx.ingress.kubernetes.io/canary-weight
基于服務權重的流量切分,適用于藍綠部署。表示Canary Ingress所分配流量的百分比,取值范圍[0-100]。例如,設置為100,表示所有流量都將轉發給Canary Ingress對應的后端服務。
### 重點注意
> 1. 以上注解規則會按優先級進行評估,優先級為:canary-by-header -> canary-by-cookie -> canary-weight。
> 2. 當Ingress被標記為Canary Ingress時,除了nginx.ingress.kubernetes.io/load-balance和nginx.ingress.kubernetes.io/upstream-hash-by外,所有其他非Canary的注解都將被忽略。
> 3. 相同服務的Canary Ingress(Ingress的YAML文件中host/path相同)僅能夠定義一個,從而使后端服務最多支持兩個版本。
> 4. 即使流量完全切到了Canary Ingress上,舊版服務仍需存在,否則會出現報錯。
> 5. 更多內容請參閱官方文檔[Annotations](https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/#canary)。
## 部署兩個版本服務
在集群中部署兩個版本的Nginx服務,并通過Nginx Ingress對外提供七層域名訪問。
1. 創建第一個版本的Deployment和Service,本文以app-v1為例。YAML示例如下:
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-v1
namespace: default
spec:
replicas: 3
selector:
matchLabels:
app: app-v1
template:
metadata:
labels:
app: app-v1
spec:
containers:
- name: nginx
image: nginx:1.21.4
imagePullPolicy: IfNotPresent
lifecycle:
postStart:
exec:
command:
- /bin/sh
- -c
- "echo \\<b\\>version: v1\\</b\\>, \\<br\\>IP: $(hostname -I), \\<br\\>hostname: $(hostname) > /usr/share/nginx/html/index.html"
---
apiVersion: v1
kind: Service
metadata:
name: app-v1
namespace: default
spec:
type: ClusterIP
ports:
- name: http
port: 80
targetPort: 80
selector:
app: app-v1
```
2. 創建第二個版本的Deployment和Service,本文以app-v2為例。YAML示例如下:
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-v2
namespace: default
spec:
replicas: 3
selector:
matchLabels:
app: app-v2
template:
metadata:
labels:
app: app-v2
spec:
containers:
- name: nginx
image: nginx:1.21.4
imagePullPolicy: IfNotPresent
lifecycle:
postStart:
exec:
command:
- /bin/sh
- -c
- "echo \\<b\\>version: v2\\</b\\>, \\<br\\>IP: $(hostname -I), \\<br\\>hostname: $(hostname) > /usr/share/nginx/html/index.html"
---
apiVersion: v1
kind: Service
metadata:
name: app-v2
namespace: default
spec:
type: ClusterIP
ports:
- name: http
port: 80
targetPort: 80
selector:
app: app-v2
```
3. 查看兩個服務的運行狀況
```shell
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
app-v1-68db595855-99j89 1/1 Running 2 25h
app-v1-68db595855-l8q6h 1/1 Running 0 25h
app-v1-68db595855-z8kwv 1/1 Running 1 25h
app-v2-595cf6b7f-8kh74 1/1 Running 0 25h
app-v2-595cf6b7f-jbp5q 1/1 Running 2 25h
app-v2-595cf6b7f-kspjd 1/1 Running 0 25h
```
4. 創建Ingress,對外暴露服務,指向app-v1版本的服務。YAML示例如下
```yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: app-v1
namespace: default
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
ingressClassName: nginx
rules:
- host: www.ecloud.com
http:
paths:
- path: /nginx(/|)(.*)
backend:
serviceName: app-v1
servicePort: 80
```
5. 驗證服務
- 命令行驗證
```shell
$ for i in `seq 3`;do curl www.ecloud.com/nginx ;sleep 1 ;done
<b>version: v1</b>, <br>IP: 20.0.122.151 , <br>hostname: app-v1-68db595855-l8q6h
<b>version: v1</b>, <br>IP: 20.0.58.213 , <br>hostname: app-v1-68db595855-z8kwv
<b>version: v1</b>, <br>IP: 20.0.32.154 , <br>hostname: app-v1-68db595855-99j89
```
- 瀏覽器驗證

## 灰度發布新版本
基于Header、Cookie和服務權重三種流量切分策略均可實現灰度發布;基于服務權重的流量切分策略,調整新服務權重為100%,即可實現藍綠發布。您可以在下述示例中了解具體使用方法。
### 基于客戶端Header的流量切分場景
1.創建Canary Ingress,指向新版本的后端服務,并增加annotation。YAML示例如下
```yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: app-v2
namespace: default
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-by-header: canary
nginx.ingress.kubernetes.io/canary-by-header-value: "true"
spec:
ingressClassName: nginx
rules:
- host: www.ecloud.com
http:
paths:
- path: /nginx(/|)(.*)
backend:
serviceName: app-v2
servicePort: 80
```
2.驗證服務
- 命令行驗證
```shell
$ for i in `seq 3`;do curl -s -H "canary:true" www.ecloud.com/nginx ;sleep 1 ;done
<b>version: v2</b>, <br>IP: 20.0.32.155 , <br>hostname: app-v2-595cf6b7f-jbp5q
<b>version: v2</b>, <br>IP: 20.0.122.152 , <br>hostname: app-v2-595cf6b7f-8kh74
<b>version: v2</b>, <br>IP: 20.0.135.154 , <br>hostname: app-v2-595cf6b7f-kspjd
```
- 瀏覽器驗證


> Google瀏覽器安裝 `Requestly: Modify Headers, Mock API, Redirect` 插件,可以添加 `header` 鍵值對
### 基于客戶端Cookie的流量切分場景
1.創建Canary Ingress,指向新版本的后端服務,并增加annotation。YAML示例如下
```yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: app-v2
namespace: default
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-by-cookie: canary
spec:
ingressClassName: nginx
rules:
- host: www.ecloud.com
http:
paths:
- path: /nginx(/|)(.*)
backend:
serviceName: app-v2
servicePort: 80
```
2.驗證服務
- 命令行驗證
```shell
$ for i in `seq 3`;do curl -s -H "Cookie: canary=always" -H "Host: www.ecloud.com" http://192.168.31.188/nginx ;sleep 1 ;done
<b>version: v2</b>, <br>IP: 20.0.32.155 , <br>hostname: app-v2-595cf6b7f-jbp5q
<b>version: v2</b>, <br>IP: 20.0.122.152 , <br>hostname: app-v2-595cf6b7f-8kh74
<b>version: v2</b>, <br>IP: 20.0.135.154 , <br>hostname: app-v2-595cf6b7f-kspjd
```
- 瀏覽器驗證


> Google瀏覽器安裝 `EditCookie` 插件,可以添加 `cookie`
### 基于服務權重的流量切分場景
1.創建Canary Ingress,指向新版本的后端服務,并增加annotation。YAML示例如下
```yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: app-v2
namespace: default
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "30"
spec:
ingressClassName: nginx
rules:
- host: www.ecloud.com
http:
paths:
- path: /nginx(/|)(.*)
backend:
serviceName: app-v2
servicePort: 80
```
2.驗證服務
```shell
$for i in `seq 10`;do curl -s -H "Cookie: canary=always" www.ecloud.com/nginx ;sleep 1 ;done
<b>version: v1</b>, <br>IP: 20.0.32.154 , <br>hostname: app-v1-68db595855-99j89
<b>version: v1</b>, <br>IP: 20.0.32.154 , <br>hostname: app-v1-68db595855-99j89
<b>version: v1</b>, <br>IP: 20.0.32.154 , <br>hostname: app-v1-68db595855-99j89
<b>version: v1</b>, <br>IP: 20.0.58.213 , <br>hostname: app-v1-68db595855-z8kwv
<b>version: v2</b>, <br>IP: 20.0.32.155 , <br>hostname: app-v2-595cf6b7f-jbp5q
<b>version: v1</b>, <br>IP: 20.0.58.213 , <br>hostname: app-v1-68db595855-z8kwv
<b>version: v1</b>, <br>IP: 20.0.122.151 , <br>hostname: app-v1-68db595855-l8q6h
<b>version: v2</b>, <br>IP: 20.0.135.154 , <br>hostname: app-v2-595cf6b7f-kspjd
<b>version: v1</b>, <br>IP: 20.0.58.213 , <br>hostname: app-v1-68db595855-z8kwv
<b>version: v2</b>, <br>IP: 20.0.122.152 , <br>hostname: app-v2-595cf6b7f-8kh74
```
> 注意:
> 基于權重(30%)進行流量切分后,訪問到新版本的概率接近30%,流量比例可能會有小范圍的浮動,這屬于正常現象。
將百分比拉到100,則成功實現了藍綠發布。
## 附件
Google瀏覽器插件 `Modify Headers`、`EditCookie` 上傳到百度云盤上,有需要的的話,請到百度云上下載。
> 鏈接:https://pan.baidu.com/s/1fdQxfJeV2uFZ2KrKuu8m4Q
提取碼:q9dx
安裝方法:
打開 `Google瀏覽器` ,輸入 `chrome://extensions/` 進入到擴展程序,然后把下載好的插件拖拽到瀏覽器即可。
- 前言
- 架構
- 部署
- kubeadm部署
- kubeadm擴容節點
- 二進制安裝基礎組件
- 添加master節點
- 添加工作節點
- 選裝插件安裝
- Kubernetes使用
- k8s與dockerfile啟動參數
- hostPort與hostNetwork異同
- 應用上下線最佳實踐
- 進入容器命名空間
- 主機與pod之間拷貝
- events排序問題
- k8s會話保持
- 容器root特權
- CNI插件
- calico
- calicoctl安裝
- calico網絡通信
- calico更改pod地址范圍
- 新增節點網卡名不一致
- 修改calico模式
- calico數據存儲遷移
- 啟用 kubectl 來管理 Calico
- calico卸載
- cilium
- cilium架構
- cilium/hubble安裝
- cilium網絡路由
- IP地址管理(IPAM)
- Cilium替換KubeProxy
- NodePort運行DSR模式
- IP地址偽裝
- ingress使用
- nginx-ingress
- ingress安裝
- ingress高可用
- helm方式安裝
- 基本使用
- Rewrite配置
- tls安全路由
- ingress發布管理
- 代理k8s集群外的web應用
- ingress自定義日志
- ingress記錄真實IP地址
- 自定義參數
- traefik-ingress
- traefik名詞概念
- traefik安裝
- traefik初次使用
- traefik路由(IngressRoute)
- traefik中間件(middlewares)
- traefik記錄真實IP地址
- cert-manager
- 安裝教程
- 頒布者CA
- 創建證書
- 外部存儲
- 對接NFS
- 對接ceph-rbd
- 對接cephfs
- 監控平臺
- Prometheus
- Prometheus安裝
- grafana安裝
- Prometheus配置文件
- node_exporter安裝
- kube-state-metrics安裝
- Prometheus黑盒監控
- Prometheus告警
- grafana儀表盤設置
- 常用監控配置文件
- thanos
- Prometheus
- Sidecar組件
- Store Gateway組件
- Querier組件
- Compactor組件
- Prometheus監控項
- grafana
- Querier對接grafana
- alertmanager
- Prometheus對接alertmanager
- 日志中心
- filebeat安裝
- kafka安裝
- logstash安裝
- elasticsearch安裝
- elasticsearch索引生命周期管理
- kibana安裝
- event事件收集
- 資源預留
- 節點資源預留
- imagefs與nodefs驗證
- 資源預留 vs 驅逐 vs OOM
- scheduler調度原理
- Helm
- Helm安裝
- Helm基本使用
- 安全
- apiserver審計日志
- RBAC鑒權
- namespace資源限制
- 加密Secret數據
- 服務網格
- 備份恢復
- Velero安裝
- 備份與恢復
- 常用維護操作
- container runtime
- 拉取私有倉庫鏡像配置
- 拉取公網鏡像加速配置
- runtime網絡代理
- overlay2目錄占用過大
- 更改Docker的數據目錄
- Harbor
- 重置Harbor密碼
- 問題處理
- 關閉或開啟Harbor的認證
- 固定harbor的IP地址范圍
- ETCD
- ETCD擴縮容
- ETCD常用命令
- ETCD數據空間壓縮清理
- ingress
- ingress-nginx header配置
- kubernetes
- 驗證yaml合法性
- 切換KubeProxy模式
- 容器解析域名
- 刪除節點
- 修改鏡像倉庫
- 修改node名稱
- 升級k8s集群
- 切換容器運行時
- apiserver接口
- 其他
- 升級內核
- k8s組件性能分析
- ETCD
- calico
- calico健康檢查失敗
- Harbor
- harbor同步失敗
- Kubernetes
- 資源Terminating狀態
- 啟動容器報錯