作為一個互聯網創業公司,餓了麼從初創到壯大,在移動互聯網時代,業務量和技術團隊的體量經歷了10倍增長,這其中的經歷,是互聯網領域許多創業公司技術團隊的一個縮影。在這裡把我們成長過程中的體會和教訓記錄下來。
餓了麼的技術體系,經歷了以下四個階段:
1、核心系統 All in one 的早期架構;
2、以系統領域化拆分、業務系統和中間件等基礎設施分離為基礎的全面服務化的架構;
3、隨著自動化平臺、容器調度體系成熟,治理從傳統運維向 DevOps 轉變的基礎設施體系;
4、多數據中心體系基礎上的Cloud Ready架構成型。
在這期間,業務的快速增長,大大小小的事故,組織結構的變化、融合,團隊的工程師文化和技術棧背景,不同技術理念的衝突,交織在一起,互相沖擊、影響著架構變遷。
第一階段:All in one
這是餓了麼技術體系早期的樣子,技術聯合創始人帶著一群 Geek,從0到1,搭建了最早的技術體系,支撐了百萬級的單量。
這個階段,業務一路狂奔,技術拼命追趕業務,唯快不破。
技術棧以 Python 為主,兼有部分 PHP 的系統,單機多應用的混布模式,應用發佈、系統運維基本上是開發工程師敲命令行完成。核心的業務系統,商戶、用戶、交易都共享一個 codebase ,建立在一個名為 zeus 的系統下。短時間內業務飛速增長,在線數據庫已經支撐不了 ETL 的需求,大數據體系、數倉開始建立。
大數據所在的上海數據中心,在線業務系統所在的北京數據中心開始搭建,這兩個數據中心見證了我們架構的變遷,直到後來整體上雲,最早的時候,技術團隊40多人,有時候需要創始人跑到機房裡面搬服務器。
系統跟不上業務發展速度的時候,核心系統經歷過一些間歇性宕機的尷尬階段。一些剛剛開始開拓的業務系統,也經歷了系統剛上線就連續宕機,不得不臨時放慢業務的階段。但是這個過程也有收穫,很多開發工程師線上排障能力非常強,腳本玩得很溜。
這個時期的工程師經常一人身兼數職,前端、後端、開發、測試、運維部署,對業務的理解也很深刻,甚至身兼技術和產品的角色。
收穫和教訓——文化的重要性
早期餓了麼有一個鮮明的特點,就是大家都非常有擔當,開放且包容。推卸、逃避責任的事情很少發生。雖然當時很多事故回過頭來看比較嚴重,但是,組織對技術人員成長中的錯誤也相對包容。整個團隊比較扁平,上下級之間的技術爭論是常有的事情,但是都能就技術論技術。
餓了麼的工程師文化氛圍還是挺強烈的:工程師想著服務器的資源利用率能不能再壓榨一下;在決定大規模使用Redis之前,會去讀Redis的源碼;很多方案,都是找個吧檯和白板快速討論,快速達成共識,落地上線。可能也是這個氛圍,吸引到很多相同味道的人,形成了技術團隊的文化。技術團隊創始人最初形成的文化能延續下來,是這個團隊從最初的幾十人發展到上千人,還保有凝聚力和執行力的基礎。
第二階段:拆遷和基建
技術系統架構影響了業務交付效率,那麼就需要重構甚至重建系統;如果是組織結構的不合理,阻礙了系統架構的迭代,成為業務發展瓶頸,那麼就需要調整組織結構。
打個比方,如果說業務系統是賽車,那麼基礎設施就是跑道。基礎設施也是這個階段我們建設的重點,為承接將來業務快速增長打下基礎。
這個階段我們面臨幾個問題:
Python為主的技術棧,現有的工程師單兵作戰能力都很強,但是當時市場上的兵源嚴重不足。
All in one的系統,各業務領域沒有劃分,業務模塊之間代碼交錯,影響到了交付效率,需要給業務快速發展鬆綁。
基礎設施和業務系統開發沒有分開,身兼數職的開發工程師,在基礎設施運維、中間件開發、前後端業務系統開發各個方面,各有長短。
傳統的手工上線、部署、運維、監控模式 —— SSH到服務器上手工執行腳本,效率低下,事故發生時恢復時間長,發佈後難以回滾。
成建制
隨著業務量迅速增長,業務系統日漸複雜,技術團隊也隨之擴張,當時人才市場上 Java 工程師還是比 Python 的工程師來源多,有更大的選擇餘地,因此,以 Java 為主的多個領域開始逐步壯大,形成了 Python 和 Java 兩大技術棧體系。
大前端、移動端、多個領域的後端業務應用、運維、中間件、風控安全、大數據、項目管理等多個角色分工,不同角色的工程師做自己擅長的事情。在這個階段,系統和組織形成了業務開發、運維、共享組件及中間件幾個體系架構。
業務系統
1、業務領域劃分
單體架構的系統開始按照領域拆分。All in one 的系統,通過劃分業務領域,各個領域的技術骨幹認領掉所負責的領域,組織結構相應調整,才很艱難的完成了劃分。導購、搜索推薦、營銷、交易、金融、公共服務、商戶商品、商戶履約、客服,新建的物流運單、分流、調度等系統,大數據數倉,逐步識別出自己領域、子領域及相應模塊。在這個過程中,也有一些骨幹在接手所負責領域後,沒有在第一時間重視人力資源,導致交付能力不足,成為業務發展的瓶頸。從技術骨幹再到技術團隊負責人這一轉變過程中,很容易被忽略的就是團隊的人員結構。
2、系統拆分
隨著各自領域內的需求快速迭代,系統也迅速膨脹,相互之間的依賴、領域邊界也開始變得錯綜複雜。原來可以“閉環”的,現在需要交互了,甚至原來可以直接動其他領域的代碼提PR,訪問別人的數據庫,現在都不行了。從單體到領域切分服務,改變原有的思維方式,導致很多不適應,也走了一些彎路:導購域為了性能,自己落了一份商戶商品數據緩存供商品查詢,從而需要理解商戶端領域的業務,訂閱這類主數據的變更,而商戶端的這部分數據就沒有辦法收口,緩存的新鮮度保障、底層數據結構變更、系統重構都比較麻煩;交易域麻煩也不少,一些領域為了不依賴交易,自己冗餘了大部分的訂單數據;物流履約領域則是下游存在多份運單數據冗餘,導致領域職責沒有收口,帶來很多一致性問題,系統的複雜度也隨之增加,系統交互和溝通成本也上升了。
而另一個極端,則是系統拆得過散,頻繁的互相調用依賴,把本該高內聚的系統拆成低內聚了。訂單和物流都經歷了過度的異步化帶來的痛苦,故障恢復時間過長,複雜性和排障成本增加。這段時間,領域邊界的分歧也是一個頭疼的事情。
體會和教訓——康威定律、技術文化
訂單履約體系裡面,有一個隸屬於商戶域的團隊,負責推單系統,該系統主要職責是承接商戶呼叫配送,並將訂單推送到物流運單中心。因為想減少對訂單系統的依賴,以防訂單系統掛了,無法履約,開發團隊冗餘了很多訂單的數據,為此,要同時考慮訂單和運單的正向、逆向,自身的可用性,系統也被設計得比較複雜。
在這個過程中,一旦有項目涉及到物流和推單系統交互,兩個團隊就經常發生領域邊界的分歧,涉及到一些從訂單取數的場景,物流團隊認為,“這部分邏輯我不應該理解,你們應該從上游系統取到推給我們”,而這個隸屬於商戶域的團隊則認為,“這部分不是推單的領域內的數據,而且推單系統自己也用不到,物流需要應該自己去取”。
類似問題反覆出現,反覆討論,消耗掉大家很多時間,而且可見的將來還會發生。鏈路過長也帶來穩定性隱患。最終,負責訂單域的團隊,來負責推單系統,訂單領域內的邏輯可以閉環,這個問題就迎刃而解了。
所以,涉及到兩個領域邊界的時候,一旦相似問題反覆出現,我們可能要考慮一下康威定律。
關於爭論:系統設計階段的激烈爭論是非常合理的,充分的討論會大大減少方案出現硬傷的概率,開發階段也少受返工之苦。技術討論圍繞技術合理性,就事論事的展開,不要因為技術以外原因推脫或者權威說得算,討論完大家才能很坦然的接受決定,最重要的是,參與的工程師都能充分理解最終方案的利弊和取捨,落地不會有偏差,出了問題不推諉。這也是很多團隊技術氛圍吸引人的一個地方,文化不是口號,是日常的這些細節和實踐。
運營體系
1、 團隊
負責軟件交付的業務運維、負責底層操作系統和硬件交付的系統運維、負責數據庫的DBA、穩定性保障團隊,相繼成立。
2、 監控告警
集群實例的數量急劇擴張,登錄到服務器上查看日誌的運維模式已經不現實,也被基於遙測的監控體系所替代。隨著7*24小時的NOC團隊(故障應急響應)建立,基於ELK的監控體系也搭建起來,將核心指標投到了監控牆上。
體會和教訓:監控告警機制的意義
互聯網應用到了一定複雜度以後,龐大的集群,尤其容器化之後,IP動態分配,日誌數量巨大,日誌數據繁雜而離散,監控和排障需要藉助的是和衛星排障一樣的思路——遙測。日誌系統要支持聚合和查詢,監控需要實時收集採樣各種指標、監控面板可以隨時查看系統當前健康狀況、告警機制第一時間發現系統冒煙。
有一次出現了一個問題,從監控面板上看一切正常,後來發現根因是一個int32 溢出的bug,導致下單失敗,但是,為什麼監控看不出來?因為代碼裡面把異常吞掉了,調用返回成功,而我們核心指標用的是下單接口的成功調用量指標和一些異常指標。
經過關鍵指標的治理,我們的監控面板關注三類核心指標:
業務指標 —— 實時關注業務整體的健康狀況,從這個指標可以很直觀的看到,業務的受損程度、時長和影響面,比如單量什麼時候掉的、掉了多少比例、掉了多久,關鍵頁面訪問的成功率怎麼樣。對於上面訂單的案例而言,需要在下單成功後打點(由負責訂單落庫的實現邏輯來完成),確保真實反映業務狀況。需要注意的是這類指標通常涉及到敏感的業務信息,因此需要做一些處理。
應用指標 —— 關注應用實時的健康狀況,應用本身及直接上下游的調用量、時延、成功率、異常等。為了安全起見,應該注意敏感系統信息不露出。特別是涉及到金融、安全領域的業務系統。
系統指標 —— 關注中間件、操作系統層面的實時健康狀況,Network Input/Output、CPU load & Utilization、Memory Usage等。故障發生時,這些指標通常會先後發生異常,需要關注哪個是因哪個是果,避免被誤導。
當然以上還不夠,監控還有一些需要關注的地方,隨著業務的發展,對我們的監控系統帶來更多挑戰,後面一個階段,監控體系會有翻天覆地的變化。
這個事故給我們的另一個教訓就是,關鍵路徑和非關鍵路徑的隔離:那個int32 溢出的bug是在非關鍵路徑上觸發的,當時還沒有梳理出來關鍵路徑,所以非關鍵路徑故障影響到關鍵路徑的事故時有發生。隨著關鍵路徑的梳理,後續服務降級的能力也才開始逐步建設起來。
3、 業務運維
業務運維團隊擔負起了很多業務系統運行時環境的初始化工作,比如虛擬機、HAProxy、Nginx、Redis、RabbitMQ、MySQL這些組件的初始化工作,容量評估工作。穩定性保障工作也是主要職責之一。這些分工讓開發工程師可以更專心於業務系統的交付,但也是一把雙刃劍,這是後話了。
4、 系統運維
隨著專業運維團隊的建立,系統從物理機遷移到了虛擬機上,單機單應用(Service Instance per VM)是這個階段的主要部署模式。硬件設備的運維,網絡規劃,慢慢都更專業規範起來,CMDB也開始構建。
5、 DBA/DA
數據庫的運營統一收口到這個團隊,負責數據庫容量規劃,可靠性保障,索引優化等等,交付了包括數據庫監控體系在內的很多數據庫運營工具產品,與此同時,他們也參與到業務系統的數據架構設計和評估選型當中。
6、 制度
故障等級定義、架構評審機制、全局項目機制也相繼出爐。制度的建立、執行和以人為本,三件事情,從來難統一,得不到人的認可,則執行會打折,背離制度設立初衷,所以,制度也需要迭代。制度是底線,制度覆蓋不到的地方靠團隊文化來維持,但是,如果把文化當制度來執行,就得不償失了。
體會和教訓——架構師到底要做什麼?
架構師是一個角色,而不是職級。這個角色的職責包含但不僅僅限於以下方面:
1、業務系統的技術方案設計和迭代規劃
2、非功能需求的定義和方案設計、技術選型
3、現有架構的治理、領域邊界的劃分、設計原則與現實(技術債)的取捨和平衡
4、架構未來的演進
但是,如果不夠深入,以上很難落地。從事前設計,到事中交付階段的跟進,事後線上系統運營及反饋、持續的優化迭代,都需要架構師充分主導或者參與。
敏捷開發,設計是很容易被忽略掉的一環,制度上,我們通過組織由運維、中間件、業務開發、數據庫、安全風控各領域的架構師組成的評審會議,在算力和基礎設施資源申請通過前,審核評估設計方案。
實際上,這個上線前的“事前預防”也還是有侷限性,因為這個時候設計方案已經出來了,雖然設計文檔評審前已經提前提交,但是沒有能夠參與到整個生命週期,深入程度有限,只能做到兜底。更好的機制是每個團隊都有架構師的角色,在設計過程中充分參與,這才是真正的“事前”。
所以,很長一段時間,跨領域的重點項目或者整個技術中心的項目,架構師才會主導或者相對深入的介入,而日常迭代,在設計方案、領域邊界各方有重大分歧的時候,架構師往往是被動的捲入,變成居委會大媽的角色。覆蓋的領域太廣,架構師這個時候成為了瓶頸。而業務系統的架構恰恰是由日常的迭代逐步構建而成。後來全局架構組成立,架構師才真正開始分領域深入到更多業務的日常交付當中:
設計方案的負責人,作為Owner,為方案負責,並跟進交付,有權力有義務;
構建影響力。除了日常和開發工程師一起並肩交付,沒有捷徑,這個需要比較長的時間和項目的積累,很難一蹴而就,這個過程中如果得到一線工程師和開發經理的認可,影響力就會逐漸形成,有更強的推動力,反之,則會逐步喪失影響力。架構師如果沒有影響力,一切無從談起。因為組織上,架構師不一定在一線開發工程師或者開發經理的彙報線上。中立客觀的態度,開放的心智,也是構建影響力的關鍵。影響力是架構師 Leadership 的體現。
穩定性的保障,永遠是架構師的核心關注點之一,穩定性指標是架構師必須要揹負的。其中包括交付質量、可用性、彈性、系統容量等等,不深入到具體領域,很難去提供保障。為此,架構師需要有調動資源保障穩定性的權力。
技術文化的影響,架構規劃的貫徹,除了常規的宣講、分享以外,更為重要的是日常項目交付、技術討論過程中的潛移默化。設計思想、設計原則、技術文化認同,這些不是說教能形成的,是在一個個項目迭代、一次次線上排障、一場場事故覆盤會中達到共識的。
在以上幾點的基礎上,才能夠在全局上擔負起架構的職責,推進架構的演進。敬請期待下週內容:餓了麼全面服務化的架構實現和挑戰;容器化實踐與 DevOps 轉變等。
作者:黃曉路(花名:脈坤),2015年10月加入餓了麼,負責全局架構的工作。