您現在的位置是:首頁 > 人文

5G新基建 邊緣計算乘風破浪

由 阿里云云棲號 發表于 人文2022-05-23
簡介侵入性與語言綁架這可能是最大的問題,基本只能使用 Java 作為研發語言,這一點在國內也備受爭議,因為不論是作為架構師還是入門的程式設計師,都需要嘗試新的技術棧來進行儲備或是採用新的功能,而且比如使用自制的 client 去實現 ribbo

limitation是什麼意思

5G新基建 邊緣計算乘風破浪

作者 | 張羽辰(同昭)阿里雲交付專家

導讀

:如今,幾乎所有的事情都離不開軟體,當你開車時,腳踩上油門,實際上是車載計算機透過力度感應等計算輸出功率,最終來控制油門,你從未想過這會是某個工程師的程式碼。

當我們談論架構時,我們到底在談論什麼?

面向物件程式設計?函式式?模組化設計?微服務?這些詞彙貌似都和架構這個 buzzword 有點關係,的確我們這個領域充滿了很多難以理解的詞彙,這些詞彙從英語翻譯到中文已經喪失了部分上下文,再隨著上下文的改變使得意義徹底扭曲,比如:引擎、框架、架構、應用、系統……誠然大家都或多或少對這些詞語達成共識,在工作中使用這些詞彙進行溝通,某時就是指“我們都懂的那個東西”,但是在我深入的想聊聊架構或者說軟體架構時,的確不得不問自己這個問題,我們到底是談論什麼?

事實上,架構這個詞根據上下文所確定的範圍較為固定,建築學上的架構指代房屋結構、整體設計、組合構成等,而這些 high-level 設計往往並不需要全面瞭解底層,就像使用 RestTemplate 進行 WebService 呼叫時,我們也不關心 socket 是在四層連線的一樣,

因為細節被隱藏了

但是,建築學上的架構與軟體架構卻又極大的不同之處,問題出現在“軟體”這個詞上,按照 software 的詞解,ware 是指產品一樣的東西,而 soft 則強調易變,這是與 hardware 所對應的。我們希望“軟體”能夠進行快速的修改,應該能夠快速響應甲方或者客戶的需求,所以軟體架構必然不像建築架構一樣,建築一經建成,修改的成本極高,

而軟體應該走對應的方向,發揮易於修改的特點。

5G新基建 邊緣計算乘風破浪

“現在的大多數軟體非常像埃及金字塔,在彼此之間堆建了成千上萬的磚塊,缺乏結構完整性,只是靠蠻力和成千上萬的奴隸完成。” —— Alan Kay。

筆者認為,雖然這句話表達的意思我很贊同,但實際上,金字塔作為帝王的陵墓,是有著完整的設計邏輯,並且隨著好幾座金字塔的迭代的,以及逐漸完備的施工管理,後期金字塔是非常傑出的建築代表,並作為地球上最高的人造建築持續了好幾千年。關於金字塔是否由奴隸建造還是存有爭議。(圖片來自 Isabella Jusková @ Unsplash)。

作為工程師,我們一方面關注軟體產品的能力和行為,這往往是一個專案的起點,另一方面我們需要關注軟體的架構設計,因為我們希望設計有著彈性、易於維護、高效能、高可用的系統,更希望系統能夠不斷演進,而不是在未來被推倒重做。所以,回正我們的視野,當我們決心要設計一個好的架構時,我們需要明確,

架構往往決定的是軟體的非功能性需求

。這些非功能性需求有:

易於開發

:我們希望工程師一進入團隊就可以立刻開始進行研發工作,我們希望程式碼易於閱讀與理解,同時開發環境足夠簡單統一,說到這裡大家可以回想下當你進入專案時,學習上下文的痛苦。當我們開始採用 docker 輔助開發時,時任架構師提出了一個要求,只要一行命令就可以使用 docker 啟動本地測試環境,而且必須所有的微服務都要做到這一點。痛苦的改造完成後,三年後進入專案的同學只需要安裝好 docker,再在 ternimal 中執行一句 。/run-dev。sh 就能夠獲取一個具有完整依賴的本地環境,提效明顯。

方便部署

:如果系統的部署成本很高,那使用價值就不會很高了,我們很多企業都存在那種動也不敢動,改也不敢改,停也不敢停的系統,除了祈禱它別掛掉好像沒有別的辦法,或者很多企業都採用了 K8s 這種先進的編排系統,但是在應用部署和上線時,還是走的每週四變更的路子。現代的釋出方式 AB、金絲雀、灰度無法採用是因為改造成本過高,或者沒有足夠的自動化測試來保證改動安全,更別提將釋出做到 CICD 裡面了。

易於運維

:DevOps 的初衷是建立一種縮短運維與研發距離的文化,讓出現問題後更容易處理,希望讓大家將視野放在產品上而不是限定自己的工種,這並不是期望運維的同學能夠成為 Java 專家,迅速的進行 heap 分析發現問題,我們強調的是運維時的閉環能力。在軟體產品層面,我們也希望產品是足夠獨立的、自治,可以獨立部署,能夠做到橫向擴充套件,有著完整的可觀測性,畢竟當今的硬體成本很多時候是遠遠小於人力的。

維護成本

:隨著時間的推移,給軟體增加新功能就會變的越來越難,越是執行長久的專案就會陷入重寫還是重構的苦惱。往往風險在與,修改程式碼會增加破壞已有功能的風險,而且技術債也會越來越多難以償還,即使是重寫某些功能和模組,我們也很難確定是否真的覆蓋到了所有的功能,簡而言之,don‘t break anything 的確很難做到。

以及最重要的一點,

演進能力

:良好的架構設計應該能讓系統處於易於演進的狀態,能夠實現給飛馳的汽車換輪胎的能力,而不會被框架、底層的某種資料庫、作業系統或者其他東西所綁架,但是這太難以做到了。的確,在專案進行技術選型時,因為某種資料庫的特性而有傾向,但是在上層設計中,我們必須保證不依賴於資料庫的特性,而將使用這些特性的地方放到底層細節中。我們也需要考慮,不使用 Spring 提供的 Dependency Injection,我們該如何組織我們的 beans,也要考慮將來系統的前端是 web 還是 mobile 還是都要支援?

這裡引用 Robert C·Martin(Uncle Bob)的原語,“軟體產品是有兩方面的價值,一方面是實現功能的價值,另一方面是架構的價值,

而架構的價值可能更重要一些,因為它代表著軟體 soft 的特性。

5G新基建 邊緣計算乘風破浪

本書例子過少,而且缺乏現有流行框架的重構或者改進建議,有點形而上,但是在方法論層面筆者還是認為值得一讀。Robert C·Martin 對資料庫(特指 RDBMS)的態度很值得討論,首先他認為資料庫是一種細節,在架構中應該與業務解耦,他強調業務程式碼與資料庫的無關性。同時在我們的程式碼進行計算時,表格往往不是理想的資料結構,比如有些場景會使用樹、DAG 等等。可以回想一下,當你需要把一個樹存入資料庫時,你該如何實現?

微服務是一種軟體架構,不要擴充套件它

根據我們之前的討論,後端系統採用微服務是不會影響到其功能上的價值,本質上微服務化和單體應用的差別並不會表達在功能上,很多微服務進展不順利的同學會經常說到:這東西用單體寫早就完事兒!的確是這樣,

這側面也印證了微服務只是一種軟體架構,而不是別的神奇的東西,並不是某個業務需求必須要使用微服務完成,我們看中微服務,也是看中了架構方面的優勢,即那些非功能性需求

。也有人使用 pattern 來描述它,也有人說和 SOA 基本上是一個東西,只是粒度不同,所以我們一開始就別相信這個世界有靈丹妙藥,也別期望有個什麼技術能夠瞬間替代 Oracle。

作為開發“企業級後端應用”的同學,我們經常會面臨很多非業務需求上的苦惱:有時我們需要同時支援移動端、移動 web、桌面端三種客戶端;有時候我們需要支援不同的協議比如 JSON 或 XML;有時我們又需要使用不同的中介軟體傳遞訊息;或者在研發時,我們知道有一個地方寫的不好,我們想在未來補課重構;我們想嘗試最新的技術但是代價過高;系統無法擴容,或者成本極高;系統過於複雜無法在本地執行導致極低的效率……這些苦惱才是採用微服務的主要驅動力,回到我們對軟體架構的討論之中,我們希望的是透過足夠松耦合的獨立服務,來降低元件之間變化的成本,也就是說今天更新發送通知的功能,並不會影響到使用者檢視購物車,也不會讓研發人員半天改完,再等三天才能上線。

但是世界上沒有免費的午餐,雖然我們知道微服務有很多很好的特性,比如元件即服務、松耦合、獨立部署、面向業務、高維護性、高擴充套件性等等,這裡並不想展開討論它的好處,我們先考慮投入成本。假設我們每個同學都完整的學習了微服務的所有知識,對市面上的框架、產品非常熟悉,摩拳擦掌準備開始,在拆解完幾個服務後,我們會發現,沒有足夠的自動化手段,靠手動的方式進行測試、編譯、部署、監控,這是顯而易見的會降低體驗,如果沒有最佳化好的部署策略,所有的服務都在某個釋出日上線,那更是一種災難。

5G新基建 邊緣計算乘風破浪

隨著規模的擴大,單體應用的程式碼改動成本會越來越大。很多時候我們微服務的架構實踐是存在誤區的,我們總認為流量經過某個 gateway 後直達某個服務,確忽視了服務之間呼叫的場景,理想的微服務架構應該是一張網,每個節點都是獨立的、自治的服務。

一些之前使用單體很容易做到的場景,在分散式的環境下會更加困難。比如我們可以透過 RDBMS 提供的資料庫事務來支撐一致性,但是如果訂單服務和價格服務分離,勢必要進行分散式事務來保證一致性(往往是最終一致性),而分散式事務的成本和難度就不用贅述了。在單體環境下,我們可以很輕鬆的使用切面進行許可權驗證,而在微服務的場景中,服務之間相互呼叫是難以控制的。

拆分服務或者服務邊界劃分是另一件很難做到的事情,最吃香的理論也許是根據 DDD 去進行劃分,天然的領域或者子域(domain)貌似都能對應一個服務,因為足夠的界限上下文(bounded context)能夠保持服務的獨立性,使其細節被隱藏在界限之內,聽起來是個不錯的主意。但是現實卻十分殘酷,使用 DDD 生搬硬套去進行軟體開發的例子不在少數,成功例子也難以複製。

雖然我在實踐中也經常使用業務領域去進行服務劃分,但是我並不認為這是 DDD 的做法,沒有必要規定有多少個 domain 就有多少服務,也不需要規定 sub domain 能否獨立服務。

與其進行頂層設計一攬子的解決方案,我更相信演進的力量,如果你真的需要拆分一個服務,足夠的基礎設施與自動化工具應該允許你低成本的去做,而不是一開始就畫好所有的架構圖

。這就跟所有的改革一樣,革命派往往不是一步功成,而是逐漸的積累的。所以使用微服務,當你能夠負擔的起(only you can afford it),也表示你能負擔的失敗一樣,技術世界不存在一蹴而就,all in 非常危險。

衛報網站(Guardian)的微服務改造就是一個很好的例子,網站核心依舊是一個巨大的單體,但是新功能透過微服務實現,這些微服務呼叫單體所提供的 API 來完成功能。對於常常出現的市場活動(比如某個體育比賽的專用板塊),這種方式能夠快速實現活動頁面與功能,完成業務需求,並在活動結束後刪除或丟棄。我之前參與專案中,也透過等量替換與重構,慢慢絞殺(Strangler Pattern)掉一個巨大的陳舊的 JBoss 應用。

PlayStation 首席設計師 Mark Cerny 在今年的 PS5 新主機的技術分享中提到,遊戲主機需要平衡好演進與革命(balance the evolution and revolution),我們不想丟掉多年來開發者的積累,在複用過去的成功經驗時,我們也希望大家能夠使用更先進的技術。

人人都愛恨 Spring Cloud

看起來,在 Java 世界中,Spring Cloud 貌似是微服務的最優解了,甚至在很多同學的簡歷上,Spring Cloud 幾乎可以和微服務劃等號了,不止一次的有人告訴我說:公司的技術棧不是 Java,所以搞不了微服務很難受,並不是我沒有學習精神和冒險精神云云。很遺憾,對於軟體架構來說,跟可沒有規定程式語言,設計模式不是也出了很多版本嗎?歸根結底還是 Spring Cloud 的全家桶策略更吸引人,什麼事兒都不如加上幾個 jar 就能擁有的神奇次時代架構更有吸引力。

不可否認,我在學習 Spring Cloud 的時候也驚歎其完整性,幾乎常見的微服務需求都有足夠完整的解決方案,而大多數方案是做在應用層,具有良好的適配性,比如 eurake 的註冊發現、zuul 閘道器與路由、config service、hystrix circuit breaker 等等,透過統一的程式設計正規化(基於 annotation 的注入與配置),足夠豐富的功能選擇(常用功能甚至都有兩種選擇),以及較好的整合方式。前有 Netflix 的成功經歷,後隨著微服務的浪潮,再加上足夠龐大的 Java 社群,可以說是王道中的王道。但並非 Spring Cloud 沒有弱點,反倒這些功能設計與隨後的容器化浪潮產生了分歧,至今融合 Spring Cloud 與 Kubernetes 都是熱門話題,這裡我們展開說說它的不足或者限制(limitation)。

侵入性與語言綁架

這可能是最大的問題,基本只能使用 Java 作為研發語言,這一點在國內也備受爭議,因為不論是作為架構師還是入門的程式設計師,都需要嘗試新的技術棧來進行儲備或是採用新的功能,而且比如使用自制的 client 去實現 ribbon 的負載均衡也是很難的,但是如果不用 Java,做到這一點也很難,不是說 Java 語言不夠優秀,而是我們對未來應該有更多的選擇,對於一個技術公司來說程式語言應該不會成為限制,試問這個時代誰不想學習一點 golang 或者 rust 或者 scala 呢?其他服務比如 SSO、Config Service 也過於整體,如果想進行某項適配,則必須進行大量的修改(還好是開源的)。我們很擔心這種情況都會隨著框架的老去而面臨推到重來的境界,Ruby on Rails 可能就是前車之鑑吧。侵入性是另一個問題,還記得我們在討論軟體架構時所提倡的實踐規則嗎?儘量不要讓頂層設計依賴底層的框架或者某種細節,但是滿螢幕的 annotation 與 jar 的直接引用,無疑告訴我們想去掉它們還是非常難的。

雲原生的融合問題

對於雲原生,不論是 CNCF 還是 Pivotal (VMWare)都在強調容器化、微服務、面向雲環境等,CNCF 圍繞 Kubernetes 開始發展壯大,也隨著這種先進的容器編排技術的流行人們漸漸發現它和 Spring Cloud 在功能上還是存在很多重疊,雖然 k8s 與 IaaS 沒有重疊,但是現在還有多少廠商再推純 IaaS 呢?既然有功能重疊,就有取捨,考慮到 Spring Cloud 的全家桶屬性,這個分歧處理一直都不能很好的解決。

5G新基建 邊緣計算乘風破浪

集中式的資源

不論是 Config 、Eureka 都是聚合的單點,及時它們有叢集的方式達到近乎 100% 的可靠性,但在邏輯架構上,所有的微服務都依賴它們,這些集中式的資源的耦合是非常強的,它們會一直存在在你的生產環境之中,直到最後一個使用它們的系統下線。我們在架構中需要避免使用共享的例項與資源,一個應用不會因為不能寫日誌而崩潰,也不應該因為本地沒有 eureka 而無法啟動。

畏懼平臺繫結

誠然,在程序之內解決服務註冊發現、負載均衡是很好的,它代表了最好了平臺無關性,但平臺的其他能力也很難享受的到了。繫結 k8s 貌似是個更好的選擇,因為相對於 Spring Cloud 它更靈活,也能做到不會被基礎的雲平臺繫結,但也更難以掌握與運維。當然我也不是認為 K8s 必須作為微服務的選擇,作為容器的編排平臺,它可以做更多的事情(比如跑資料庫、中介軟體等),執行微服務應用只是其中之一。

方法論的落地能力

2020年已經過了一半,從技術上來說,Serverless 已經進入成熟期,Kubernetes 也更加成熟,已經成為事實的標準。但是很多時候我們的方法論與架構設計是跟不上的技術發展的,很多同學可能還在經歷每週的釋出日,很多同學還沒辦法改進團隊內老舊的技術,很多同學的 Jenkins 還是停留在打包的階段,很多同學機器上還是沒有安裝 docker,很多時候並不是框架或者平臺的問題,而是方法論還停留在過去。

保持演進

應用程式也應該踐行開閉原則,對擴充套件開放使得我們在未來有更多的選擇。

微服務是一個很好的機會能讓我們真正的演進架構而不需要付出過多的代價,當我們需要元件化系統時,元件的關鍵特性正是可獨立替換或升級,我們可以不影響其他部分去進行替換和重構,這樣的成本是顯然低於拋棄舊的巨型框架而重寫的

。有著正確的態度和工具,我們可以更快、更頻繁的控制變更,我們可以激進的選擇新的技術棧,也可以合併兩個耦合過緊的服務,隨著服務的不斷聚合、抽出,你會發現系統的邏輯架構會越來越清楚,再進行修改就會信心倍增了。我們可以針對每個服務使用不同的儲存技術,我們可以使用 OSS 處理檔案,而不是繼續往 Oracle 裡面塞圖片和影片。

Istio 的解法與問題,以及 Mesh 還缺少什麼

5G新基建 邊緣計算乘風破浪

這個開幕雷擊雖然槽點滿滿,但並沒有降低社群對 Istio 的信心,反倒是漸漸發現這次的大改動使 Istio 變得有點好用了,可以在生產中採用而不需要付出太多代價了。當然,漂亮話永遠好說。

2017 年的時候 Service Mesh 還是一個襁褓中的概念,現在已經成為了微服務領域的未來之選,但遺憾的是目前只有 Istio 足夠成熟能代表這項技術,當然我也有幸實踐過類似的 Sidecar 來進行反向代理、日誌收集、效能監控、健康檢查等功能,但是距離 Mesh 的願景還是有大的差距。

今天並不想展開 Service Mesh 或者 Istio 的優勢,程序之外的解決方案能夠確保系統的靈活性,而流量控制、服務治理、端對端的傳輸安全、限流、發現註冊等等,

我們希望工程師能夠聚焦業務,實現架構的靈活性,聚焦真正的價值,而剩下的進行配置就好

。所以我想這也是 Serverless 被無限看好的原因,既然我們想 delegate 對基礎設施的控制,那為什麼還需要關心容器呢?Istio + k8s 的方案已經足夠好,至少我們團隊正在認真學習,在向客戶提供最佳實踐之前,我們依舊有很多問題需要解決,下面列出一些代表性的:

彈性與自恢復問題

使用系統度量、引數等觸發彈性伸縮是常見的需求,這裡我們是使用雲監控?還是自己搭一套?我一直傾向資料與用途解耦,使用 pub sub 模式解決問題,比如 CPU 過高可能會觸發多個行為:觸發警報,觸發彈性規則,展示在 dashboard 上。我們可以增加 pod 來分擔服務的壓力,或者因為某個 pod 異常退出後,啟動新的 pod 來完成自恢復,這系列動作也是需要我們自己解決的。

監控與可觀測性

日誌、警報等可觀測性的問題,這一方面的實踐較多,唯一比較擔心的是 ARMS 或者 Newrelic 這種 APM 功能目前沒在目標平臺上實踐過,我們希望能夠清晰的看到每個服務的實時效能,目前這一部分還缺乏考慮與設計。

限流與降級的實踐

Istio 的流量控制能力是非常強大的,如何對服務採取降級、限流這種常見的治理操作,也是需要總結出實踐經驗的。避免串流錯誤(cascade failure)在微服務領域也很常見,也需要避免故障蔓延。

多種自動化部署

藍綠、灰度、金絲雀,這些多樣的部署方式也需要落地,我們可能會寫一些 deployment util 之類的小指令碼,部署的方式需要和 CICD 打通。一個部署工具,一個配置檔案,一個 CICD 組成未來單個應用的部署方式。

ABAC 與 OPA

基於屬性的許可權控制以及 OPA 的可定製性我們是非常看好的,而且 sidecar 可以在程序之外解決許可權驗證問題,的確值得嘗試,剛好也學習下 golang 用於定製 OPA。

Saga 與補償事件

分散式事務是微服務實踐中的大坑,我曾經也寫過類似的文章,專案經驗中更多的是將事務放在單一的服務內,再加上我自己也沒機會寫過真正的網店系統。補償事件也是常用的最終一致性方案,總之放在一起驗證。

高可用和混沌工程

基於 K8s 實現應用的高可用應該不難,容器的天生優勢就是易於啟動與管理,如果隨機殺掉 pod 不會影響系統可用性,這就算是實現了類似於 SLB + ECS + ASG 的能力,真正危險的是其他 非業務型 pod,比如 istio 的各種 supervisor。

5G新基建 邊緣計算乘風破浪

實踐微服務,作為架構師所考慮的東西遠遠大於只是實現業務,但是一旦鋪平道路,下來的研發與迭代將會更加順利。

推薦文章