Facebook工程發布技術的幕后故事
英文原文:Exclusive: a behind-the-scenes look at Facebook release engineering
Facebook 的總部位于美國加州的 Menlo Park,這里曾經是 Sun 公司的駐地。在其入口處,一個“贊”的標志牌(“贊”就是一個豎大拇指的姿勢)赫然樹立。當我最近造訪 Facebook 園區時,一群年輕人正在這個標志牌前,爭先恐后地用手機拍照留念。
多虧了大衛·芬奇的電影《社交網絡》,全球數以百萬計的影迷都知曉了這么一個瘋狂的故事,Facebook 從一個大學宿舍里創建的試驗項目,發展成了世界第二大互聯網站點。但是,就仿佛你喜歡跑車但不了解引擎技術一樣,很少有人知道,Facebook 每天處理數以億計的用戶請求,實際上需要非常復雜的技術架構。這些精巧的技術架構,同 Facebook 的傳奇故事一樣引人入勝。
Facebook 園區入口處
我最近才得到了這次難得的機會,得以造訪 Facebook 的總部,親身體驗一下那里的風土人情。Facebook 給了我獨家的采訪權,去探究他們在部署新功能和應用方面的幕后故事。我看到了第一手的技術資料,例如公司的新發布設計方案:為公共主頁添加“時間線”特性。
當我穿過園區的前門,走入環繞主建筑的街道時,我發現,這條街被命名為:“黑客之路”。就像 Facebook 的創始人 Mark Zuckerberg 當年在他寫給投資人“公開信”中提到的那樣,當時他正在為 Facekbook 融資以籌集啟動資金。他還把“黑客的方式”注入了公司的管理層和開發團隊。在我采訪 Facebook 的兩天時間里,我充分意識到發布工程技術的重要性。伴隨著網站規模的迅速增長,這項技術扮演了關鍵的角色,將“黑客方式”融入到應用當中。
(伯樂在線另外配圖,Justin Sullivan, Getty Images)
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 獨特的開發者文化的象征。