字節跳動億級DAU客戶端發佈最佳實踐

本文是字節跳動發佈工程團隊的高磊講師在2021 GOPS 全球運維大會中「字節跳動億級DAU客戶端發佈最佳實踐」的分享全文。

首先做一下自我介紹:我是字節跳動發佈工程團隊的工程師高磊。從事軟件開發工作有10多年的時間,在傳統軟件公司還有一些創業公司都做過,最近大概6,7年時間基本專注在DevOps方向,也積累了一些自己的理解和經驗。

file

今天我分享的主題是【字節跳動億級DAU客戶端發佈最佳實踐】,通過今天的分享,希望大家可以瞭解字節在客戶端發佈方面做的一些實踐。本次我將從四個方面來進行分享,分別是:1.移動端發佈的特點難點,2.字節移動發佈體系的介紹,3.移動發佈的實踐總結,4.未來的展望。

1. 移動發佈特點&難點

我們先從移動發佈特點難點開始。 發佈這個概念應該屬於CD(持續部署)的範疇,也就是Devops的一部分。平時我們接觸的比較多的是服務端Devops,今天的分享主題看似會偏客戶端一些,但大部分的內容和理念應該是相通的;本來我之前想的是,如果能到場的話,我會做一個現場的調研,看下到場的做客戶端的同學比例;不過既然現在沒有這個機會,我先簡單介紹服務端和客戶端的發佈流程的區別:

file

其實這裏的內容並不複雜,但作爲我們後續內容的一個背景鋪墊還是有必要講的。 從構建打包到包出來的這個階段,兩者應該區別不大,無非底層依賴的打包工具略有差異,最本質的區別在於出包後的流程;服務端的更新流程,是把打出來的二進制包發到我們自己的服務器上,所以他的整個過程是可控的,一方面你可以隨意更新新版本;另一方面呢,萬一上線的版本有問題,也能做到一鍵回滾;但客戶端是不行的,我們的包打出來以後,一般我們會把新版的包放到服務器上,如果對於正式包,我們會上傳到商店來託管,但用戶什麼時候過來更新,我們是不知道的;如果這個版本有問題,那你也沒有辦法很快會退到上一個版本,你還得規規矩矩的再走一遍這個發版的流程,這個止損的成本是比較高的。 這就是兩者最大的區別:客戶端升級依賴終端,不完全取決於平臺,這個特性也決定了兩者在版本發佈全週期其他方面的不同。

file

具體來說,首先是部署介質,這個上面提到了,一個是可控的服務器環境;另外一個是複雜變化的終端設備,操作系統有Android/iOS, Android有很多不同的廠商,比如小米,華爲, vivo等等; 第二,服務端的版本概念比較弱,一般來說,我們不用版本號去定位某次服務端的發佈,但客戶端不同,我們的交流語言就是某某版本,版本號是客戶端發佈裏面非常重要的一個信息; 第三個差異點是發版週期,服務端的發版一般沒有明確約定,有新功能的話可以每天都更新,一天發多次都是常態;這個也符合Devops持續交付的理念; 移動端,一般準備週期比較長,目前主流的節奏可能是1到2週一個大版本,也有一些不常更新的應用甚至可以一個月才發一次版; 第四個差異點是參與人羣,服務端一般情況下只要測試階段通過,到了發佈階段基本上就是RD來主導,但客戶端因爲版本比較珍惜,一個是一個,所以在發版階段設計了很長的鏈路,每個階段都會配不同的角色去完成某項工作,現在字節不少團隊還有QABM這個角色,就是QA裏面專門負責發版管理的同學;整體分工可以說越來越精細和複雜了; 第五點是止損效率,這個之前已經提到過了,不能很快的回滾止損這個“硬傷”可以說是造成客戶端和服務端發版差異主要的原因; 最後一點是多版本並行,服務端顯然常態下只會保留一個最新的版本,在上線或者灰度階段會短暫的多版本並行一段時間,但是客戶端歷史上累積的這些版本會永久的保留下來,不再變更; 那麼說了這麼多,相信大家對兩者差異應該有了一個更清晰的認識,大家可能都會想,“客戶端發版”真麻煩,這個是對的,而且因爲麻煩,一旦發出去以後,就要承擔一些可能的風險。

file

下面我給大家看幾個典型線上事故的例子,大家可以看下這張PPT,這是我們線上曾經出現過的真實事故,有因爲錯誤下發64位機型安裝包到32位機型導致升級失敗的,有爲安裝包配置錯誤下載鏈接導致安裝失敗的,還有使用不恰當物料導致應用商店拒絕上架的...真是什麼事故都有。不難看出,移動場景下發布面臨的風險很多,有安全的問題,有數據的問題,有測試的問題,稍有遺漏就會給公司造成很大的損失,或者是財務損失,或者是用戶流失,這些都是我們在設計發佈系統時要解決的問題。

file

那麼看了以上這些事故,再結合客戶端發版的特點,就會引出幾個問題:

  1. 如何打造一個高效的發佈流水線?
  2. 如何保證流程的安全性?
  3. 如何達到更好更快的放量效果?
  4. 如何保證發佈數據的可靠性?

2. 字節跳動移動發佈體系

這些問題在字節是怎麼解決的?接下來我將爲大家介紹字節的移動發佈體系,並嘗試回答以上四個問題。

file

首先先介紹下字節的移動發佈中臺發展史,大致可以分爲幾個階段: 第一階段是2017年以前,因爲業務發展比較快,公司級的中臺發展相對滯後,更多的是業務從自身發版的需求出發,簡單搭建了一些小的jenkins集羣,用來串聯各種打包和測試任務;那這個狀態持續了一段時間後,問題就出現了,大量分散的jenkins集羣維護起來很麻煩,業務需要自己去維護這樣一個平臺,投入太大,很多任務編排都重複建設,沒有很好的共享資源,隨着業務越來越多,發版問題越來越突出,所以一箇中臺的出現就變得迫在眉睫了; 於是我們進入了第二階段,從2017開始,第一代發佈平臺1.0就出現了,我們內部叫“Rocket”,字面意思是火箭,顯然它的寓意是快,這個階段相之前有了一些進步,最大的改變是通過jenkins提供了平臺級的流水線,不同的團隊可以自己定製,同時也有了專門的編譯團隊來解決打包的環境和部署問題,情況比之前要好了很多,jenkins出了問題有人維護,有了流水線,也可以重複執行。 這個過程大概持續到2019,一些隱患又暴露出來了,首先是jenkins打包集羣,受限於jenkins本身的單master架構問題,海量的jenkins任務對於構建團隊而言同樣苦不堪言,多的時候一天發生10多次重啓機器也不罕見,另外呢,這一期的流水線也有一些問題,過度追求靈活的配置,那會的原子能力其實不能真正算原子能力,更像是一些腳本的零散組合,缺乏系統的協議約定,帶來的問題就是用戶的配置成本很高,因爲需要關心的細節太多了,平臺和平臺之間的能力聯動不透明,導致各工具之間比較割裂,用戶配置流水線成本很高;同時在安全等方面的流程設計不合理,導致一些卡點形同虛設;所以從2019年開始,我們開始推進第二代發佈系統的建設,這一期我們做了這樣幾個事情:

  • 首先在構建層面,我們拋棄了基於jenkins的二次開發模式,完全採用自研的分佈式調度集羣,實現多主多活,自動恢復,支持任務優先級調度等方案,整個可用性也有了大幅提升;
  • 在流水線設計方面,我們放棄了之前完全腳本化的方式,提煉出了很多通用的系統原子能力,大幅降低了用戶的使用成本,交互體驗有了大幅改善;
  • 除此之外,我們在安全方面做了一些建設,整體從需求開始就有滲透,目前來看基本覆蓋了CI/CD全流程;
  • 在數據這塊,我們確定了以製品庫作爲發佈的數據基座,從之前關注版本的宏觀視角落到關注產物的微觀視角,整個平臺的架構更加清晰;
  • 最後,我們圍繞灰度放量這個環節進行了多種方式的探索,目標是提高我們的問題反饋率,儘可能把一些潛在的嚴重問題前置暴露;

file

大家可以看看這個圖,這是我們當前版本的一個主要架構體系; 首先平臺目前較好支持了頭條,抖音,西瓜,小說,飛書等業務的發版需求,支持的終端場景也從Android, iOS 應用擴展到,Mac,Windows等更多場景;在此過程中,我們也沉澱出了很多的平臺和能力,從需求規範到研發打包,測試,發佈,以及上線後的監控和反饋;我們的最終目標就是打造一個一站式的通用移動研發平臺;

file

那回過頭來,我們再看下之前提到的幾個問題,我會分4個小節,分別從流水線,安全,測評,製品庫這幾個方向介紹平臺的一些特點,一起來看下字節對這些痛點給出了怎樣的解決方案。

file

首先,針對如何構造一個高效流水線這個問題,我們搭建了字節發佈流水線。流水線對於Devops來說基本算是一個既定的事實標準了,我們也不例外,在字節的場景下,他主要解決了兩個問題: 第一:多場景自定義任務編排; 曾經我們嘗試想從一個最佳實踐出發滿足所有業務的場景,後來發現這條路是行不通的,因爲字節不同業務的發展階段不同,成熟的業務比如頭條,抖音,他的團隊分工和成熟度和一些新興業務肯定不同,在權限控制,測試流程,准入準出規範上差別很大,舉個例子,一些小業務在上線之初,可能都沒有灰度階段,你如果非得拿一套發佈的全家桶模板往給他套上,逼着他必須走一個複雜的灰度流程,對於業務方來說其實是一個拖累,而不是提效; 第二:解構平臺的複雜度,便於度量; 整個移動devops的流程很長,如果不依賴流水線,那你需要自己單獨管理這十幾個甚至幾十個原子能力的相互關係;流水線的設計可以讓這些原子能力用一個編排的方式管理起來;對於平臺來說,我們也更容易去度量整個平臺的質量瓶頸,是出在哪一個具體的能力上,比如某個業務的發版速度一直很慢,我們通過分析流水線上每個原子的執行時間,就很容易的定位到是具體哪個階段有問題,是自動化測試效率低,還是灰度階段放量慢,都可以用自動化的方式來完成,甚至可以做成報表,給出一些定量的分析; 從我們自己的實踐來看呢,我覺得其中的關鍵點在於:原子能力的粒度;

  • 粒度過大,那內部結構就過於複雜,不利於直接深入到本質,這說明還需要往下拆解;
  • 粒度過小,沒有太多度量的價值,那他就可以合併到其他原子能力裏面; 這裏總結了我們的兩個原則,大家可以參考下:
  1. 有獨立的功能定位:
  • 這裏面獨立指的是執行獨立,權限獨立,數據獨立這三個維度;
  • 執行獨立指的是原子能力在給定了基本的依賴數據後就可以獨立運行,可以實現特定的功能;
  • 權限獨立指的是原子能力需要具備獨立的權限,不受其他原子能力的直接控制;
  • 數據獨立指的是原子能力應該享有獨立的數據通路,可以直接和流水線框架做一些數據的溝通;
  1. 可獨立度量改進:整個發版流水線的度量最終會落到單個原子能力上,如果你的原子能力本身無法被某個指標度量,那麼說明這是個無效的原子能力,他不具備獨立存在的條件,需要通過兼併或者拆解來達到度量的要求;

file

流水線問題講完了,我們再來看看如何保證發佈安全。前幾年大家會把安全問題當做一個不得已而爲之的事情,也就是安全問題是徹底暴露了纔想去解決的事情,現在呢,業界逐步形成了一些共識,就是安全問題不應該作爲一個救火隊員,或者作爲兜底手段存在,而應該作爲一個devops必需的參與者,滲透到devops的全流程;那在字節的場景下,我們從前兩年開始也逐步把安全的理念貫徹到整體移動發佈的全流程,從需求階段開始,我們就開始做相關的安全合規評估,在CI和CD階段,我們也會分別做靜態以及動態掃描,在最終release到商店前,我們還會基於我們累計的一些案例庫做一個審覈,避免觸碰一些安全方面的紅線。 平臺能力剛上線的時候,我們確實能發現很多的安全漏洞,有網絡方面的問題,有隱私合規的問題,看上去效果不錯,但是發現漏洞僅僅是起點,更重要的是我們要去消費漏洞,去修復問題;從平臺的角度看,一旦發生了高危漏洞,應該禁止業務方發版; 但從業務方的角度呢,版本的按時上線纔是他們最關心的,最後的結果就是平臺臨時給開綠燈(可能是hardcode),然後雙方拉老大開羣討論,最後定一個限期整改的方案出來。 這種事情多了以後,所謂“安全很重要”就變成了一句口號,所以我們需要一個機制去解決這個問題。這個機制呢,不是技術層面的,更重要的是在公司內部形成自上而下的共識。 一般來說,這裏面有三方參與的,安全團隊,平臺團隊和業務團隊;我們需要明確每個團隊在安全問題當中的定位:

  • 首先安全團隊的職責是負責對安全問題定級,提供安全問題的整改方案,並且輔助落地;比如在我們現在的平臺上,安全團隊就提供了黑盒和白盒的掃描能力,以及相應的規則庫;
  • 其次是平臺團隊,平臺團隊對系統的流程負責,平臺不能不管,也不能管的太死,簡單粗暴的規則“一刀切”肯定是不行的,更合理的做法是提供靈活的卡點能力和配置能力,業務可以根據自己的實際情況配置
  • 卡口級別;在具體的問題上,我們採取的還是“增量問題動態修復,存量問題限期整改”這樣的原則;
  • 最後是業務團隊,需要提高對安全問題的重視和反饋,積極配合整改意見的實施,分階段按優先級來推動安全問題的整改落實;

file

接下來我們想解決的痛點是:如何提高放量的效果? 在一開始我們介紹服務端和客戶端差異的時候提到了,放量這個階段的差異是移動端和服務端最顯著的區別,我們希望能通過正式發版前的灰度環節發現更多的問題;顯然我們安裝的新版人越多,反饋的問題越多,那麼我們的灰度效果也越好;於是我們嘗試了兩個思路: 第一個思路是對內: 我們考慮到公司內部有好幾萬人,裏面有RD,PM,QA,他們的專業和對故障的敏感程度是遠超過普通用戶的,如果可以很好的利用這部分資源,我們相當於有一個幾萬人的後備資源池,這個是非常厲害的;所以我們在18年開始在公司內部做了這樣的嘗試,上線了一款小程序,叫“字節內測”,我們自己的運營團隊會定期和業務方做一些合作,吸引他們在我們的平臺設置一些活動,引導大家去下載他們的新版,同時反饋試用過程中的問題,並且給予一定的激勵;那從目前來看呢,我們的ROI還是很正向的;每週大概參與到活動的人數在7000人以上,平均反饋的問題數量都在幾十個上下,其中P0到P2的問題能佔到四分之一以上,如果嚴格去計算的話,我們爲此付出的成本,主要是運營人力和激勵,還是明顯少於這些問題外泄到線上後帶來的損失;所以呢,內測活動還是很划得來的;但話說回來,這個對我們內部運營的要求是比較高的,我們需要持續進行一些運營的引導,降低用戶參與的門檻,保證活動-反饋這個通路的暢通。

file

我們嘗試的第二個思路是對外: 公司用戶雖然不少,但相對於外部的幾億用戶而言,還是很小的一部分,所以我們主要精力還是要這些數以億級的普通用戶上,他們纔是我們要挖掘的重點;問題就變成了我們怎樣在這幾億用戶裏通過算法精準的去找到這樣一些目標人羣。

file

要使用模型算法,那前提得準備數據;這裏面能利用的數據維度還是非常多的: 首先是用戶app信息:用戶使用app的習慣,瀏覽內容偏好,活躍時間段,老用戶還是新用戶等等; 其次是用戶基礎信息:性別,年齡,城市等等這些也可以輔助我們做一些判斷; 除了用戶相關的這些信息外,我們也可以結合版本本身的一些屬性,比如我這次上線的是一個直播相關的功能,那我應該優先去覆蓋平時玩直播比較活躍的這部分用戶;這樣的話,可以實現一個版本信息和用戶的深度雙向匹配;提高我們算法的準確性;

file

那麼在實際落地階段,我們還會根據不同業務的情況來對模型做針對性的優化;比如對於頭條,抖音這種億級APP而言,我們可以拿到足夠的數據來訓練個性化的模型;但對於一些小業務而言,因爲數據規模達不到可以單獨訓練的量級,所以我們會提供一套通用的模型。 總而言之,目標還是非常明確的,就是提供CTR和CVR這兩個數據的轉化率。 這些就是我們在放量這個事情上做的一些嘗試工作,目前我們也取得了一定成績,通過我們的算法模型比手動盲選平均有10個點左右的提升,但這個事情的天花板很高,我們後面的提升空間依然很大,需要繼續努力;

file

再講最後一個痛點:如何保證發佈數據的有效性? 大家應該還記得我們分享開始時提到的因爲鏈接配置出錯導致的線上事故,根本原因在於我們的升級體系沒有一個可信的數據源。 爲什麼我們不能直接用鏈接呢,因爲鏈接僅僅代表了這個產物的獲取方式,並不是唯一代表這個升級包,如果這個鏈接被篡改或者覆蓋,那麼意味着你的發佈就會出錯,而製品庫的存在就可以避免這一點,他保證給到下游使用的數據是可信的,是經過完整測試的。 所以在我看來,製品庫他是作爲整個devops的核心數據基座存在;如果說CI(持續集成)是對代碼負責;那麼CD(持續發佈)就是對製品負責。 發佈過程中所有在出包後的數據,基本上可以認爲都在針對製品來做的,不管是安全檢測,還是功能測試,或者是用戶故事,都代表這個製品存在的某些特性;如果我們發現某個製品有問題,我們可以把他放到黑名單裏面,這樣就不會影響後續的放量。 有一個案例可以分享下:我們內部有一個直播中臺團隊,因爲他們是跨業務部門,頭條/抖音/西瓜等多個業務都要用到他們的直播插件;之前在製品庫出現之前,他們的做法是用飛書在線文檔來存儲這個包,然後還會非常詳細標記這個包的狀態,誰,什麼時間,什麼原因,備註信息是什麼,裏面有什麼功能?這個過程是非常繁瑣和低效的,那在製品庫出現之後,我們可以把整個出包前的配置信息和之後經歷的事件動作全部集中在一處,這樣用戶就可以很方便的根據各種標籤條件去找到他需要的包,同時我們還支持訂閱模式,比如我只關注正式類型的包,那我可以訂閱下正式包這個標籤,當出現此類包的時候,就會主動通知到你;當然前提是你得有這個產品的訪問權限。 那麼現在,我們可以回答這個問題了,發佈數據的有效性是通過我們對製品庫的質量品控來保證的,他作爲我們平臺的一個數據基座,需要做到和流程解耦,保持一定的獨立性,這樣才能適配各種複雜的場景。

3. 字節跳動移動發佈的實踐總結

那第三塊我們整體介紹一下字節在發佈體系裏面的一些實踐和總結。

file

先看這組數據:我們現在大概每週會有 10 萬次的構建,每週超過 700 次的灰度,每週超過千萬量級的灰度實際的人羣。

file

我也總結出了幾點關於迭代的經驗,大家可以參考一下: 首先是最佳實踐的問題,我們整個平臺的迭代史都是以一個典型業務作爲的一個模板進行持續優化的,不追求一步到位,持續迭代是最好的狀態; 第二是珍惜事故,事故都是很寶貴的經驗,所以我們對於每次事故就儘量要做到極致,我們要做非常完善的這個 case study,也就是“5W” 原則;這個地方需要提一下,作爲平臺方千萬不要去甩鍋,業務方沒有遵守規範,那可以問下自己是不是給了業務方犯錯的空間了?我是不是給到足夠的引導跟支持了?我能不能讓他沒有犯錯的空間,我能不能杜絕一切犯錯的可能性? 第三個是需求,因爲需求是做不完的,那怎麼辦呢?我覺得作爲平臺方,心態要開放一些,我們可以做通用的需求,平臺可以儘量做到閉環;但是對一些個性化的需求,不要去強求,不把所有東西都攬下;我們可以制定規則,給其他人蔘與的機會,一起合作共建,把生態做大。 最後一點是平臺價值是需要被度量的,還是老生常談的那句話,不能度量的不能被改進;你如果想改進,就必須可以被度量。

file

其次分享一下移動發佈版的發展趨勢,這個趨勢是我們內部這幾年做平臺感知到的一些變化。 首先是版本發佈逐步出現高頻化的趨勢;從之前的單月到雙週,而現在主流的業務可能是一個單週的節奏在迭代,將來可能用不了多久,可能會呈現半周/天級別的演進,這個趨勢還是比較明顯的; 第二是安全越來越受到重視;這應該是一個常態了,上面其實講的比較多了,這裏不再贅述; 第三是精準測試場景;傳統的都是基於已經寫好的用例,這些用例最大的問題在於後期沒有人維護;當UI只要有一點變化,測試用例就基本上不可用,必須持續的對這個用例進行更新,這樣後期的維護成本是很高的。現在基於 AI 技術的精準測試,後面可能成爲一種主流,我們不需要去維護大規模的case ,它會自動根據我當前的這種場景去實時生成測試場景,我們內部也在做這方面的一些嘗試。 最後一點是持續灰度的理念;隨着版本的日益高頻這個是必然的,也就是灰度版本和正式版本的界限會越來越模糊;你不知道哪天是在灰度,明天就正式了,過兩天的又進入下一個灰度。持續灰度這個理念也是我目前感知到的一個點。

file

從平臺視角來看,還有幾個點可以稍微提一下;速度、效率、安全、成本。具體的每一點大家可以參考一下,我就不再細講。 我稍微提一下降本這一點:爲什麼我們有降本呢?因爲我們要存儲大量的包,所以對存儲是有一定的要求的。 帶寬這一塊可能是應用場景下比較明顯的一個特點,因爲如果你是一款擁有億級用戶APP的話,隨着迭代週期的縮短,包的更新頻率越來越高,那由此產生的帶寬成本的費用也是比較高。每年的花在 CD 成本上的這個錢估計可能會上億。錢其實還是挺貴的,我們還是想辦法降一下成本。具體怎麼降呢?其實有很多方法的,但是不是今天的主題,所以先不一一展開了。

file

最後做一下總結:今天我們講了很多的點,有流水線、安全能力還有放量。仔細去看這些點,它們其實都在追求 balance :比如說流水線,在原子能力上太大或太小都不行,要追求它的 balance;安全和業務的 ROI 的中間的取捨,我們也要做 balance;放量這一塊,我們要在速度、效果、用戶體驗之間追求balance。 所以總結起來,整個發佈平臺的迭代過程,我們都在不斷追求 balance 。在特定的時間段內,想要追求平臺的最大業務收益,就必須在某一方面進行取捨,不能什麼都想要。

file

在這裏分享一下我們平臺的未來的發展方向: 第一,我們會對發佈概念做一些延展。從目前的小發布體系,逐步的把它向大發布體系去引進。那何謂大發布體系呢?除了現在正常的升級包、熱修包以外,我們可能還會把配置資源或者靜態資源給也納入整個的發佈體系裏面來,形成面向業務統一的一個大體系。 第二,我們會持續優化放量,對放量做一些算法模型化的優化,也嘗試引入更多的一些數據維度增加它的豐富性。 第三,我們會更加精細的度量,把它形成消費的閉環,而不僅僅是去搭一個數據看板,數據很好看但並沒什麼用;最終還是要去消費、去改進。

最後介紹一下火山引擎應用開發套件MARS,今天分享中所提到的能力未來會通過MARS開放給大家,感興趣的朋友可以掃描右下角的二維碼,申請免費試。如果有私有化部署的需求,可以直接在公衆號留言,我們會有專人去提供幫助。 今天我的分享內容就全部結束了,期待將來與大家能有更多的交流,謝謝大家。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章