
作為一個網際網路創業公司,餓了麼從初創到壯大,在移動網際網路時代,業務量和技術團隊的體量經曆了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月加入餓了麼,負責全局架構的工作。