關鍵信息
- Linkerd 2.0 在一個被廣泛接受的服務網格產品的基礎上進行了大量改造,原產品使用 Scala 實現,從 Twitter 的 Finagle RPC 系統中受益良多。
- 新版本產品從 JVM 轉為使用 Go(控制平面)和 Rust(數據平面)協作實現。
- Buoyant 團隊在 Rust 網絡棧方向進行了深入的探索,並簡化 UX,提升易用性,降低學習門檻。最終造就了更快、更輕更簡單的運維體驗。
- Linkerd 2.0 誕生至今已經過了 6 個多月,團隊相信這次重寫已經開始獲得回報,很多從前面對 Linkerd 1.x 感覺無從下手的用戶,現在已經滿意的開始使用 2.x。
服務網格正成為現代雲原生技術棧的重要成員。它把服務間通信(數據中心的慣用語中稱之為東西向流量)的機制從應用代碼遷移到了平臺層,並提供了用於對通信進行度量和處理的工具,讓運維人員以及平臺所有者得到一個基本獨立於應用代碼的觀察和控制層。
服務網格這個名詞的歷史還很短,背後的概念卻不是那麼新潮。這些概念在 Twitter、Netflix 和 Google 等公司中使用了超過十年,一般是通過 Finagle、Hystrix 以及 Stubby 這樣的胖客戶端形式實現的。從技術視角來看,現代服務網格中,部署協作代理(Sidecar)的方式是胖客戶端組件的變體,將客戶端庫換成代理服務器,這種方式因為 Docker 和 Kubernetes 這樣的容器和容器編排系統的流行而逐步成為可能。
服務網格的流行趨勢始於 Linkerd,它是服務網格的最早產品和項目。在 2016 年發佈第一個版本開始,目前有兩條並行的開發路線:原始的 1.x 分支,構建在 “Twitter 技術棧” 之上,包含了 Scala、Finagle、Netty 以及 JVM;2.x 分支使用 Rust 和 Go 從頭做起。
Linkerd 2.0 的出現,不僅是對底層的重新實現、還因為在多年以來的生產環境中的應用過程中總結了很多經驗教訓,所以在方法層面也有了很大改觀。本文將對這些實踐經驗進行討論,並探究其成為 Linkerd 2.0 哲學、設計和實現基礎的原因。
Linkerd 是什麼?值得注意麼?
Linkerd 是一個開源的服務網格項目,也是 CNCF 成員。它在 2016 年出現,為全球各種規模的公司提供生產級別的架構支持,客戶不僅包含 Strava 和 Planet Labs 這樣的初創公司,還有 ComCast、Expedia、Ask 和 Chase Bank 等大型企業。
Linkerd 為微服務應用提供了可觀察行、韌性和安全性方面的支撐。尤其重要的是,這些功能是屬於平臺層的。這意味著 Linkerd 的功能是跨越所有服務、獨立於實現方式的,這就讓平臺的所有者能夠跳出對開發團隊的路線圖和路線選擇的依賴,進行獨立思考。例如 Linkerd 能夠在服務之間的通信中加入 TLS,讓平臺運維人員能夠配置證書的生成、分發和驗證過程,無需在服務開發團隊的路線圖中加入 TLS 的相關工作。
Linkerd 是通過在網格中的服務中加入透明的 L5/L7 代理完成工作的。這些代理構成了 Linkerd 的數據平面,負責處理各自代理服務的所有出入流量。數據平面受到控制平面的管理,Linkerd 的控制平面是一組進程,為運維人員提供集中的對流量進行觀測和管理的服務。
Linkerd 基於對現實的一個認識:流經微服務的通信,和應用自身的代碼一樣,都是其運維工作的重要組成部分。Linkerd 無法介入微服務的內部,但是可以通過對成功率、吞吐量以及響應延遲的觀測,來報告服務的健康情況。與此類似,Linkerd 無法修改應用的錯誤處理邏輯,但是可以通過對失敗或緩慢的請求進行重試,來提高服務的健康程度。Linkerd 還能對連接進行加密,提供安全的服務身份認證,使用流量遷移的方式完成金絲雀和藍綠部署等功能。
Linkerd 1.x
我們在 Twitter 運行的應用,是業界最早、最大規模的微服務應用之一,Linkerd 就誕生於這種運維經驗之中。Twitter 從三層的 RoR 應用遷移到了 Mesos 和 JVM 基礎之上的類似雲原生的架構,這個過程中創建了一個庫:Finagle,它為每個服務提供了服務發現、重試、監控等功能。Finagle 是 Twitter 進入大規模微服務階段的重要一步。
Linkerd 1.x 誕生於 2016 年,根植於經過生產考驗的 Twitter 技術棧:Finagle、Scala、Nettfy 和 JVM。我們最初的目標很簡單:把 Finagle 的強力語義公諸於世。用 Scala 庫的形式提供異步 RPC 支持是很受限的,因此我們將 Finagle 綁定為代理服務器的形式,這樣就可以為各種語言編寫的應用提供服務了。與此同時,容器和編排系統的快速躥紅,很好的降低了為每個服務實例部署代理的成本。Linkerd 增長強勁,尤其是在快速推進 Docker 和 Kubernetes 之類新技術的雲原生社區。
從無到有,Linkerd 和服務網格模型本身都得到了長足的進步。今天 Linkerd 的 1.x 分支正在世界各地的公司中廣泛採用,並在持續的發展之中。
Linkerd 的經驗教訓
Linkerd 雖然很成功,很多組織還是不想將 Linkerd 部署到生產環境,或者願意這樣做,但是要進行大量投入。
這種情況的形成,有很多方面的原因。有的組織不想把 JVM 引進到自己的環境之中。JVM 的運維較為複雜,有些運維團隊因為這樣那樣的原因,拒絕任何基於 JVM 的軟件進入他們的系統——尤其是 Linkerd 這樣擔任關鍵角色的系統。
其它的組織不願意為 Linkerd 分配系統資源。一般來說,Linkerd 1.0 在資源充足的情況下,是很能承受規模負載的——單一進程每秒能夠處理幾萬的的請求;然而對付小負載的能力讓人不太滿意——單進程的 RSS 很難降低到 150MB 以下。Scala、Netty 和 Finagle 加劇了資源問題——它們的共同目標都是在資源充足的環境下提供最大的吞吐量。
一個組織可能要部署成百上千個 Linkerd 代理,資源消耗頗為可觀。作為妥協,我們建議用戶為每個節點而非進程部署數據平面,這樣用戶能夠降低資源消耗。然而這樣一來就提高了運維的複雜性,限制了 Linkerd 的能力實現,例如為每個服務提供 TLS 證書。
最近 JVM 在這方面有了長足的進步。Linkerd 1.x 的資源消耗和尾部延遲在 IBM OpenJ9 上都大有改觀,並且 Oracle 的 GraalVM 承諾會作出進一步的改善。
最後一點,還有複雜性方面的問題。Finagle 是一個功能非常豐富的庫,我們將其中的很多功能直接通過配置文件的形式暴露給了用戶。結果是 Linkerd 1.x 具有很好的定製性和彈性,但是也有了陡峭的學習曲線。其中一個設計失誤就是引入了 dtab 這一來自 Finagle 的路由語言作為基礎配置原語。任何想要對 Linkerd 行為進行定製的用戶都會陷入到 dtab 之中,在投入使用之前都需要進行很多的智力投入。
重新開始
儘管 Linkerd 的接受度還在上升之中,我們在 2017 年底得出共識,我們必須重新審視我們的方案。Linkerd 的價值主張無疑是正確的,但是它對運維團隊的高要求可能不太必要。當我們反思我們在協助組織採用 Linkerd 的經驗時,我們確認了一些面向未來的關鍵原則:
- 節省資源:Linkerd 應該儘可能的降低性能和資源成本,尤其是代理層。
- 開箱即用:Linkerd 不應該擾亂現存的應用,也不應該依賴複雜的配置。
- 簡單易用:Linkerd 應該能夠用較低的認知門檻來進行韻味。組件應該讓用戶感覺到清晰,其行為應該易於理解。
每一項需求都是一系列的挑戰,為了降低系統資源需求,我們只能告別 JVM。為了開箱即用,我們需要在網絡協議檢測等複雜技術上進行鑽研。最後,簡單是最複雜的需求,我們要在每個方面明確的落實極簡主義、漸進性和內省的原則。
面對這次重寫,我們認為我們首先應該專注於一個初始的用例。我們決定聚焦在 Kubernetes 環境下的通用協議,包括 HTTP、HTTP/2 以及 gRPC,這只是一個起點,以後會突破這些約束進行擴展。
目標 1:節省資源
Linkerd 1.x 中,控制平面和數據平面都是同一個平臺的產物。然而這兩個組件的需求是很不一樣的。數據平面會伴隨每個服務的每個實例一同部署,處理進出該服務的所有流量,因此必須又快又小。另外它還必須安全:Linkerd 的用戶相信它能夠用於處理敏感信息,符合 PCI 和 HIPAA 的合規性要求。
而控制平面是單獨部署的,並不存在於請求的處理路徑之中,對速度和資源的需求較低。它更看重對擴展和迭代的支持。
很明顯 Go 是控制平面的理想實現平臺。Go 具有運行時支持,以及類似 JVM 的垃圾回收機制,這一平臺為現代網絡服務而進行了優化,其運行成本大大低於 JVM。相對於 JVM,Go 語言的靜態二進制、內存佔用和啟動時間都是很吸引人的。我們的性能測試結果中,Go 比本地編譯的語言稍慢,但是對於控制平面來說就足夠快了。最後,Go 的生態系統讓我們獲得了很多關於 Kubernetes 的相關功能支持,另外我們認為這個語言的低門檻和流行度也都有助於開源社區的貢獻。
我們考慮過用 Go 和 C++ 開發數據平面,然而 Rust 註定是最符合我們需求的選擇。Rust 專注於安全,尤其是它強大的 Borrow checker,在編譯時強制執行安全內存實踐,避免了一整類內存相關的安全漏洞,這就使他比 C++ 更有吸引力。它能夠被編譯為本地代碼,並有細粒度的內存管理能力,這使 Rust 比 Go 具有更好的性能和內存控制能力。Rust 兼具豐富功能和表現力的語言特性,對我們的 Scala 程序員很有吸引力,零成本抽象模型聲明讓我們在不犧牲安全性或性能的情況下提高了表達能力。
Rust 的最大問題是(2017 年):它的生態系統比其它語言來說相對落後。我們知道選擇 Rust,就意味著要在網絡方面進行深耕。
目標 2:開箱即用
解決了底層平臺的決策問題之後,我們就要著手解決下一個設計目標了:開箱即用。對於 Kubernetes 應用,向既有應用中加入 Linkerd 不應破壞原有功能,也不應該依賴複雜的配置。
為了滿足這個需要,我們做了很多設計抉擇。我們給 Linkerd 的代理加入了協議檢測的能力:它能夠代理 TCP 流量,與此同時還能自動檢測其中的 7 層協議。在 Pod 創建時用 Iptables 設置流量劫持,業務代碼中的任何 TCP 連接都會被透明的經過本地的 Linkerd 代理,如果這些連接中使用的是 HTTP、HTTP/2 或者 gRPC,Linkerd 會自動的使用 L7 的方式進行干預——例如報告成功率、重試冪等請求、在請求級進行負載均衡等。這些目標都可以在無需用戶配置的情況下完成。
我們還努力提供儘可能多的缺省功能。Linkerd 1.x 中提供了豐富的代理級指標,把聚合和報告留給用戶去做。在 Linkerd 2.0 中,我們將 Prometheus 作為控制平面的成員,這樣我們就可以在 Grafana 中提供開箱可用的聚合指標視圖了。我們使用這套指標構建了一套 UNIX 風格的命令,讓運維人員可以在命令行中觀察運行中的服務。結合協議檢測功能,平臺運維人員可以從 Linkerd 獲得豐富的服務級的指標,無需進行配置。
進出應用的 TCP 連接都被路由到 Linkerd 的數據平面(Linkerd-proxy),Linkerd 數據平面讓這些流量可以被控制平面進行觀測和管理。
目標 3:簡單易用
這是最重要的目標,簡單和易用在某種程度上是矛盾的(感謝 Rich Hickey 的 《Simple Made Easy》,讓我們茅塞頓開)。我們意識到,Linkerd 是一款面向運維的產品,也就是說,這並不是一個雲廠商代你運維的服務網格產品,我們希望你會自己運行自己的 Linkerd。這樣的話,減小 Linkerd 的運維面積是一個重點。幸運的是,幾年來幫助用戶採納 Linkerd 1.x 的經驗,給我們很大幫助:
- Linkerd 不應藏在幕後或過於神奇。
- Linkerd 的內部狀態應該是可觀察的。
- Linkerd 的組件應該具備良好的定義、充分解耦並具備清晰的邊界。
為了這個目標,我們在服務中做了很多的設計決策。我們放棄了把控制平面集成到單一進程中的嘗試,而是使用其自然形態進行了邊界拆分:一個提供 Web 界面的 Web 服務;一個 Proxy API 服務來和數據平面進行通信等。我們在 Linkerd 的儀表盤上向用戶直接公開了這些組件,我們還設計了符合 Kubernetes 生態系統中慣用的儀表盤和命令行客戶端的界面:linkerd install 命令會輸出一個 Kubernetes 清單文件,使用 kubectl apply 提交之後就能進行安裝,Linkerd 儀表盤的觀感和 Kubernetes 儀表盤類似。
我們還用加入約束的方式來避免複雜性。我們在 Kubernetes 核心名詞,例如 Deployment 和 Pod 的基礎上進行工作,儘量少定義自己的名詞。我們儘可能的使用 Kubernetes 的自有功能,例如 Secret 和 Admission Controller。我們限制對 CRD 的使用,因為我們深知,CRD 也是重要的複雜性的來源。
最後,我們加入了擴展檢測,讓運維人員能夠觀測到 Linkerd 的內部狀態並進行校驗。我們把控制平面也加入了網格,這樣運維人員就可以使用 Linkerd 豐富的遙測數據來觀測和了解 Linkerd 的內部狀態了。我們還加入了 linkerd endpoints 命令,用於導出 Linkerd 的內部服務發現信息,以及用於驗證 Kubernetes 集群和 Linkerd 設置的 linkerd check 命令。
簡而言之,我們盡了最大的努力,希望 Linkerd 能夠清晰可見,而非簡陋或奇幻。
今天的 Linkerd 2.0
在經過了接近一年的內部投入之後,在 2018 年 9 月,我們啟動了 Linkerd 2.0。雖然有著基本相同的價值主張,但是我們對易用性、可運維以及降低資源用量的關注,催生了一個和 1.x 看截然不同的產品。六個月後,我們已經得到了回報,許多無法採用 1.x 的用戶已經開始採用 2.x。
目前 Linkerd 的用戶和貢獻者社區正在蓬勃發展,未來一片光明。2.x 分支有 50 多個貢獻者,每週都能規律的完成發佈,還有積極友好的 Slack 頻道可以進行溝通。我們為我們的努力深感自豪,並期待為我們的用戶繼續解決實際問題,同時也會繼續堅持我們的設計理念。
閱讀更多 偽架構師 的文章