K8S Ingress 實現AB測試、藍綠發布、金絲雀(灰度)發布

假設有如下三個節點的?K8S?集群:

?

k8s31master 是控制節點

k8s31node1、k8s31node2?是工作節點

容器運行時是 containerd

一、場景分析

閱讀本文,默認您已經安裝了 Ingress Nginx。

1)A/B 測試

A/B 測試基于用戶請求的元信息將流量路由到新版本,這是一種基于請求內容匹配的灰度發布策略。只有匹配特定規則的請求才會被引流到新版本,常見的做法包括基于 HTTP Header 和Cookie。基于 HTTP Header 方式,例如 User-Agent 的值為 Android 的請求(來自安卓系統的請求)可以訪問新版本,其他系統仍然訪問舊版本。基于 Cookie 方式,Cookie 中通常包含具有業務語義的用戶信息,例如普通用戶可以訪問新版本,VIP 用戶仍然訪問舊版本。

如下圖所示,某服務當前版本為v1,現在新版本v2要上線。希望安卓用戶可以嘗鮮新功能,其他系統用戶保持不變。

通過在監控平臺觀察舊版本與新版本的成功率、RT對比,當新版本整體服務符合預期后,即可將所有請求切換到新版本v2,最后為了節省資源,可以逐步下線到舊版本v1。

在 K8S 中,可以利用?Ingress Nginx?基于 Header 或 Cookie 進行流量切分的策略來實現 A/B 測試發布。業務使用 Header 或 Cookie 來標識不同類型的用戶,我們通過配置 Ingress 來實現讓帶有指定 Header 或 Cookie 的請求被轉發到新版本,其它的仍然轉發到舊版本,從而實現將新版本灰度給部分用戶。

2)金絲雀發布

金絲雀發布是將少量的請求引流到新版本上,因此部署新版本服務只需極小數的實例。驗證新版本符合預期后,逐步調整流量權重比例,使得流量慢慢從老版本遷移至新版本,期間可以根據設置的流量比例,對新版本服務進行擴容,同時對老版本服務進行縮容,使得底層資源得到最大化利用。

如下圖所示,某服務當前版本為 v1,現在新版本 v2 要上線。為確保流量在服務升級過程中平穩無損,采用金絲雀發布方案,逐步將流量從老版本遷移至新版本。

?在 K8S 中,可以利用?Ingress Nginx?基于權重進行流量切分的策略來實現金絲雀發布。先切一部分的流量到新版本,然后對新版本進行監控,等觀察一段時間穩定后再逐漸加大新版本的流量比例直至完全替換舊版本,最后再平滑下線舊版本,從而實現流量的定向分配。

二、注解介紹

Ingress Nginx 是一個 K8S Ingress 工具,支持配置 Ingress Annotations 來實現不同場景下的灰度發布和測試。

  • 前提:
# 注解的鍵和值只能是字符串。其他類型,如布爾值或數值,必須加引號,例如:"true"、"false"、"100"。
# 開啟灰度發布
nginx.ingress.kubernetes.io/canary: "true"
  • ?Ingress Nginx Annotations 支持以下幾種 Canary 規則:

nginx.ingress.kubernetes.io/canary-by-header:利用請求頭,通知 Ingress 將請求路由到 Canary Ingress 中指定的服務。當請求頭部設置為 always 時,請求將被路由到金絲雀版本。當頭部設置為 never 時,請求永遠不會被路由到金絲雀版本。對于任何其他值,頭部將被忽略,請求將根據優先級與其他金絲雀規則進行比較。

nginx.ingress.kubernetes.io/canary-by-header-value:利用請求頭值,通知 Ingress 將請求路由到 Canary Ingress 中指定的服務。當請求頭設置為該值時,請求將被路由到金絲雀版本。對于任何其他頭值,將忽略該頭,并按照優先級與其他金絲雀規則進行比較。此注解必須配合使用 nginx.ingress.kubernetes.io/canary-by-header。這個注解是nginx.ingress.kubernetes.io/canary-by-header 的擴展,允許自定義請求頭值而不是使用硬編碼值。如果未定義 nginx.ingress.kubernetes.io/canary-by-header 注解,則它沒有任何效果。

nginx.ingress.kubernetes.io/canary-by-header-pattern: 這個注解的作用與 canary-by-header-value 相同,但它使用的是 PCRE 正則表達式匹配。注意,當設置了 canary-by-header-value 時,這個注解將被忽略。如果給定的正則表達式在請求處理過程中導致錯誤,該請求將被認為不匹配。

nginx.ingress.kubernetes.io/canary-by-cookie:利用 cookie,通知 Ingress 將請求路由到 Canary Ingress 中指定的服務。當 cookie 值設置為 always 時,請求將始終路由到金絲雀版本。當 cookie 設置為 never 時,請求永遠不會路由到金絲雀版本。對于任何其他值,將忽略 cookie,并根據優先級將請求與其他金絲雀規則進行比較。

nginx.ingress.kubernetes.io/canary-weight:整數(0-)百分比的隨機請求將會被路由到金絲雀 Ingress 中指定的服務。權重為 0 表示該金絲雀規則不會將任何請求發送到金絲雀 Ingress 中的服務。權重為?<weight-total> 表示所有請求都將發送到 Ingress 中指定的備用服務。?<weight-total> 默認為 100,可以通過 nginx.ingress.kubernetes.io/canary-weight-total 進行增加。

nginx.ingress.kubernetes.io/canary-weight-total:流量的總權重。如果未指定,默認為 100。

金絲雀規則的評估順序遵循優先級

優先級順序如下:按頭部信息金絲雀 -> 按Cookie金絲雀 -> 權重金絲雀

三、實驗準備

  • 鏡像下載

[root@k8s31node1 ~]# ctr -n=k8s.io images pull swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/openresty/openresty:latest
[root@k8s31node1 ~]# ctr -n=k8s.io images tag  swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/openresty/openresty:latest  docker.io/openresty/openresty:latest[root@k8s31node2 ~]# ctr -n=k8s.io images pull swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/openresty/openresty:latest
[root@k8s31node2 ~]# ctr -n=k8s.io images tag  swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/openresty/openresty:latest  docker.io/openresty/openresty:latest
  • ?部署 v1

apiVersion: apps/v1
kind: Deployment
metadata:name: nginx-v1
spec:replicas: 1selector:matchLabels:app: nginxversion: v1template:metadata:labels:app: nginxversion: v1spec:containers:- name: nginximage: "openresty/openresty:latest"imagePullPolicy: IfNotPresentports:- name: httpprotocol: TCPcontainerPort: 80volumeMounts:- mountPath: /usr/local/openresty/nginx/conf/nginx.confname: configsubPath: nginx.confvolumes:- name: configconfigMap:name: nginx-v1
---
apiVersion: v1
kind: ConfigMap
metadata:labels:app: nginxversion: v1name: 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: ClusterIPports:- port: 80protocol: TCPname: httpselector:app: nginxversion: v1

該 yml 定義了三個資源 ConfigMap、Deployment、Service。

  • ConfigMap 定義了一個?nginx.conf 配置文件,使用 lua 腳本輸出 nginx-v1。
  • Deployment 定義了一個 Pod,里面運行?openresty 它是一個封裝了 nginx+lua 的 web 服務器。Pod 有兩個標簽?app: nginx、version: v1。
  • Service 代理了?Deployment 運行的 Pod。

部署 v2

apiVersion: apps/v1
kind: Deployment
metadata:name: nginx-v2
spec:replicas: 1selector:matchLabels:app: nginxversion: v2template:metadata:labels:app: nginxversion: v2spec:containers:- name: nginximage: "openresty/openresty:latest"imagePullPolicy: IfNotPresentports:- name: httpprotocol: TCPcontainerPort: 80volumeMounts:- mountPath: /usr/local/openresty/nginx/conf/nginx.confname: configsubPath: nginx.confvolumes:- name: configconfigMap:name: nginx-v2
---
apiVersion: v1
kind: ConfigMap
metadata:labels:app: nginxversion: v2name: 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: ClusterIPports:- port: 80protocol: TCPname: httpselector:app: nginxversion: v2

?創建?v1 ingress

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:name: nginx
spec:ingressClassName: nginxrules:- host: canary.example.comhttp:paths:- path: /          pathType:  Prefixbackend:  #配置后端服務service:name: nginx-v1port:number: 80

?對外暴露域名?canary.example.com 訪問。

修改本機 hosts

192.168.40.20 canary.example.com 

?瀏覽器訪問

四、實戰

1)nginx.ingress.kubernetes.io/canary-by-header

  • 創建 v2 ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:annotations:nginx.ingress.kubernetes.io/canary: "true" # 開啟金絲雀nginx.ingress.kubernetes.io/canary-by-header: "Canary"name: nginx-canary
spec:ingressClassName: nginxrules:- host: canary.example.comhttp:paths:- path: /pathType:  Prefixbackend:  #配置后端服務service:name: nginx-v2port:number: 80

現在系統里面有兩個 ingress,一個 v1 版本,一個 v2 金絲雀版本。?

注意:

ingress 要 ADDRESS 那一欄出來才能訪問。

curl -H "Host: canary.example.com" -H "Canary: always" 192.168.40.20

請求頭參數 Canary 匹配?always,走金絲雀版本服務。

請求頭參數 Canary 不匹配?always,走 v1 服務。

2)?nginx.ingress.kubernetes.io/canary-by-header-value

刪掉上一個 ingress,以免干擾下面實驗。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:annotations:nginx.ingress.kubernetes.io/canary: "true"nginx.ingress.kubernetes.io/canary-by-header: "Canary"nginx.ingress.kubernetes.io/canary-by-header-value: "v2"name: nginx-canary
spec:ingressClassName: nginxrules:- host: canary.example.comhttp:paths:- path: /pathType:  Prefixbackend:  #配置后端服務service:name: nginx-v2port:number: 80

?請求頭參數 Canary 匹配 v2,走金絲雀版本服務。

curl -H "Host: canary.example.com" -H "Canary: v1" 192.168.40.20

?3)?nginx.ingress.kubernetes.io/canary-by-header-pattern

刪掉上一個 ingress,以免干擾下面實驗。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:annotations:nginx.ingress.kubernetes.io/canary: "true"nginx.ingress.kubernetes.io/canary-by-header: "Canary"nginx.ingress.kubernetes.io/canary-by-header-pattern: "v2|v3" # 匹配v2或v3name: nginx-canary
spec:ingressClassName: nginxrules:- host: canary.example.comhttp:paths:- path: /pathType:  Prefixbackend:  #配置后端服務service:name: nginx-v2port:number: 80

?請求頭參數 Canary 匹配 v2 或 v3,走金絲雀版本服務。

curl -H "Host: canary.example.com" -H "Canary: v1" 192.168.40.20

?4)nginx.ingress.kubernetes.io/canary-by-cookie

刪掉上一個 ingress,以免干擾下面實驗。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:annotations:nginx.ingress.kubernetes.io/canary: "true"nginx.ingress.kubernetes.io/canary-by-cookie: "Canary"name: nginx-canary
spec:ingressClassName: nginxrules:- host: canary.example.comhttp:paths:- path: /pathType:  Prefixbackend:  #配置后端服務service:name: nginx-v2port:number: 80

cookie 參數?Canary 匹配 always,走金絲雀版本服務。

cookie 參數 Canary 不匹配?always,走 v1 服務。

curl -H "Host: canary.example.com" -H "Cookie: Canary=always" 192.168.40.20

5)nginx.ingress.kubernetes.io/canary-weight

?刪掉上一個 ingress,以免干擾下面實驗。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:annotations:nginx.ingress.kubernetes.io/canary: "true"nginx.ingress.kubernetes.io/canary-weight: "10"name: nginx-canary
spec:ingressClassName: nginxrules:- host: canary.example.comhttp:paths:- path: /pathType:  Prefixbackend:  #配置后端服務service:name: nginx-v2port:number: 80

10%的流量打到金絲雀服務。

for i in {1..10}; do curl -H "Host: canary.example.com" 192.168.40.20; done;

五、金絲雀比較

實現金絲雀發布的方式有很多,從Java程序員的角度來看,就有:

基于 Spring Cloud Gateway 路由斷言工廠、基于 Nginx、基于 K8S Deployment 偽金絲雀、基于 Ingress Nginx 注解、基于 Istio 流量切分。基于 K8S Gateway API。

基于 Spring Cloud Gateway 路由斷言工廠:路由規則變化很難做到實時響應,要實現實時響應代碼實現復雜。

基于 Nginx:要有很多的配置。

基于 K8S Deployment 偽金絲雀:沒有實現流量的切分。

基于 Istio 流量切分:技術棧門檻高。需要對于服務網格的一整套有所了解。

綜上來看,基于 K8S Gateway API?是配置最簡單的方式了。

Ingress Nginx 注解,實現金絲雀還需要有兩個 Ingress,一個分發舊版本流量,一個分發灰度版本流量。K8S Gateway API 只需要一個。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/80292.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/80292.shtml
英文地址,請注明出處:http://en.pswp.cn/web/80292.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

深入理解構造函數,析構函數

目錄 1.引言 2.構造函數 1.概念 2.特性 3.析構函數 1.概念 2.特性 1.引言 如果一個類中什么都沒有&#xff0c;叫作空類. class A {}; 那么我們這個類中真的是什么都沒有嗎?其實不是,如果我們類當中上面都不寫.編譯器會生成6個默認的成員函數。 默認成員函數:用戶沒有顯…

Oracle 11.2.0.4 pre PSU Oct18 設置SSL連接

Oracle 11.2.0.4 pre PSU Oct18 設置SSL連接 1 說明2 客戶端配置jdk環境3服務器檢查oracle數據庫補丁4設置ssla 服務器配置walletb 上傳測試腳本和配置文件到客戶端c 服務器修改數據庫偵聽和sqlnet.orad 修改客戶端的sqlnet.ora和tnsnames.ora的連接符e 修改java代碼的數據連接…

BrepGen中的幾何特征組裝與文件保存詳解 deepwiki occwl OCC包裝庫

有這種好東西我怎么不知道 AutodeskAILab/occwl: Lightweight Pythonic wrapper around pythonocc 組裝幾何特征以創建B-rep模型 保存為STEP和STL文件細說 Fast 快速 Searched across samxuxiang/BrepGen Ill explain how BrepGen assembles geometric features to create B-r…

重慶 ICPC 比賽游記

2025.5.9 比賽前一天晚上&#xff0c;激動地睡不著覺&#xff0c;起來收拾了好多東西。&#xff08;其實就四本書&#xff0c;剩下的全是零食……關鍵在于這四本書基本沒用。&#xff09; 2025.5.10 學校喪心病狂的讓我們 6:20 到校門口集合坐車&#xff08;據說是怕趕不上比…

0x08.Redis 支持事務嗎?如何實現?

回答重點 Redis 支持事務,但它的事務與 MySQL 等關系型數據庫的事務有著本質區別。MySQL 中的事務嚴格遵循 ACID 特性,而 Redis 中的事務主要保證的是命令執行的原子性和隔離性,即所有命令在一個不可分割的操作中順序執行,不會被其他客戶端的命令請求所打斷。 最關鍵的區…

佰力博科技與您探討表面電阻的測試方法及應用領域

表面電阻測試是一種用于測量材料表面電阻值的技術&#xff0c;廣泛應用于評估材料的導電性能、靜電防護性能以及絕緣性能。 1、表面電阻的測試測試方法&#xff1a; 表面電阻測試通常采用平行電極法、同心圓電極法和四探針法等方法進行。其中&#xff0c;平行電極法通過在試樣…

數據庫的規范化設計方法---3種范式

第一范式&#xff08;1NF&#xff09;&#xff1a;確保表中的每個字段都是不可分割的基本數據項。 第二范式&#xff08;2NF&#xff09;&#xff1a;在滿足1NF的基礎上&#xff0c;確保非主屬性完全依賴于主鍵。 第三范式&#xff08;3NF&#xff09;&#xff1a;在滿足2NF的基…

產品經理入門(2)產品體驗報告

產品體驗報告大綱&#xff1a;重點在產品體驗——優點。 1.產品概括 可以從各大平臺搜產品介紹。 2.市場分析 按照產品方向分析各個指標——包括有效使用時間,市場規模等。 3. 用戶分析——對用戶通過各項指標畫像。 4.產品體驗——對各項功能與設計的體驗。 5.報告總結

[Java][Leetcode simple] 13. 羅馬數字轉整數

一、自己想的 只有提到的六種情況是-&#xff0c;其他都是 public int romanToInt1(String s) {int res 0;int n s.length();Map<Character, Integer> map new HashMap<>();map.put(I, 1);map.put(V, 5);map.put(X, 10);map.put(L, 50);map.put(C, 100);map.pu…

如何在 CentOS 7 虛擬機上配置靜態 IP 地址并保持重啟后 SSH 連接

在使用 CentOS 7 的虛擬機時&#xff0c;我們通常需要配置靜態 IP 地址&#xff0c;以確保在每次虛擬機重啟后能夠通過 SSH 連接。本文將介紹如何在 CentOS 7 系統中配置靜態 IP 地址&#xff0c;并確保配置在系統重啟后依然生效。 步驟 1&#xff1a;檢查虛擬機網絡接口 首先…

matlab求解問題

一、目的 掌握Matlab中函數求導、函數極值和極限問題的求解,能夠借助Matlab工具對簡單優化模型進行求解。 二、內容與設計思想 1、函數求導 1.1求解給定函數的一階導數&#xff1a;diff(y, x)用于對變量x求y的導數。 1.2求解給定函數的二階導數&#xff1a;在求出一階導數的…

C語言斐波那契數列

斐波那契數列&#xff08;Fibonacci sequence&#xff09;&#xff0c;又稱黃金分割數列 、兔子數列。由意大利數學家萊昂納多?斐波那契在 1202 年提出&#xff0c;源于其《算盤書》中一道兔子繁殖問題。定義&#xff1a;在數學上&#xff0c;該數列以遞歸形式定義。最常見的是…

AI浪潮:開啟科技新紀元

AI 的多面應用? AI 的影響力早已突破實驗室的圍墻&#xff0c;在眾多領域落地生根&#xff0c;成為推動行業變革的重要力量。 在醫療領域&#xff0c;AI 宛如一位不知疲倦的助手&#xff0c;助力醫生提升診療效率與準確性。通過對海量醫學影像的深度學習&#xff0c;AI 能夠快…

Ansys 計算剛柔耦合矩陣系數

Ansys 計算剛柔耦合系數矩陣 文章目錄 Ansys 計算剛柔耦合系數矩陣衛星的剛柔耦合動力學模型采用 ANSYS 的 APDL 語言的計算方法系統轉動慣量的求解方法參考文獻 衛星的剛柔耦合動力學模型 柔性航天器的剛柔耦合動力學模型可以表示為 m v ˙ B t r a n η F J ω ˙ ω J…

算法題(148):排座椅

審題&#xff1a; 本題需要我們找到最佳的排座椅方案&#xff0c;并輸出行&#xff0c;列方案 思路&#xff1a; 方法一&#xff1a;簡單貪心 由于題目會告訴我們有哪幾對的同學會交頭接耳&#xff0c;所以我們可以記錄下第幾行/第幾列上可以隔開的同學對數&#xff0c;而題目限…

企業級電商數據對接:1688 商品詳情 API 接口開發與優化實踐

在數字化浪潮席卷全球的當下&#xff0c;企業級電商平臺之間的數據對接已成為提升運營效率、增強市場競爭力的關鍵環節。作為國內知名的 B2B 電商平臺&#xff0c;1688 擁有海量商品資源&#xff0c;通過開發和優化商品詳情 API 接口&#xff0c;企業能夠快速獲取商品信息&…

【Cesium入門教程】第七課:Primitive圖元

Cesium豐富的空間數據可視化API分為兩部分&#xff1a;primitive API面向三維圖形開發者&#xff0c;更底層一些。 Entity API是數據驅動更高級一些。 // entity // 調用方便&#xff0c;封裝完美 // 是基于primitive的封裝// primitive // 更接近底層 // 可以繪制高級圖形 /…

Oracle APEX 必須輸入項目標簽型號顯示位置

1. 正常Oracle APEX中必須輸入項目標簽的紅星顯示在標簽文字左側&#xff0c;偏偏項目要求顯示在右側&#xff0c; 加入如下全局CSS代碼 .t-Form-label {display: flex;flex-direction: row-reverse;gap: 1px; }以上。

深入理解 TypeScript 中的 unknown 類型:安全處理未知數據的最佳實踐

在 TypeScript 的類型體系中&#xff0c;unknown 是一個極具特色的類型。它與 any 看似相似&#xff0c;卻在安全性上有著本質差異。本文將從設計理念、核心特性、使用場景及最佳實踐等方面深入剖析 unknown&#xff0c;幫助開發者在處理動態數據時既能保持靈活性&#xff0c;又…

項目QT+ffmpeg+rtsp(二)——海康威視相機測試

文章目錄 前言一、驗證RTSP地址的有效性1.1 使用VLC播放器驗證1.2 使用FFmpeg命令行驗證1.3 使用Python代碼檢查網絡連接1.4 檢查攝像頭Web界面1.5 使用RTSP客戶端工具二、關于IPV4的地址2.1 原來2.1.1 原因2.2 解決2.3 顯示前言 昨晚拿到一個海康威視的相機,是連接上了交換機…