在上一篇文章《餓了麼技術往事(上)》中,我介紹了餓了麼最先期 All in One 階段的架構,以及第二階段業務系統拆分與團隊運營的一些思考,以及我對於架構師職責的感覺,接下來我會詳細介紹餓了麼全面服務化的架構演進歷程。前端
業務線的工程師深陷到快速的迭代和業務複雜性當中,業務的快速增加、外賣行業午晚高峯業務特色帶來的併發挑戰,領域拆分後所需的服務體系框架支撐,責任天然落到了中間件團隊。面試
當時中間件團隊主要負責的三件事就是發佈系統、SOA框架、統一的數據訪問層。算法
外賣業務週末的單量一般比工做日要高,可是工做日事故率要高於週末,爲何?變動是萬惡之源,週末不多發佈。因此,發佈系統接手管控,取消手動發佈的模式,解決發佈回滾的問題,經過發佈自動化提升效率的同時,回收服務器的權限,下降安全和穩定性的隱患。固然發佈系統的做用遠不止於此,後續這個體系及其團隊充當起了基礎架構演進的核心角色。這個是後話了。數據庫
SOA框架是支撐業務服務的骨架。和多數相似框架同樣,爲應對複雜的服務體系,服務註冊和發現,常見的基於Design for failure的設計,熔斷、限流、艙壁、多集羣隔離這些功能都同樣。可是,較特殊的地方在於——咱們有兩套SOA框架,Java 版和 Python 版。前面提到,咱們有兩個主要的技術棧 —— Java 和 Python,使得咱們凡是須要 SDK 的地方,都須要支持兩種語言,毫無疑問會對增長中間件團隊負擔。在當時確實是個難題,這個如今固然也有解,後面會提到。編程
體會和教訓——是否應該統一技術棧?緩存
關因而否應該統一技術棧,沒有一個標準的答案。每一個公司的技術棧和技術體系,有其造成的背景,如同架構同樣,不放在上下文裏面討論合理性,每每沒有結果,煙囪型也好、L型也好,只要是適合本身的技術和架構就好。安全
Python 技術棧當時已經支撐了不少核心系統,推翻現有系統,換技術棧的時間成本不可忽視。而當時市場競爭很是激烈,對於餓了麼這樣的創業公司,數據、時間和人是最寶貴的。並且,有一支能力很是強的 Python 技術團隊,從裏面抽調部分工程師,支撐 Python 技術棧的中間件建設,也不會帶來額外的人力成本。維護兩個技術棧,中間件團隊的負擔會增長,可是,換取的是時間和優秀的工程師,仍是划算。這些 Python 工程師裏面,負責業務系統的不少人後來也成長爲獨擋一面的角色,跟上了業務快速增加的步伐(後續會有相關的內容分享)。而負責中間件的 Python 工程師,他們的一些創造性實踐,也爲咱們後續架構演進奠基了基礎。服務器
好的技術體系和架構,起決定性的不是技術棧,最終仍是優秀的工程師。網絡
由於多技術棧的存在,DAL 層選擇了中心化的方案,而沒有采起 SDK 。統一的數據訪問層爲後續分庫分表、限流保護、多數據中心上線後的數據糾偏打下了基礎。爲了保證系統有足夠強的吞吐能力,DAL 層採起了異步 IO 的方案來處理出入流量,中間件的最高境界是你們會忘記它的存在,DAL 層涉及到底層和數據庫的交互,尤其敏感,而這個中間件幾乎作到了,沒有出現太重大事故,也不多有開發吐槽這一層的問題。後來,這個一直穩健的團隊在餓了麼多數據中心建設當中,負責了核心的流量調度及容災切換管控體系。你們都習慣了叫 DAL,不少人不知道這個系統叫 Athena。架構
基於 DAL 的上線,DBA 和 DA 這個時期就忙着給各個團隊作分庫分表的事情:
按業務功能領域切分——拆庫
按照訪問頻率、動靜態屬性等等規則——垂直分表
基於Hash Partition(須要注意的是避免熱點和Rebalance帶來的成本)—— 水平Sharding
總之就是選擇合適的 Partition 策略,下降數據庫單個實例的負載。存儲後來能支撐住千萬級單量,除了上游隊列的削峯、緩存的緩衝、數據庫讀寫分離之外,也得益於適當的 Data Partition 策略。
其餘團隊還在拼命追趕業務、填坑補課的時候,大前端團隊知足業務需求的同時,還爲開源社區貢獻出了很是優秀的產品 Element。就在你們認爲這支團隊會繼續在前端領域上一騎絕塵下去的時候,使人沒有想到的是,這個團隊幾年後會爆發出巨大的潛力,成爲整個架構體系升級中一個舉足輕重的角色。爲何叫大前端,由於他們和傳統的前端團隊作的事情不太同樣,後面會講到。
招聘優秀的工程師,持續招聘優秀的工程師,這是一句正確的廢話。可是有多難,帶過團隊的應該都深有體會,特別是你的公司尚未自帶光環的狀況下。優秀的工程師會吸引來更多更優秀的工程師,反之亦然,面試這個過程是雙向的,尤爲是優秀的工程師。有業務壓力的時候,主管很容易扛不住,下降要求。當時大前端團隊校招淘汰率仍是挺驚人的,換來的是這個團隊的工程師很高的技術素養和基本功,爲後面成爲一個真正的全棧團隊打下了的基礎。
Leader的我的能力,決定了他(她)是這個團隊的地基仍是天花板。
基於 Hadoop、Spark、HBase 的經典大數據架構這個時候也搭建起來了,由於是自建的數據中心,因此這些產品都須要有一個專業的團隊來運維,所以大數據也有了本身的運維和中間件團隊。在這個階段,在線和離線數據同步、數據治理上面還不完善,由於產品化還在路上,不少工具缺失,致使不少團隊都要本身直接去從數倉取數,不得不維持運營團隊支撐定製化的手工取數需求。各個團隊喊得最多的就是大數據的人不夠,想要本身作。核心仍是業務發展太快。後面隨着大數據團隊逐漸壯大,更多強援加入,各個產品相繼成熟才得以緩解。
這是一個不得不說,可是也不能說太多的團隊,因此這部分只能務虛一些,任何一個到了必定規模的企業,風控安全團隊是「真」底線。其餘技術團隊在面對這個一樣是負責技術的團隊面前,有時候確實也挺一言難盡的,這個時候高層的支持相當重要。尤爲是從 0 開始建設這個團隊,對內的掃盲和對外風控,同樣艱難。
若是說一個技術公司,系統毀了,有什麼還能留下來,就還能重建,那確定是數據(如今可能還要加一個算法模型)。有什麼缺失了,隨時均可能垮掉,那確定是風控安全。
餓了麼的這支風控安全團隊,對內、對外、對線上、對線下、對其餘……都面臨不少挑戰和衝突,堪稱業務專家的羊毛黨和無孔不入的黑客,確實使人歎爲觀止。而咱們的風控也經歷了從開始的粗粒度約束、到依賴業務規則針對各類補貼、帳期等場景兜底、再到依賴算法模型實時風控主動攔截的階段。
若是你們身邊有作風控安全的同窗,請珍惜,哪怕他們有時候看到系統處處是窟窿的時候,脾氣暴躁。由於他們成天面對這麼多黑暗面,還能對這個世界報以但願。開個玩笑,從人道的角度出發,這個團隊須要按期的心理按摩。
這個階段,咱們初嚐了算法的威力。一開始只有搜索,可是尚未推薦召回系統,當時給推薦系統的物理機是咱們能拿得出手的最好的物理機,其餘業務系統分配的大都是虛機。系統上線之後,效果、轉化率都還不錯。以後不久這一待遇被另外一個團隊承包——負責配送履約的智能調度團隊,大數據、機器學習、算法模型須要充分發揮功效,須要長時間緊貼業務、深入理解業務,在智能調度領域咱們也作過很多艱難的嘗試、吃過不小苦頭,直到咱們有了本身的算法專家團隊。
這個階段咱們還經歷了第一次外賣行業的大促——517大促,讓你們真切感覺到了這個市場的巨大潛力,同時系統的一系列短板也暴露無遺,除了積累了大促的經驗之外,更大的收穫是讓咱們看到架構還有很大的升級空間。還收穫了一支全鏈路壓測團隊,他們在從此架構升級以及系統質量、容量等穩定性保障過程當中,扮演了關鍵角色。
在餓了麼技術往事系列文章的開篇,我提到了餓了麼的技術體系經歷瞭如下四個階段:
核心系統 All in one 的早期架構;
以系統領域化拆分、業務系統和中間件等基礎設施分離爲基礎的全面服務化的架構;
隨着自動化平臺、容器調度體系成熟,治理從傳統運維向 DevOps 轉變的基礎設施體系;
多數據中心體系基礎上的 Cloud Ready 架構成型。
如今咱們前兩個階段基本完成了,開始了相對而言最艱難的階段了……
這個階段,咱們的業務已經發展到必定規模,系統的長時間抖動或者崩潰,很容易上熱搜,尤爲是飯點時段。發生事故時候,衝在第一線的除了各業務線的工程師,還有運維團隊,他們每每是最早響應,排障衝在第一線的團隊。這個階段說是靠他們生扛頂住了穩定性的壓力也不爲過:平常基礎設施部署、事故發生時的應急響應、事故發生後的基礎設施優化和改進措施落地,他們都承擔了不少。
事故的教訓,也讓咱們學會了遵循一系列業界積累下來的設計原則,爲架構演進到下一階段打下基礎。
業務領域拆分、基礎設施和業務系統分別建設後,給業務快速發展解綁了。可是包括穩定性在內的一系列挑戰依然須要面對:
基礎設施部署的標準化
系統的生命週期怎麼管理?
每次故障都是昂貴的學費,故障能夠避免嗎?
複雜性帶來的挑戰:團隊裏面幾乎沒有人面臨過這個體量的業務、這個複雜度的系統。快速交付的同時,如何保證系統的穩定和健壯?
由於雲上資源的靈活性,咱們在雲上搭建了兩個測試環境:alpha做爲開發環境,用於軟件工程師平常開發調試;beta做爲集成測試環境,用於測試工程師完成系統交付上線前的集成、迴歸測試。費了九牛二虎之力才達成全部團隊的共識,推進beta環境的系統和數據的完整性建設。在這裏面發揮重要做用的,除了各個業務的開發、測試、運維團隊,還有一個就是以前提到的負責發佈系統的團隊,這個團隊不只僅提供了一個簡單的發佈系統,基於持續集成和持續部署實現的開發、測試、生產環境類似化,是咱們的系統架構繼續演進的開端。
技術團隊職責細分後,運維團隊提供了保姆式的服務,這把雙刃劍的另外一面,就是開發團隊很容易造成惰性,對本身的系統管生無論養,對系統的容量、治理關心不夠,由於有運維團隊。這就帶來不少問題,代碼不是運維工程師寫的,可是有些團隊系統甚至是運維工程師部署的。由於開發團隊最貼近業務需求,需求變動可能帶來將來的潛在容量風險,他們比較有發言權;而容量水位的現狀反過來是運維團隊更瞭解。由於這個時候,不少基礎設施運維還沒徹底自動化,因此難以統一化、標準化,每一個運維工程師都有本身的運維風格,平常排障上,有時候須要開發和運維一塊兒才能完成。
此外,只生不養的思惟方式,客觀上也容易形成算力成本變成糊塗帳。這個時候,開發、部署、系統運營(治理)角色的不統一帶來的問題就會凸顯。
應用Owner要成爲名副其實的Owner,須要有應用的全景視角,對應用生命週期的把控能力。這個階段,開始推進從虛擬化到容器化的轉型,發佈系統從一個簡單的CI、CD的體系,延伸到了算力和調度的領域。基於一系列運維自動化工具的建設和全面容器化調度的實施,從而帶來標準化的運維,才能把開發工程師(應用的Owner)推到應用完整的生命週期運營的位置上,勝任DevOps的角色。這個時候,事實上底層的算力平臺,已經具有云上PaaS的雛形了。
在這個過程當中,也作了很多嘗試,好比,爲了提升 alpha/beta 這兩個測試環境的基礎設施交付效率,有過一段時間基於 slack 的 ChatOps 實踐,工程師都比較歡迎;還有過 Infrastructure as Code 和 GitOps 的實踐,很惋惜當時各方面條件和時機都不夠成熟,沒有持續推廣。
alpha 和 beta 環境:
工程師在開發機上自測是否是就能夠了,「在我機器上是好的」這句話估計開發工程師都說過或者聽過,在開發階段提供alpha環境,目的就是爲了開發、測試、生產環境的儘可能接近,避免因爲開發、測試、生產三個階段因爲環境差別巨大帶來的問題。解決不了「在我機器上是好的」這個問題,沒有辦法大規模順利上雲。工程師本身的電腦,某種程度上是一臺「mommy server」,上面運行着須要的一切環境,並且每一個工程師的祖傳環境還不同,這類環境在生產上是不可複製的。
怎麼作到高質量快速交付,保證系統的穩定?
在快速迭代的同時,作到快速試錯、快速糾錯、快速回退。須要發佈系統作到每一個編譯的版本、每次發佈的版本,像代碼同樣,可回溯可跟蹤。關鍵在於build和release是immutable的
首先,build和release有惟一的ID,纔可追溯,可回滾;
其次,是配置分離,把和環境(dev/test/product)相關的config從代碼中剝離開來,不然系統很難遷移,更不用說大規模上雲。第一反應多是,把和環境相關的config寫在xml或者yaml文件就能夠了,可是,這些文件也是代碼。
相似的,將這些隨環境變化的config寫在發佈流水線的腳本里面,都不是完全分離的方式。由於發佈環境會發生變化,可能未來有更多的測試環境、更多的數據中心、每一個數據中內心面可能還有多泳道。
所以,要作到「build once, deploy many times/every where」,config要存儲在環境的上下文中,好比開發、測試、生產環境各自有一個配置中心,線上系統拉起的時候,先從配置中心拉取配置信息。要衡量環境相關的config和代碼是否已經分離,看看能不能開源就知道了(拋開價值和代碼質量不談)。
接觸過傳統的運維工程師都知道,這是一羣責任心極強的人(刪庫跑路,剷平數據中心的事情是不可能幹出來的,雖然有能力……),他們維護着系統的底線,第一次517大促事故的時候,咱們靠運維工程師救了你們一命。
可是,即便有操做的SOP,只要是人,執行重複任務的次數足夠多,總會犯錯。而每一個資深的運維工程師,都有本身祖傳的腳本,一夫當關萬夫莫開,可是休假就麻煩了,特別是在高鐵上信號很差的時候……最佳實踐→ SOP → 腳本 → 自動化工具產品,沿着這個路徑迭代彷佛不可避免。
傳統的運維工程師角色的演進方向,一個是爲雲上的IaaS/PaaS服務,對操做系統和底層硬件有着豐富經驗的,仍是運維工程師,他們當中開發能力強的,轉型SRE,對運維產品理解深的,能夠選擇 Technical Product Manager 的角色,爲雲上運維相關平臺產品提供解決方案,或者憑藉豐富的雲上系統落地實施經驗,爲各上雲企業提供實施方案。
另外一個方向,因爲合規和其餘緣由,還有部分沒有上雲的企業,依然須要基礎設施運維工程師。隨着雲逐漸變成和水電煤同樣的社會基礎設施,運維工程師只寫操做系統腳本、實施部署的時代已經漸行漸遠了。
架構的歷次演進,和幾回事故或者險些釀成事故的「冒煙」事件,有着很大的關係:
交易系統崩潰的「餓死了」事故,咱們開始分離關鍵路徑和非關鍵路徑,建設了非關鍵路徑的降級能力。故障應急響應常規三板斧:重啓、回滾、降級,至此完備。
第一次 517 大促入口崩潰的事故,是咱們核心系統上雲的開端。
F5 的 CPU 被打滿,讓咱們意識到網關做爲入口難以擴展的巨大風險,從而基於從新構建的大網關體系,取代了 F5 這一層硬件負載均衡。大網關體系是咱們多數據中心架構最核心的系統之一。
基於 VIP 的 keepalived+HaProxy 負載均衡體系下,各類 failover 和上下游頻繁擴縮容過程當中,相關的穩定性冒煙或者事故頻發,促成了充當 data plane 的 sidecar 上線,這是咱們構建類 Service Mesh 架構體系最重要的組件。
核心交換機 bug 引起的數據中心故障,對咱們下決心建設多數據中心體系有着很大的影響
關於這些事故和架構的故事,隨着架構的演進,後面會逐個展開。
那個時候,咱們經常自嘲是「事故驅動」型開發(Disaster Driven Development)。不少工程師除了本身的工位,在公司裏面最有「感情」的就是整面牆都是監控大屏的NOC做戰室,大小事故、各類大促活動值守,熬夜全鏈路壓測,裏面經常擠滿熟悉的面孔。
(1)事故覆盤
事故覆盤和按期的故障驗屍總結會是一個很好的機制。很容易被忽略的是,除了找到事故發生的 root cause,還須要從中發現存在的隱患,而不是 case by case 的解決問題,覆盤的目的是阻止相似的事情再次發生,必要的時候,能夠引入業務、產品、技術共同解決。
另外一個陷阱是,故障覆盤變成追責的過程,那麼參與覆盤的各方就很容易陷入互相指責、洗脫責任的怪圈,反而忘記了覆盤的根本目的,也容易浪費大量時間,引發沒必要要的內耗。只要是參與覆盤的人,都是有責任在身上的,爲未來的故障負責,若是相似事故再次發生,或者沒有在覆盤中發現應該發現的隱患,參與的人都難辭其咎。
覆盤結果要避免懲罰爲目的 —— 除非違反了規章制度(底線,不排除有些是惡法,但不在討論範圍內)。不然甩鍋、不做爲的氛圍會日漸滋生,自省有擔當和有做爲的我的或者團隊,很容易成爲吃虧的一方。事故覆盤的過程,是瞭解各個團隊甚至組織文化的一個視角。
(2)彈性設計
物流、交易經歷事故後,各自採起的措施再次印證了,反脆弱的設計是咱們的應用發展到今天的核心設計思路之一。
傳統思路是基於一個上下文可控的理想系統環境下作出的設計,儘可能避免一切意外的發生。而反脆弱的設計,偏偏假設黑天鵝事件必定發生,是墨菲定律的信徒,開句玩笑話,雲廠商若是承諾你「咱們必定會掛」,你必定要珍惜,你面對的是一個坦誠相待的乙方,值得託付。這不是推責給雲廠商,這是由雲上基礎設施的特徵決定的,大多數場景下,雲上提供的服務是基於大規模標準化服務器(Off-the-shelf hardware)構建的虛擬化、容器化基礎設施(Immutable Servers),而不是超高規格的個性化定製獨佔設備(Snowflake Servers)——沒法規模化,成本也會大規模上升,所以,會更注重快速恢復能力,水平擴展能力,總體的健壯性,而不是具體某一個單機 SLA。
因此雲上系統更強調算力的抽象,CPU核數、內存、網絡帶寬,把數據中心看做一個超級計算機,和 CPU 具有糾錯機制同樣,雲上基礎設施不是不會發生錯誤,只是結合它的「操做系統」(好比 Kubernetes),提供的是糾錯能力(好比容器的故障轉移 —— 故障容器銷燬,新容器拉起,本質上也是冗餘),而云上業務系統須要適配這類糾錯機制實現本身的自愈 —— 面向雲編程 —— 接受短期的抖動(Transient Fault)會不時發生的這一個事實。
物流經過補償機制加強本身的健壯性,交易引入 chaos engineering,都是基於這個上下文。要求應用是 stateless 或者 disposable 的,目的是爲了 crash 後可以迅速拉起,快速自愈——因此,儘可能分佈式緩存,儘可能少本地緩存,應用拉起時初始化的工做盡可能少,交給獨立的服務幹這些事。業界的不少模式實踐:bulkhead, circuit breaker, compensation transaction, retry都是指向提高系統的彈性(resilience),足夠健壯的系統可以在經歷系統抖動後,迅速自愈。
故障和意外同樣,難以免。咱們能作的是減小人禍,敬畏生產環境,由於一次故障影響的多是騎手一天的生計、商戶一天的營收、用戶的每日三餐。同時,提升系統的健壯性和自愈的能力,在故障發生的時候,儘量的避免演變成更大的災難,及時止損。
這個階段,咱們經歷了一個大事故,原由就是核心交換機掛了,可能有人問,不都堆疊的嗎,不都有主備嗎,不都自動切換的嗎,說得都對,可是都掛了。由於交換機的一個bug,主備切換後,備機也很快被網絡風暴打掛,沒經歷過咱們也不相信。此次又「餓死了」,咱們只能坐等供應商的工程師抱着設備打車到機房更換,這個時候,一羣人擠在應急響應指揮室(NOC做戰室)裏一點辦法都沒有。
在第一次517大促以後,咱們就開始第一次容災嘗試了,當時採起的是最快最簡單粗暴的方案,用最短的時間,在雲上搭建一個了災備環境並跑通了業務鏈路。但這是一個冷備的環境,冷備最大的風險,就是平常沒有流量,真正 failover 切換的時候,有比較大的不肯定性。此次事故再加上另外一個因素,咱們下決心將技術體系推動到下一個階段。
2016年第一次517大促,10點開搶的瞬間,咱們系統崩掉了,要不是當時一個很穩的運維工程師,淡定操做限流,可能很多人在餓了麼的職業生涯當時就結束了。由於對當時的基於Nginx和部分自研插件的網關層比較自信,不相信網關層會頂不住,因此全鏈路壓測的時候根本沒有壓這一層,過後覆盤的時候發現是操做系統一個參數配置的問題,若是壓測必定能重現。
由於業務的效果很好,大促就成爲常態,事實上第一次大促,咱們是在本身的IDC裏面用常規業務系統來扛的,因此影響到了非大促的正常交易。後面專門針對大促高併發大流量的場景設計了一套系統,也是隔離、排隊、CDN、限流這些常規的套路,沒什麼特別的。可是,對咱們影響更深遠的在於,這套體系徹底是在雲上搭建的,2016年以前雖然雲上有系統,可是生產環境流量不多,頂可能是短信觸達這類系統在上面,更可能是用於搭建測試環境。在當時看來,雲上強大的流量清洗、資源 scale out 能力,很適合大促的場景,後面,這套體系經歷了屢次大促,沒有波瀾。
在雲上搭建大促體系以及災備節點的經歷,讓咱們後續在雲上搭建全站的網關,並進一步構建整個數據中心,有了很是大的信心。下一篇我將繼續介紹餓了麼架構演變到了Cloud-Ready的狀態,技術體系演進爲業務發展提供了更多可能性。
做者介紹:黃曉路(脈坤),2015年10月加入餓了麼,負責全局架構的工做。
原文連接本文爲阿里雲原創內容,未經容許不得轉載。