在 Bookinfo 微服務的灰度發佈示例 中,KubeSphere 基於 Istio 對 Bookinfo 微服務示例應用實現了灰度發佈。有用戶表示本身的項目尚未上 Istio,要如何實現灰度發佈?html
在 Ingress-Nginx (0.21.0 版本) 中,引入了一個新的 Canary 功能,可用於爲網關入口配置多個後端服務,還可使用指定的 annotation 來控制多個後端服務之間的流量分配。 KubeSphere 在 2.0.2 的版本 中,升級了項目網關 (Ingress Controller) 版本至 0.24.1,支持基於 Ingress-Nginx 的灰度發佈。node
上一篇文章已經對灰度發佈的幾個應用場景進行了詳細介紹,本文將直接介紹和演示基於 KubeSphere 使用應用路由 (Ingress) 和項目網關 (Ingress Controller) 實現灰度發佈。nginx
說明: 本文用到的示例 yaml 源文件及代碼已上傳至 GitHub,可 clone 至本地方便參考。git
KubeSphere 基於 Nginx Ingress Controller 實現了項目的網關,做爲項目對外的流量入口和項目中各個服務的反向代理。而 Ingress-Nginx 支持配置 Ingress Annotations 來實現不一樣場景下的灰度發佈和測試,能夠知足金絲雀發佈、藍綠部署與 A/B 測試等業務場景。github
Nginx Annotations 支持如下 4 種 Canary 規則:web
-
nginx.ingress.kubernetes.io/canary-by-header
:基於 Request Header 的流量切分,適用於灰度發佈以及 A/B 測試。當 Request Header 設置爲always
時,請求將會被一直髮送到 Canary 版本;當 Request Header 設置爲never
時,請求不會被髮送到 Canary 入口;對於任何其餘 Header 值,將忽略 Header,並經過優先級將請求與其餘金絲雀規則進行優先級的比較。後端-
nginx.ingress.kubernetes.io/canary-by-header-value
:要匹配的 Request Header 的值,用於通知 Ingress 將請求路由到 Canary Ingress 中指定的服務。當 Request Header 設置爲此值時,它將被路由到 Canary 入口。該規則容許用戶自定義 Request Header 的值,必須與上一個 annotation (即:canary-by-header)一塊兒使用。api-
nginx.ingress.kubernetes.io/canary-weight
:基於服務權重的流量切分,適用於藍綠部署,權重範圍 0 - 100 按百分比將請求路由到 Canary Ingress 中指定的服務。權重爲 0 意味着該金絲雀規則不會向 Canary 入口的服務發送任何請求。權重爲 100 意味着全部請求都將被髮送到 Canary 入口。安全-
nginx.ingress.kubernetes.io/canary-by-cookie
:基於 Cookie 的流量切分,適用於灰度發佈與 A/B 測試。用於通知 Ingress 將請求路由到 Canary Ingress 中指定的服務的cookie。當 cookie 值設置爲always
時,它將被路由到 Canary 入口;當 cookie 值設置爲never
時,請求不會被髮送到 Canary 入口;對於任何其餘值,將忽略 cookie 並將請求與其餘金絲雀規則進行優先級的比較。cookie注意:金絲雀規則按優先順序進行以下排序:
canary-by-header - > canary-by-cookie - > canary-weight
把以上的四個 annotation 規則能夠整體劃分爲如下兩類:
1.1. 在 KubeSphere 中建立一個企業空間 (workspace) 和項目 (namespace) ,可參考 多租戶管理快速入門。以下已建立了一個示例項目。
1.2. 爲了快速建立應用,在項目中建立工做負載和服務時可經過 編輯 yaml
的方式,或使用 KubeSphere 右下角的工具箱打開 web kubectl
並使用如下命令和 yaml 文件建立一個 Production 版本的應用並暴露給集羣外訪問。以下建立 Production 版本的 deployment
和 service
。
$ kubectl appy -f production.yaml -n ingress-demo
deployment.extensions/production created
service/production created複製代碼
其中用到的 yaml 文件以下:
production.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: production
spec:
replicas: 1
selector:
matchLabels:
app: production
template:
metadata:
labels:
app: production
spec:
containers:
- name: production
image: mirrorgooglecontainers/echoserver:1.10
ports:
- containerPort: 8080
env:
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
---
apiVersion: v1
kind: Service
metadata:
name: production
labels:
app: production
spec:
ports:
- port: 80
targetPort: 8080
protocol: TCP
name: http
selector:
app: production複製代碼
1.3. 建立 Production 版本的應用路由 (Ingress)。
$ kubectl appy -f production.ingress -n ingress-demo
ingress.extensions/production created複製代碼
其中用到的 yaml 文件以下:
production.ingress
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: production
annotations:
kubernetes.io/ingress.class: nginx
spec:
rules:
- host: kubesphere.io
http:
paths:
- backend:
serviceName: production
servicePort: 80複製代碼
2.1. 此時,在 KubeSphere UI 的企業空間 demo-workspace 下,能夠看到 ingress-demo 項目下的全部資源。
Deployment
Service
Ingress
2.2. 訪問 Production 版本的應用需確保當前項目已開啓了網關,在外網訪問下打開網關,類型爲 NodePort
。
2.3. 以下訪問 Production 版本的應用。
$ curl kubesphere.io:30205
Hostname: production-6b4bb8d58d-7r889
Pod Information:
node name: ks-allinone
pod name: production-6b4bb8d58d-7r889
pod namespace: ingress-demo
pod IP: 10.233.87.165
Server values:
server_version=nginx: 1.12.2 - lua: 10010
Request Information:
client_address=10.233.87.225
method=GET
real path=/
query=
request_version=1.1
request_scheme=http
request_uri=http://kubesphere.io:8080/
Request Headers:
accept=*/*
host=kubesphere.io:30205
user-agent=curl/7.29.0
apiVersion: extensions/v1beta1
x-forwarded-for=192.168.0.88
x-forwarded-host=kubesphere.io:30205
x-forwarded-port=80
x-forwarded-proto=http
x-original-uri=/
x-real-ip=192.168.0.88
x-request-id=9596df96e994ea05bece2ebbe689a2cc
x-scheme=http
Request Body:
-no body in request-複製代碼
參考將上述 Production 版本的 production.yaml
文件,再建立一個 Canary 版本的應用,包括一個 Canary 版本的 deployment
和 service
(爲方便快速演示,僅需將 production.yaml
的 deployment 和 service 中的關鍵字 production
直接替換爲 canary
,實際場景中可能涉及業務代碼變動)。
基於權重的流量切分的典型應用場景就是藍綠部署
,可經過將權重設置爲 0 或 100 來實現。例如,可將 Green 版本設置爲主要部分,並將 Blue 版本的入口配置爲 Canary。最初,將權重設置爲 0,所以不會將流量代理到 Blue 版本。一旦新版本測試和驗證都成功後,便可將 Blue 版本的權重設置爲 100,即全部流量從 Green 版本轉向 Blue。
4.1. 使用如下 canary.ingress
的 yaml 文件再建立一個基於權重的 Canary 版本的應用路由 (Ingress)。
注意:要開啓灰度發佈機制,首先需設置
nginx.ingress.kubernetes.io/canary: "true"
啓用 Canary,如下 Ingress 示例的 Canary 版本使用了基於權重進行流量切分的 annotation 規則,將分配 30% 的流量請求發送至 Canary 版本。
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: canary
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "30"
spec:
rules:
- host: kubesphere.io
http:
paths:
- backend:
serviceName: canary
servicePort: 80複製代碼
4.2. 訪問應用的域名。
說明:應用的 Canary 版本基於權重 (30%) 進行流量切分後,訪問到 Canary 版本的機率接近 30%,流量比例可能會有小範圍的浮動。
4.3. 基於 Request Header 進行流量切分的典型應用場景即灰度發佈或 A/B 測試場景
。參考如下截圖,在 KubeSphere 給 Canary 版本的 Ingress 新增一條 annotation nginx.ingress.kubernetes.io/canary-by-header: canary
(這裏的 annotation 的 value 能夠是任意值),使當前的 Ingress 實現基於 Request Header 進行流量切分。
說明:金絲雀規則按優先順序
canary-by-header - > canary-by-cookie - > canary-weight
進行以下排序,所以如下狀況將忽略原有 canary-weight 的規則。
4.4. 在請求中加入不一樣的 Header 值,再次訪問應用的域名。
說明:
舉兩個例子,如開篇提到的當 Request Header 設置爲
never
或always
時,請求將不會
或一直
被髮送到 Canary 版本;對於任何其餘 Header 值,將忽略 Header,並經過優先級將請求與其餘 Canary 規則進行優先級的比較(以下第二次請求已將
基於 30% 權重
做爲第一優先級)。
4.5. 此時能夠在上一個 annotation (即 canary-by-header)的基礎上添加一條 nginx.ingress.kubernetes.io/canary-by-header-value: user-value
。用於通知 Ingress 將請求路由到 Canary Ingress 中指定的服務。
4.6. 以下訪問應用的域名,當 Request Header 知足此值時,全部請求被路由到 Canary 版本(該規則容許用戶自定義 Request Header 的值)。
4.7. 與基於 Request Header 的 annotation 用法規則相似。例如在 A/B 測試場景
下,須要讓地域爲北京的用戶訪問 Canary 版本。那麼當 cookie 的 annotation 設置爲 nginx.ingress.kubernetes.io/canary-by-cookie: "users_from_Beijing"
,此時後臺可對登陸的用戶請求進行檢查,若是該用戶訪問源來自北京則設置 cookie users_from_Beijing
的值爲 always
,這樣就能夠確保北京的用戶僅訪問 Canary 版本。
灰度發佈能夠保證總體系統的穩定,在初始灰度的時候就能夠對新版本進行測試、發現和調整問題,以保證其影響度。本文經過多個示例演示和說明了基於 KubeSphere 使用應用路由 (Ingress) 和項目網關 (Ingress Controller) 實現灰度發佈,並詳細介紹了 Ingress-Nginx 的四種 Annotation,還未使用 Istio 的用戶也能借助 Ingress-Nginx 輕鬆實現灰度發佈與金絲雀發佈。