灰度發佈是一種常見的服務滾動升級或 A/B 測試策略。在新版本服務正式發佈前,能夠部署少許的新版本服務和上個版本共存,用部分生產流量測試新版本的功能和特性。若是新版本反饋良好,則能夠漸進地提升新版本的比例或者所有替換成新版本,若是有問題也可以及時撤回,不至於形成太大範圍的影響。nginx
目前,原生容器發佈基本都是使用 deployment,經過給 deployment 和 service 靈活配置 labels ,能夠實現一種基於服務版本的灰度發佈。正則表達式
因爲原生 Ingress 對象描述能力的限制,一些常見 Ingress controller 的灰度發佈功能也大打折扣,很難知足用戶灰度發佈的實際需求。api
博雲基於原生Ingress,作了大量加強,基於請求特徵的灰度發佈是其中一個重要特性。app
使用 deployment 實施灰度發佈負載均衡
經過配置 pod labels 和 service label selector,Kubernetes 原生支持灰度發佈。假設咱們部署了 echo-demo 服務的兩個版本的 deployment:dom
echo-demo-v1.yamlcurl
name: echo-demo-v1 replicas: 3 ... labels: app: echo-demo track: stable ... image: deploy.bocloud/ingress/echo-demo:1.0.0
echo-demo-v2.yamlide
name: echo-demo-v2 replicas: 2 ... labels: app: echo-demo track: canary ... image: deploy.bocloud/ingress/echo-demo:2.0.0
以及一個 echo-demo service:測試
apiVersion: v1url
kind: Service
metadata:
name: echo-demo
spec:
ports:
- port: 80 protocol: TCP targetPort: 8080
selector:
app: echo-demo
上述配置中,echo-demo service 聚合了 echo-demo-v1 和 echo-demo-v2 兩個版本的服務,兩個版本分別有 3 個和 2 個實例。此時咱們訪問 echo-demo service,請求將根據實例數量的佔比按 3:2 的比例分佈到 v1 和 v2 兩個服務中。這樣就實現了基於權重的灰度發佈。
然而這種灰度發佈卻自有其限制和問題。首先,若是咱們給服務加上自動水平伸縮(HPA),那麼兩個版本的服務將徹底根據各自的負載狀況獨立調整 pod 實例數量,這樣一來兩個版本的實例數量和比例就會發生變化,打破咱們一開始設置的灰度比例。其次,若是咱們只想配置很小比例的請求到灰度版本,好比 100:1,那麼在最少提供一個灰度版本 pod 的前提下,須要配置最少 100 箇舊版本實例,很不靈活。第三,除了按比例的灰度策略,有時可能還須要根據請求特徵來辨別用戶並分配灰度服務到其中的一小部分。因爲deployment有着上述的缺陷,致使其不多被當作灰度使用的緣由。因此在實際應用當中,灰度發佈基本上 由ingress來作。而這幾個問題,均可以經過使用 Ingress 灰度發佈方案來解決。
Ingress 能描述灰度發佈嗎?
Ingress 是 Kubernetes 平臺的一個原生 API,定義了 HTTP 路由到 Kubernetes service 的映射。Ingress Controller 依據 Ingress 對象的聲明,動態生成負載均衡器的配置文件,由負載均衡器將 k8s 內部服務暴露出去,Nginx Ingress Controller 是使用最普遍的一個 Ingress 控制器。一個典型的 Ingress 描述文件以下:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: echo-demo
labels:
app: echo-demo
annotations:
kubernetes.io/ingress.class: nginx
namespace: default
spec:
rules:
- host: test.domain.com http: paths: - backend: serviceName: echo-demo-v1 servicePort: 80 path: /echo - backend: serviceName: hello-world servicePort: 8080 path: /hello
由 Ingress API 能夠看到,一個域名下能夠定義多個訪問路徑,然而一個路徑卻只能定義一個 service。因而,在使用 Ingress 對象來描述灰度發佈的情形下,要實現一個訪問端點與多個服務版本的映射,仍然只能採用上述一個 kubernetes service 聚合多個版本的 deployment 的方式,這種方案的種種問題上文已經分析過了。社區版 Nginx Ingress Controller 受限於 Ingress API 的描述能力,對灰度發佈的支持徹底是不可用的狀態。有沒有一種辦法,既兼容 Ingress API,又能作到一個訪問端點映射到多個 service 呢?
博雲基於 Nginx Ingress Controller 開發的 Ingress 控制器 BeyondELB,設計了一種 Ingress 組合模型,在兼容 Ingress API 的基礎上,用 labels 給 Ingress 對象分類,並將多個不一樣類別的 Ingress 對象組合成一個邏輯 Ingress,從而實現了一個訪問端點到多個 service 的映射。這種組合模型爲實現另外一種灰度發佈方案提供了可能。
使用 BeyondELB
實施基於權重的灰度發佈
上面能夠看到,使用 deployment labels 實現的基於權重的灰度發佈,其灰度比例徹底依賴於服務的實例數量,而引入 HPA 以後服務實例數量可能會發生傾斜從而打破灰度比例。而由 Ingress 組合模型實現的灰度方案中,一個訪問端點可以配置多個 service,每一個 service 有本身的灰度權重和同一版本的服務實例,灰度權重再也不和服務實例數量相綁定。經過將灰度權重和服務實例數量解耦,權重能夠隨時按需調整,服務的實例數量則能夠根據負載狀況自行伸縮,不會影響到設定好的灰度比例。
採用了 Ingress 組合模型的 BeyondELB 支持上述權重灰度發佈策略。能夠爲某個 Ingress 訪問路徑定義一個或多個服務版本,而後爲不一樣服務版本設定灰度權重,請求將按照灰度權重分配到不一樣版本的服務中。
上圖中,選擇開啓基於權重的灰度發佈,定義了一個灰度服務並設定 20 的權重,若是主版本服務的權重設定爲 80(圖中未給出主服務版本定義),則請求將按照 4:1 的比例分配到主版本服務和灰度版本服務。下面對配置了 80/20 權重的服務連續請求 100 次,可見流量按設定的比例分配到 v1 和 v2 服務中。
$ for i in seq 1 100
; do curl -s http://test.domain.com/weight; done | jq .version | sort | uniq -c
78 "1.0.0" 22 "2.0.0"
使用 BeyondELB
實施基於請求特徵的版本級灰度發佈
在某些場景下,可能會須要對灰度選擇有更好的控制,好比對於 HTTP 服務來講,能夠經過請求特徵甄別用戶,容許將具備特定請求 Header 或 Cookie 的用戶發送到灰度版本。
上圖中,咱們添加了一個灰度服務版本,而且設定灰度策略爲「基於請求特徵」。當請求附有名爲 "canary" 值爲 "true" 的請求 Header 時,將由該灰度服務版本響應;而其它未匹配該灰度條件的請求則由主服務版本響應(圖中未給出主服務版本定義)。
咱們經過如下兩個腳本測試基於 Header 的灰度效果。
請求攜帶名爲 canary 且其值爲 "true" 的 Header:
$ for i in seq 1 100
; do curl -s -H "canary: true" http://test.domain.com/header; done | jq .version | sort | uniq -c
100 "1.0.0"
請求不攜帶名爲 "canary" 的 Header:
$ for i in `seq 1 100`; do curl -s http://test.domain.com/header; done | jq .version | sort | uniq -c
100 "2.0.0"
除了基於請求 Header 的灰度匹配策略,博雲的 Ingress Controller 還支持基於請求 Cookie 的匹配策略,以及多個 Header 或 Cookie 灰度條件組合的匹配策略。咱們還能夠用正則表達式匹配 operator 來實現更具體化的灰度方案,好比以下匹配表達式能夠將 User ID 以 3 結尾的用戶發送到灰度服務版本。
(header("x-userid" regex "^[0-9]+3$")
總結
經過使用 Kubernetes pod labels 和 service,能夠初步實現基於權重的灰度發佈。但這種灰度發佈依賴於服務版本的實例數量,不只不靈活並且在引入 HPA 時會形成服務比例傾斜。博雲自研的 Ingress Controller 將灰度權重和服務實例數量解耦,服務可依據負載和 HPA 規則自行伸縮實例,不影響灰度比例。而基於 Header 或 Cookie 的灰度匹配策略,爲實現更可控的灰度方案提供了支持。除了加強的灰度發佈能力,博雲商用版本的 Ingress Controller 還支持租戶級負載、熱更新等特性,後續將會逐步介紹。