Facebook工程釋出技術的幕後故事

黃小非發表於2013-03-23

Facebook的總部位於美國加州的Menlo Park,這裡曾經是Sun公司的駐地。在其入口處,一個“贊”的標誌牌(“贊”就是一個豎大拇指的姿勢)赫然樹立。當我最近造訪Facebook園區時,一群年輕人正在這個標誌牌前,爭先恐後地用手機拍照留念。

多虧了大衛·芬奇的電影《社交網路》,全球數以百萬計的影迷都知曉了這麼一個瘋狂的故事,Facebook從一個大學宿舍裡建立的試驗專案,發展成了世界第二大網際網路站點。但是,就彷彿你喜歡跑車但不瞭解引擎技術一樣,很少有人知道,Facebook每天處理數以億計的使用者請求,實際上需要非常複雜的技術架構。這些精巧的技術架構,同Facebook的傳奇故事一樣引人入勝。

Facebook園區入口處

我最近才得到了這次難得的機會,得以造訪Facebook的總部,親身體驗一下那裡的風土人情。Facebook給了我獨家的採訪權,去探究他們在部署新功能和應用方面的幕後故事。我看到了第一手的技術資料,例如公司的新釋出設計方案:為公共主頁新增“時間線”特性。

當我穿過園區的前門,走入環繞主建築的街道時,我發現,這條街被命名為:“黑客之路”。就像Facebook的創始人Mark Zuckerberg當年在他寫給投資人“公開信”中提到的那樣,當時他正在為Facekbook融資以籌集啟動資金。他還把“黑客的方式”注入了公司的管理層和開發團隊。在我採訪Facebook的兩天時間裡,我充分意識到釋出工程技術的重要性。伴隨著網站規模的迅速增長,這項技術扮演了關鍵的角色,將“黑客方式”融入到應用當中。

Menlo Park園區佔地面積很大,各式建築佈滿其中;給我的感覺彷彿進入了一個小型的城市,而不是一個公司的園區。在這些建築裡,有趣的塗鴉式壁畫和幽默海報隨處可見。除了行政辦公區,Facebook的開發人員大多數都在開放式的環境下辦公。辦公桌分列整齊排列,工位沿著公共桌位對齊排開,員工與員工之間沒有阻隔物,便於交流。

每棟建築物內都有專門的會議室,在那裡,員工們可以討論問題而不影響他人工作。每棟建築的會議室都以完全不同的主題風格進行命名。例如,在建築物1中,會議室以Monty Python電影中的喜劇包袱進行命名。而另一個建築物中,會議室則會用美劇進行命名。當你步入第三棟建築,你會不禁一笑,這個會議室的名字叫做:“JavaScrpt語言精粹”(JavaScript:The Good Parts),這明顯是根據Doug Crockford那本影響深遠的技術書籍命名的。

我最終來到了釋出工程技術組所在的區域。就像其他的開發人員一樣,釋出工程技術組也是用開放式的工位佈局。但是他們的特色在於:把辦公區佈置成了而一個酒吧!

這個房間原本就在兩個廊柱之間有一部分牆體。當釋出工程組進駐以後,他們就把這部分空間變成了一個帶工作臺的酒吧,並稱之為“hotfix吧”,hotfix是關鍵的軟體補丁的意思。全組人員就在沿著酒吧放置一張桌子上辦公。

我就是在這個酒吧遇到了Chuck Rossi,釋出工程組的負責人。Rossi的工位距離吧檯最近,吧檯上的飲料對他來說觸手可及。他可是軟體行業的元老級人物,曾經在Google和IBM工作過。我與Rossi進行了一個下午愉快的交談,討論了他和他的團隊如何進行Facebook更新工作,以及這些日常工作的重要性。

Chuck Rossi, Facebook釋出工程組負責人,坐在Hotfix酒吧前

Facebook的BitTorrent部署系統

Facebook原始碼大多是用PHP程式語言編寫。PHP是一門快速開發語言,但是相比於底層語言和部分高階語言,它的執行速度是個缺陷。為了改進基於PHP的架構的擴充套件性,Facebook開發了一個特殊的優化器,“HipHop”。

HipHop能將PHP轉換為深度優化的C++程式碼,後者能夠編譯成執行效率極高的本地二進位制碼。Facebook於2010年將該專案以開源協議的形式釋出,隨後公司工程師報告,該專案將Facebook的CPU能耗降低了50%。

因為Facebook的整個程式碼庫都會被編譯為單個的可執行檔案,因此公司的部署過程會和傳統的PHP環境中的部署大不相同。Rossi告訴我,編譯後的二進位制檔案,也就是包含整個Facebook功能的應用,大約有1.5G的大小。每當Facebook更新了程式碼並生成了新的版本,那麼新編譯的二進位制程式碼就必須被傳送到公司的每一臺伺服器。

把1.5G的二進位制龐然大物傳送到公司無法計數的伺服器上,這是一個非凡的技術挑戰。在探索了許多解決方案後,Facebook決定開始使用BitTorrent(也就是傳說中的BT下載裡面的BT協議——譯者注),經典的P2P檔案共享協議。BitTorrent的特長,正是在不同的伺服器之間傳遞大容量的資料檔案。

Rossi介紹道,Facebook有自己的訂製BitTorrent追蹤器,這個追蹤器被用來讓Facebook基礎構架中的單個伺服器能夠從其他的伺服器獲取資料片段,只要他們處於同一機架或節點就可以。這能有效地縮短總的時耗。

進行一次完整Facebook更新平均需要30分鐘——15分鐘編譯二進位制程式碼,另15分鐘把二進位制可執行程式碼通過BitTorrent推送到Facebook伺服器上。

當然,二進位制程式碼僅僅是Facebook應用棧的一部分而已。還有許多外部資源需要引用Facebook的頁面,包括JavaScript, CSS和影象資源。這些檔案由內容分發網路(CDN)以分散式的形式儲存和管理,遍佈於不同地理位置的伺服器上。

Facebook通暢每個工作日進行一次小的更新,每週進行一次大的更新,大更新一版是在週二下午進行。釋出組負責管理這些更新,確保更新能夠成功生效。

日常釋出是Facebook開發哲學的重要組成部分。在公司早期的日子裡,開發人員採用快速增量迭代的軟體工程方法持續地對網站進行改進。這項敏捷技術在Facebook的進化過程中扮演了重要的角色,使其能夠快速前進。

當Facebook任命Rossi擔任釋出工程組的領軍人物時,他正負責調和快速開發模型和網站規模、複雜度快速增長之間的矛盾。要達到這一目的,就必須採用一些非常規的解決方案,例如BitTorrent部署系統。

在我與Rossi交談的時間裡,我發現,他解決Facebook部署難題的方法,就在於平衡實用性和準確性的程度。他一方面為部署的質量和健壯性都設立了很高的標準,另一方面卻著眼於尋求靈活、順應性強的解決方案。(譯註:除 HipHop 之外, Facebook 這個龐大系統背後還有著其他諸多軟體,詳情請參考2010年的一篇舊文《揭祕Facebook背後的那些軟體》。)

 

測試

在我們最近的一些文章裡,我提到了加速軟體釋出週期的挑戰和回報。其中主要的挑戰之一就是,如何在快速週期中保持軟體的高質量,因為快速的週期縮短了beta測試的時間,沒有足夠的測試可能造成質量的下降。

質量測試在Facebook也是一個挑戰,每天都帶來新的改變。為了幫助發現問題,員工只要從公司內網登陸Facebook,就會直接訪問尚未正式釋出的試驗版本,也就是基於最新的程式碼的版本。當員工們想從內網訪問當前正式版本的時候,他們必須使用另外的IP地址。

將測試版本設為內網的預設訪問站點,能夠讓產品的新特性在正式整合前更多地暴露。測試版本有一個內建的bug報告工具,它使得員工在遇到問題的時候能夠更方便地進行反饋。

Facebook也使用自動測試工具來避免功能退化以及發現一些低階問題。公司有兩套獨立的測試機制,一套進行一些常規測試,對程式碼進行檢查;另一套模仿使用者的互動式行為,確保站點的互動行為正常。

在進行全面的更新之前,新程式碼首先被推送到“A2”層——Facebook的少量公共伺服器。這個階段的測試把更新特性隨機暴露給Facebook的部分正式使用者,但只是所有使用者的一小部分而已。這種機制給了Facebook的工程師很好的途徑,去評估更新特性在正式產品環境中會有什麼樣的效果。(譯註:關於Facebook做測試,Quora上有個討論帖,FB工程師 Steven Grimm 給出了自己的回答:《Facebook是如何做自動化測試的》。)

Chuck Rossi和他的工作臺

 

準備工作

Facebook元件了自己的多人線上交談系統(IRC),用以進行內部交流。許多公司的工程師在工作時間都保持潛水狀態。根據Rossi所說,每個工作日平均約有700人線上。Facebook的工具工程師建立了IRC機器人,能以多種方式將IRC整合到Facebook的開發和部署工作流中。

當Rossi準備開始實施更新時,他在IRC上初始化了一個chekin過程。此時所有的開發者,不管是提交更新或者沒有提交更新,都會被通知,並要求迴應,是否做好了系統全面更新的準備。

當某個開發者在幾分鐘內沒有迴應時,Rossi就會給IRC機器人下達一個命令,讓它通過不同的通訊渠道去與這個開發者聯絡,包括email和簡訊的方式。就像Rossi跟我解釋的那樣,他通常希望在系統更新的時候,所有相關的開發人員都盡在他的掌握之中。

Facebook開發文化重要的一個方面是,開發人員對他們所開發的的程式碼在最終產品中的行為負全部的責任。這種哲學完全對映了DevOps運動的思想,也就是鼓勵打破軟體開發和軟體運維中間的障礙。

如果Facebook更新中的任何程式碼在最終產品中造成了問題,那麼負責開發這段程式碼的開發者就要立即行動起來,確保問題能夠儘快地解決。

釋出

Rossi在Facebook的辦公桌上放著一臺30英寸的Dell顯示器,一個蘋果Mac膝上型電腦,以及一個豎直顯示器。在我週二與他待在一起的時間裡,他的大部分工作都是通過瀏覽器和終端視窗(命令列介面)完成的。當他準備開始實施更新的時候,他在一個終端裡輸入了一行命令,然後整個過程就開始了。

我通過Facebook的基於Web的監控工具觀察了整個更新過程。web頁面上有一個大的進度條,顯示了公司伺服器更新的進度。隨著更新過程的進行,進度條不斷前進。在最左側的邊緣,一個很細的紅條標識出一小部分的系統更新失敗,新的程式沒有上傳。

Rossi說,在整個更新過程中,系統的一小部分更新失敗的情況很常見,這多半是由於硬體原因造成的。比如,伺服器也許因為儲存空間不夠,或者網路連線問題,致使無法通過BitTorrent傳送檔案,從而導致更新失敗。總的來說,更新失敗的伺服器的數量很少,幾乎不會造成什麼問題。

當軟體被成功部署到伺服器上後,Rossi描述了Facebook的構架是如何印象更新過程的。Facebook的設計理念是無狀態和分散式的,也就是說使用者的會話不會被繫結到任何一臺特定的伺服器上。任何一個頁面請求都有可能被Facebook架構中的任何一臺伺服器處理。

這種方法提供了很強的彈性。當Facebook實施更新時,完全不用擔心對使用者會話的序列化和遷移問題。在伺服器接收完更新資料後,部署系統自動重啟Facebook伺服器上的可執行程式。在整個構架更新期間,無論是已經更新完成的伺服器,還是仍在接收更新資料的伺服器,都可以繼續處理使用者請求。

Facebook在更新執行期間,網站仍然滿負荷運作。一個常規的Facebook部署過程並不會要求整個網站關閉維護,也不會對網站造成什麼其他的干擾。Rossi說,採用非中斷式的維護方式,是整個Facebook釋出工程策略的重要特點。他甚至把這點看做是衡量Web軟體工程質量的重要標誌。

 

更新後檢驗

在更新完成以後,Rossi檢查了系統的各個方面,確保剛才的改變不會對系統造成不良的影響。他的團隊通過一套複雜的分析工具隨時監控Facebook的執行狀態。這套工具的主儀表皮膚上包含了大量圖示,以顯示系統在流量、資源消耗、單個產品錯誤率,以及許多其他方面因素的變化。

通過觀察這些關鍵資料的起伏變動,能夠幫助Facebook識別系統問題的所在。在發生問題的時候,使用這些資料與歷史資料進行對比,能夠簡化對問題起因的判斷。釋出工程組以及Facebook的其他工程師在更新剛剛完成的時候,對這些資料尤其地關注,以確認系統不存在異常現象。

如果有問題被檢測到,例如系統的某個部分的錯誤率超出了預期,公司的工程師就開始深挖錯誤日誌,檢視到底發生了什麼事情。Facebook內部有專門的日誌分析工具,用來檢視程式碼變化與錯誤資訊之間的關聯。

Facebook內部監控工具可以監控許多資料來源,甚至包括監控Twitter裡對Facebook的評論。這些監控資訊被顯示在一個統計圖裡,圖中包含兩條走勢線,分別代表對Facebook積極和消極的評論。這非常有用,因為當使用者在一個社交網站裡遇到問題的時候,他往往會去另一個社交網站抱怨他遇到的問題。

釋出工程組的成員在Hotfix吧小喝幾杯,慶祝更新成功

 

我在Facebook採訪時觀察到的系統更新過程進行得很順利;更新後沒有產生技術問題或者bug。圖示顯示,只有一個系統模組的日誌資訊有點小問題,但是經過Rossi的小組追根溯源後,發現這只是一個不重要的問題,因此也就作罷了。

 

只有失敗者才會回退

儘管我在這兒的時候,並沒有發生什麼需要救火的緊急情況,但是Rossi為了滿足我的好奇心,還是想我描述了當更新過程不那麼順利時,Facebook是如何因對的。如果一個嚴重的bug在更新後被發現,釋出工程組全體成員就會與相應的開發人員一起儘快解決問題。當問題解決後,Rossi的小組會釋出一個新的版本,並再次實施更新。

當我問及他,如果更新後系統bug難以修復,是否會將其回退到當前的版本時。他斬釘截鐵地回答我:“只有失敗者才會回退!”

他繼續向我們解釋,實際上,系統回退功能是有的,但是它僅僅在萬不得已的時候才會使用。伺服器會自動儲存Facebook上一個版本的二進位制程式碼,以備不時之需。

他還比喻說,把Facebook回退到上一個版本,就好像給一列火車拉緊急制動閘一樣。人們不希望這種情況發生,實際上也確實很少發生。在他來到Facebook的幾年時間裡,回退功能只用過幾次而已。

Facebook的測試演習和工程師文化能夠有效地放置bug被更新到終端產品程式碼中。如果一個開發者的程式碼出現了問題,並嚴重到需做部署後修改,那麼這件事情就會被記錄下來,並影響到Facebook對這個開發者的績效考核結果。

公司的內部工具有一個Facebook激勵機制,Rossi用它來進行評分。Facebook開發人員都有一個“因果報應”評分,該評分可由程式碼審查系統進行跟蹤。在一個基於Web的儀表板工具中,Rossi可以通過點選某個開發者姓名旁邊的“頂”或者“踩”的按鈕,來增減這個開發者的“因果報應”評分。

Rossi使用的“頂”圖示其實就是Facebook裡一般使用者使用的“頂”圖示。“踩”圖示和頂圖示是一個圖示,只是倒過來了而已。當Rossi向我展示這些圖示的時候,他開玩笑說,他是全世界唯一一個可以在Facebook裡使用“踩”按鈕的人。

“因果報應”得分幫助Facebook辨別哪些員工更加努力,但是這個評分玩玩在程式碼評審階段更加有效。當Rossi看到一個得分很低的開發這提出一個程式碼合併申請的時候,他就會知道,從這個員工那裡接受程式碼可能會有很大的風險。

隨著時間的推移,得分低的員工可以通過他們的良好表現來重新贏得分數,不過有的員工喜歡走走後門,通過給Rossi“行賄”來增加他的好感。酒和紙杯蛋糕是Rossi喜歡的“贖罪物”;釋出工程組有數量可觀的酒水供應,有些就來自於那些尋求恢復自己“因果報應”分數的開發者。

Facebook釋出工程部門的Hotfix吧

未來

我同Rossi談到了他的願景:Facebook的部署策略將影響到這個公司的技術架構的進化方向。他說未來的開發技術將讓他的小組能夠戲劇性地加速部署更新的過程,把所有的構架和部署時間大大縮短,遠低於目前的30分鐘。

當前正在進行的開發專案之一,就是一個企圖代替HipHop優化器的專案。Facebook的開發者正在建立他們自己的位元組碼格式和執行時環境,並稱其為HipHop虛擬機器,用以支援下一代的Facebook平臺。當這個專案結束時,公司能夠將PHP原始碼編譯成位元組碼,並在這個虛擬機器上執行。

遷移到受控程式碼模型,就彷彿Java或者.NET一樣,能夠給Facebook帶來全面的靈活性。除了提供許多其他的優點,Rossi解釋說,這將顯著地影響部署過程。屆時公司將不必再將1.5G的二進位制程式碼部署到所有的伺服器,而是僅僅需要推送一些小的位元組碼變數,來表示那些部分發生了改變。Facebook甚至能在程式執行的時候將這些更新位元組碼拼接起來,而不需要重新啟動。

當僅僅需要幾分鐘就能完成部署的情況變成可能,而不再需要大規模的部署過程的時候,也就是Facebook摒棄其傳統的更新時間表,全面進入增量部署的時候,就彷彿邊開發邊部署一樣。採用這種方式,能夠給公司的開發人員帶來更加敏捷的開發模式。

在週二的更新過程完成以後,Rossi和他的小組分析了系統,確保更新沒有給系統造成問題。然後,他們就在hotfix酒吧喝上幾杯,表示慶祝。

當一天結束,我離開Facebook園區時,我再次信步經過了“黑客之路”標誌牌,我沉思著,釋出工程組在Facebook中扮演了重要的角色,把這個軟體推向大眾,但是他們的工作又是如此的不為人所知,就彷彿是透明的一樣。

Facebook系統向時間線檔案佈局遷移,將增加社交網路平臺在使用者經歷分享和使用者個人敘事記錄方面的功能。提供這些功能的基礎技術構架本身就有許多經歷和故事,這些都是Facebook獨特的開發者文化的象徵。

打賞支援我翻譯更多好文章,謝謝!

打賞譯者

打賞支援我翻譯更多好文章,謝謝!

Facebook工程釋出技術的幕後故事

相關文章