簡(jiǎn)化后端服務(wù)的 A/B/n 測(cè)試
A/B/n 測(cè)試,或拆分測(cè)試,是一種測(cè)試過程,用戶流量通過該過程隨機(jī)分布在應(yīng)用程序(或應(yīng)用程序組件)的兩個(gè)或多個(gè)版本之間。評(píng)估業(yè)務(wù)指標(biāo)以確定獲勝版本——產(chǎn)生更大利潤(rùn)或業(yè)務(wù)價(jià)值的版本。例如,購(gòu)物應(yīng)用程序可能使用收入和用戶參與度作為業(yè)務(wù)指標(biāo)。
我們專注于部署在 Kubernetes 中的后端服務(wù)的 A/B/n 測(cè)試。例如,在下圖中,前端可能是一個(gè)基于Node.js的在線商店。它依靠后端推薦服務(wù)向用戶提供產(chǎn)品建議。我們有興趣對(duì)推薦服務(wù)的多個(gè)版本進(jìn)行 A/B/n 測(cè)試。在圖中,我們有兩個(gè)版本,v1(當(dāng)前或默認(rèn)版本)和v2 (候選版本)。
計(jì)算業(yè)務(wù)指標(biāo)
A/B/n 測(cè)試依賴于對(duì)業(yè)務(wù)指標(biāo)的評(píng)估。這些指標(biāo)衡量應(yīng)用程序特定版本的好處或價(jià)值。例如,在在線商店應(yīng)用程序中,相關(guān)指標(biāo)可能是銷售收入或用戶參與度。業(yè)務(wù)指標(biāo)是特定于應(yīng)用程序的;它們不能由基礎(chǔ)設(shè)施計(jì)算,而必須由應(yīng)用程序本身計(jì)算。當(dāng)銷售收入等指標(biāo)由應(yīng)用程序組件(如上面的前端在線商店)計(jì)算時(shí),間接包括了后端推薦引擎的貢獻(xiàn)。但是,前端組件無法將度量值與特定版本的后端相關(guān)聯(lián),因?yàn)樗ǔ2恢朗褂昧四膫€(gè)版本的后端服務(wù)。
Iter8 SDK
為了正確地將指標(biāo)歸因于后端版本,前端有必要知道每個(gè)用戶會(huì)話正在使用哪個(gè)后端版本。為了協(xié)助前端服務(wù),可以使用 Iter8 SDK。Iter8是一個(gè)開源的Kubernetes發(fā)布優(yōu)化器,可以幫助您在幾秒鐘內(nèi)開始測(cè)試 Kubernetes 應(yīng)用程序。使用 Iter8,您可以執(zhí)行各種實(shí)驗(yàn),例如 SLO 驗(yàn)證、金絲雀測(cè)試、混沌注入測(cè)試,以及現(xiàn)在的 A/B/n 測(cè)試。Iter8 SDK 提供了兩個(gè)接口:
- Lookup(component, user_session),它標(biāo)識(shí)一個(gè)組件的版本,調(diào)用者應(yīng)該使用該版本向給定的用戶會(huì)話發(fā)送請(qǐng)求。
- WriteMetric(metric_value, component, user_session),它將度量與組件的推薦版本相關(guān)聯(lián)。
下面的序列圖顯示了我們的購(gòu)物應(yīng)用程序?qū)@些接口的使用:
為響應(yīng)用戶請(qǐng)求,前端組件調(diào)用Lookup()以確定要使用哪個(gè)版本的后端組件。Lookup()返回固定數(shù)量的用戶定義曲目標(biāo)簽之一。Lookup()保證為相同的用戶會(huì)話推薦相同的曲目標(biāo)簽,確保后續(xù)對(duì)后端的調(diào)用將發(fā)送到相同的版本。然后,前端服務(wù)將其請(qǐng)求發(fā)送到推薦的后端,使用軌道作為路由的關(guān)鍵。當(dāng)前端稍后為用戶會(huì)話計(jì)算業(yè)務(wù)指標(biāo)時(shí),它可以安全地與推薦的后端版本相關(guān)聯(lián)。WriteMetric()可用于執(zhí)行此操作,從而無需前端跟蹤到后端版本的映射。
由于Lookup()返回一組固定的軌道標(biāo)簽中的一個(gè),前端服務(wù)必須配置為將流量路由到一組固定的服務(wù)——每個(gè)軌道一個(gè)。要跟蹤的版本的映射隨時(shí)間變化,并且作為候選版本部署的一部分使用已部署的 Kubernetes 對(duì)象上的標(biāo)簽完成。
顯然,SDK 的引入對(duì)應(yīng)用程序前端組件的開發(fā)人員提出了要求。此外,該方法依賴于前端應(yīng)用程序可以根據(jù)一組軌道標(biāo)識(shí)符路由后端請(qǐng)求的假設(shè)。這意味著對(duì)應(yīng)用程序配置的要求。我們通過示例演示使用 Iter8 SDK 是多么容易。
Iter8 SDK 使用 gRPC 實(shí)現(xiàn)。該協(xié)議緩沖區(qū)文檔中描述了這些接口。可以從中為各種語言生成特定于語言的代碼。接口本身由 A/B/n 服務(wù)實(shí)現(xiàn)。
A/B/n 測(cè)試的應(yīng)用程序開發(fā)
我們將考慮一個(gè)簡(jiǎn)單的兩層應(yīng)用程序。用 Node.js 編寫的前端代表一個(gè)在線商店。 用Go編寫的后端表示由前端調(diào)用的推薦引擎,用于向用戶展示替代產(chǎn)品??梢栽诖颂幷业酱藨?yīng)用程序的完整源代碼。
前端組件支持兩個(gè)接口:
- /getRecommendation要求產(chǎn)品推薦。
- /buy完成購(gòu)買。
作為購(gòu)買(請(qǐng)求)的副作用,/buy計(jì)算用戶會(huì)話的業(yè)務(wù)指標(biāo)。接口的實(shí)現(xiàn)/getRecommendation依賴于后端推薦服務(wù)。我們想在此后端推薦服務(wù)上運(yùn)行 A/B/n 測(cè)試。
Iter8 SDK 使用 gRPC 實(shí)現(xiàn)。這些接口在協(xié)議緩沖區(qū)文檔中進(jìn)行了描述。可以從中為各種語言生成特定于語言的代碼。我們生成的代碼在示例應(yīng)用程序中,可以直接復(fù)制以用于您自己的應(yīng)用程序。
要使用 Iter8 SDK,需要 gRPC 和生成的庫:
并實(shí)例化一個(gè)客戶端:
此客戶端用于兩種用例:調(diào)用后端服務(wù)之前和寫入指標(biāo)值時(shí)。
調(diào)用后端服務(wù)
在調(diào)用后端推薦服務(wù)之前,需要Lookup()先調(diào)用該方法。返回的軌道標(biāo)識(shí)符應(yīng)該用作索引來選擇發(fā)送請(qǐng)求的路線。在我們的示例前端中,這可以按如下方式實(shí)現(xiàn):
在此實(shí)現(xiàn)中,用戶會(huì)話是從請(qǐng)求標(biāo)頭中提取的X-User。請(qǐng)注意,如果與 Iter8 SDK 交互出現(xiàn)任何問題,將選擇默認(rèn)路由??梢栽诖颂幷业酵暾氖纠a。
編寫指標(biāo)
當(dāng)/buy調(diào)用接口時(shí),表示銷售完成,計(jì)算業(yè)務(wù)指標(biāo)。在我們的示例應(yīng)用程序中,一個(gè)隨機(jī)值被分配給指標(biāo)sample_metric:
而已!不需要進(jìn)一步的更改,無論運(yùn)行了多少 A/B/n 測(cè)試——或者沒有。
可以在此處 (Node.js)找到完整的示例前端代碼。Python和Go中提供了替代實(shí)現(xiàn)。與節(jié)點(diǎn)示例一樣,生成的代碼可以直接復(fù)制到您自己的應(yīng)用程序中。
為 A/B/n 測(cè)試部署應(yīng)用程序
在部署將要進(jìn)行 A/B/n 測(cè)試的應(yīng)用程序組件時(shí),唯一的要求是添加 Iter8 A/B/n 服務(wù)用于標(biāo)識(shí)組件版本的標(biāo)簽。在我們的例子中,我們計(jì)劃只測(cè)試后端推薦組件。
Iter8 SDK要求每個(gè)版本部署的資源實(shí)例中至少有一個(gè)包含以下標(biāo)簽:
- app.kubernetes.io/name:應(yīng)用程序(組件)名稱。
- app.kubernetes.io/version: 版本名稱。
- iter8-tools/track:要用于此版本的曲目標(biāo)簽。
- iter8-tools/abn: 指示版本是否準(zhǔn)備好接收流量的標(biāo)志。
作為說明,可以使用以下命令手動(dòng)部署示例應(yīng)用程序。首先,部署前端在線商店組件:
接下來,部署示例后端推薦組件的當(dāng)前或默認(rèn)版本。我們將所需的標(biāo)簽添加到部署對(duì)象中,因?yàn)槲覀兿M诖私M件上運(yùn)行 A/B/n 測(cè)試。
最后,部署 Iter8 服務(wù)(如果尚未部署):
運(yùn)行 A/B/n 測(cè)試
我們現(xiàn)在準(zhǔn)備運(yùn)行 A/B 測(cè)試,比較后端推薦組件的當(dāng)前部署的默認(rèn)版本和新的候選版本。運(yùn)行測(cè)試有兩個(gè)步驟。第一步是部署組件的一個(gè)或多個(gè)候選版本。使用我們的示例應(yīng)用程序,我們展示了部署后端推薦引擎的候選版本所需的步驟。待候選版本完全部署后,Iter8 Service會(huì)開始將分配給它的track label發(fā)送給前端服務(wù)。作為響應(yīng),前端商店將開始向新版本的推薦引擎發(fā)送請(qǐng)求。第二步是啟動(dòng) Iter8 實(shí)驗(yàn)來評(píng)估收集到的指標(biāo)。我們展示了一個(gè)多循環(huán)實(shí)驗(yàn)——一個(gè)定期執(zhí)行直到被刪除的實(shí)驗(yàn)。在每次執(zhí)行時(shí),
部署候選版本
可以以任何方式部署候選版本——手動(dòng)(如我們所示)或使用 CI 工作流。如上所述,必須將所需標(biāo)簽添加到至少一個(gè) Kubernetes 資源對(duì)象。
在我們的示例應(yīng)用程序中部署后端推薦服務(wù)的候選版本:
在部署候選版本時(shí),必須注意確保候選版本在前端向其發(fā)送任何請(qǐng)求之前已完全初始化。這可以通過僅在候選版本完全初始化后設(shè)置 iter8-tools/abn 標(biāo)簽來確保。一旦初始化,Iter8 A/B/n 服務(wù)將開始提供響應(yīng)Lookup()請(qǐng)求的版本。
這些步驟如下圖所示。最初,僅部署默認(rèn)版本 v1 并接收來自前端的所有流量。
當(dāng)部署候選版本 v2 時(shí),前端繼續(xù)將所有請(qǐng)求發(fā)送到默認(rèn)版本。
一旦候選版本準(zhǔn)備好接收流量,例如,當(dāng) pod 為 時(shí)Ready,設(shè)置標(biāo)簽iter8.tools/abn。這會(huì)觸發(fā) Iter8 服務(wù)開始向前端推薦它,而前端又開始向兩個(gè)版本發(fā)送請(qǐng)求。在我們的示例應(yīng)用程序中:kubectl label deployment backend-candidate iter8.tools/abn=true
實(shí)際上,測(cè)試取決于應(yīng)用到前端服務(wù)的用戶負(fù)載。在本教程中,我們使用將請(qǐng)求發(fā)送到存儲(chǔ)端點(diǎn)的腳本來應(yīng)用負(fù)載,/getRecommendation并/buy.使用將本地請(qǐng)求轉(zhuǎn)發(fā)到集群:kubectl port-forward svc/frontend 8090:8090
并為不同的用戶生成負(fù)載;例如,對(duì)于用戶foo和foobar:
啟動(dòng) Iter8 實(shí)驗(yàn)
啟動(dòng) Iter8 實(shí)驗(yàn)以定期讀取通過 編寫的業(yè)務(wù)指標(biāo)WriteMetric()。可以使用預(yù)定義的 abnmetrics 任務(wù):
此命令啟動(dòng) Iter8 實(shí)驗(yàn),該實(shí)驗(yàn)運(yùn)行預(yù)定義的 abnmetrics 任務(wù)以讀取在默認(rèn)命名空間中運(yùn)行的后端應(yīng)用程序組件的記錄指標(biāo)。使用 cronjob runner 表示實(shí)驗(yàn)將根據(jù) cronjobSchedule 定期運(yùn)行(在本例中為每分鐘一次)。實(shí)驗(yàn)結(jié)果將隨時(shí)間更新。
檢查實(shí)驗(yàn)結(jié)果并決定是否推廣候選版本。第一份報(bào)告將在實(shí)驗(yàn)任務(wù)第一次運(yùn)行后(大約一分鐘)可用。示例報(bào)告如下:
提升贏家
在推廣候選版本時(shí),必須注意確保沒有用戶流量意外發(fā)送到正在升級(jí)或刪除的版本。任何可用于執(zhí)行促銷的方法都應(yīng)包括以下步驟。還顯示了示例應(yīng)用程序的手動(dòng)步驟。
最初,默認(rèn)版本和候選版本都從前端接收請(qǐng)求。
首先,iter8.tools/abn從與默認(rèn)版本關(guān)聯(lián)的資源中取消設(shè)置標(biāo)簽。這會(huì)在轉(zhuǎn)換期間禁用到默認(rèn)版本的流量——Iter8 SDK 接口Lookup()將從其推薦后端列表中刪除默認(rèn)軌道:
接下來,使用新版本重新部署與默認(rèn)軌道關(guān)聯(lián)的對(duì)象。
更新的對(duì)象準(zhǔn)備就緒后,添加iter8.tools/abn指示它已準(zhǔn)備好接收流量的標(biāo)簽。
此時(shí),默認(rèn)和候選軌道標(biāo)簽都與相同的后端版本相關(guān)聯(lián)?,F(xiàn)在可以刪除候選版本。為此,取消設(shè)置iter8.tools/abn標(biāo)簽以終止到候選資源的流量:
最后,刪除候選資源。
最后的想法
我們探討了進(jìn)行 A/B/n 測(cè)試的一些挑戰(zhàn),尤其是應(yīng)用程序的后端服務(wù)。關(guān)鍵挑戰(zhàn)涉及前端組件計(jì)算業(yè)務(wù)指標(biāo)無法正確地將它們與有助于其計(jì)算的后端版本相關(guān)聯(lián)。Iter8 SDK 使前端能夠正確地建立這種關(guān)聯(lián)。它通過提供一個(gè)查找接口來實(shí)現(xiàn)這一點(diǎn),該接口允許前端服務(wù)識(shí)別后端服務(wù)的版本以在處理用戶請(qǐng)求時(shí)使用。這樣,它可以可靠地將業(yè)務(wù)指標(biāo)分配給后端版本。我們展示了使用 Iter8 SDK 修改前端服務(wù)和運(yùn)行 A/B/n 測(cè)試是多么容易。
只需要幾行額外的代碼就可以對(duì)前端進(jìn)行一次性更改。啟用候選版本進(jìn)行測(cè)試只需要添加一些標(biāo)簽。
試用本教程后,使用您自己的應(yīng)用程序進(jìn)行試用。