基于Kubernetes的微服務架構,你學會了嗎?
2017年,這一年在容器技術發展史上具有重要的分水嶺意義,標志著“后微服務時代”的開始。這一年,發生了幾件重大事件,徹底改變了容器管理領域的格局。
首先是 CoreOS,一直以來與 Docker 競爭的 RKT 容器技術的領頭羊,宣布放棄其容器管理系統 Fleet,轉而支持 Kubernetes。接著,容器管理行業的領頭羊 Rancher Labs 也放棄了自家多年開發的 Cattle 系統,采納“All-in-Kubernetes”的策略,從此 Rancher 2.0 版本起只支持 Kubernetes。
同年,Kubernetes 的主要對手 Apache Mesos 宣布了與 Kubernetes 的集成計劃,“Kubernetes on Mesos”,這標志著他們從競爭對手轉變為支持者,讓 Kubernetes 能夠與 Mesos 的其他頂級框架(例如 HDFS、Spark 和 Chronos 等)實現資源的動態共享和隔離。
此外,2017年10月,Docker 的母公司,也是 Kubernetes 最大的競爭對手之一,宣布 Docker 將同時支持 Swarm 和 Kubernetes,這在事實上承認了 Kubernetes 的領導地位。
這些事件標志著長達三至四年的容器技術競爭戰爭,主要圍繞 Docker Swarm、Apache Mesos 和 Kubernetes,最終以 Kubernetes 的勝利告終。Kubernetes 的崛起不僅是容器技術發展的一個重要里程碑,也預示著軟件架構發展新紀元的到來。
需求場景
在采用了基于 Spring Cloud 的微服務架構之后,小書店 Fenix's Bookstore(虛擬) 成功地應對了伸縮性、獨立部署、運維管理等方面的挑戰,以及產品經理提出的日益增長的復雜業務需求。然而,對于團隊中的開發者、設計師和架構師來說,工作并沒有變得更加輕松。微服務所涉及的新技術術語,如配置中心、服務發現、網關、熔斷、負載均衡等,對新手來說學習曲線陡峭。而從產品的角度看,Spring Cloud 的各種組件,如 Config、Eureka、Zuul、Hystrix、Ribbon、Feign 等,也構成了產品編譯后代碼的主要部分。
微服務架構選擇在應用層面解決分布式問題,而不是在基礎設施層面,主要是因為軟件的應用服務比硬件基礎設施更加靈活,而后者難以跟上前者的步伐。但是,隨著 Kubernetes 在容器編排管理方面的統一,這些技術性的底層問題開始在基礎設施層面找到了廣泛認可的解決方案。因此,Fenix's Bookstore 開始了它在“后微服務時代”的又一次架構升級,此次升級主要集中在兩個目標上。
Fenix's Bookstore 的微服務架構升級主要圍繞兩個核心目標進行。第一個目標是減少非業務功能代碼的占比。在這家書店的系統中,用戶服務(Account)、商品服務(Warehouse)和交易服務(Payment)是承擔實際業務邏輯的核心模塊。而認證授權服務(Security)則同時涉及技術和業務層面,配置中心(Configuration)、網關(Gateway)和服務注冊中心(Registry)則完全是技術性質的組件。目的是盡可能地消除這些純技術組件,以及那些附屬于其他業務模塊的技術性功能。
第二個目標是在盡量不改變原有代碼的情況下完成遷移。依托于 Spring Framework 4 中的 Conditional Bean 等聲明式編程特性,現代 Java 技術組件越來越傾向于聲明式(Declarative Programming)而非命令式編程(Imperative Programming)。這樣的編程風格允許開發者從目標出發描述編碼意圖,而不是圍繞具體的技術實現過程,從而減少代碼與技術實現的耦合。如果需要更換技術實現,只需調整配置聲明即可。
從升級的角度看,如果僅以 Java 代碼來衡量,這次遷移到 Kubernetes 后的項目與之前基于 Spring Cloud 的版本在代碼層面上沒有任何區別,每一行 Java 代碼都保持不變。但實際上,區別在于 Kubernetes 實現中直接刪除了配置中心和服務注冊中心等工程,在其他工程的 pom.xml 文件中也移除了像 Eureka、Ribbon、Config 等組件的依賴。取而代之的是引入了一系列以 YAML 配置文件為基礎的 Skaffold 和 Kubernetes 資源描述。這些資源描述文件將動態構建出 DNS 服務器、服務負載均衡器等虛擬化基礎設施,替代了原有應用層面的技術組件。升級后的應用架構如下圖所示:
圖片
技術組件
Fenix's Bookstore 采用基于 Kubernetes 的微服務架構,并采用 Spring Cloud Kubernetes 做了適配,其中主要的技術組件包括以下幾種。
在 Fenix's Bookstore 的微服務架構中,為了實現容器環境的感知,我們使用了Spring Cloud Kubernetes,它集成了 Fabric8 的 Kubernetes Client。但是,Spring Cloud Kubernetes 版本 1.1.2 中使用的 Fabric8 Kubernetes Client 版本是 4.4.1,這個版本根據 Fabric8 提供的兼容性列表,僅支持到 Kubernetes 1.14。盡管在 Kubernetes 1.16 上也能運行,但在 1.18 版本上就無法正確識別 Api-Server。因此,在 Maven 項目中添加依賴時,需要手動排除舊版本,并引入更新的版本(在這個項目中使用的是 4.10.1)。
配置管理方面,項目利用 Kubernetes 的 ConfigMap 來進行配置管理,并通過 Spring Cloud Kubernetes Config 自動地將 ConfigMap 的內容注入到 Spring 的配置文件中,實現動態更新。服務發現是通過 Kubernetes 的 Service 實現的,Spring Cloud Kubernetes Discovery 能自動將 HTTP 服務請求轉換為完全限定域名(FQDN)。負載均衡方面,則直接利用 Kubernetes Service 的內置負載均衡功能(即 DNS 負載均衡),因此不再需要像 Ribbon 這樣的客戶端負載均衡組件。從 Spring Cloud Kubernetes 1.1.2 開始,Ribbon 的適配支持已被移除,且暫時沒有對 Spring Cloud LoadBalancer 這一替代品提供適配。
至于服務網關,雖然保留了 Zuul,但沒有采用 Kubernetes 的 Ingress 來替代。這里有兩個主要考慮因素:首先,Ingress Controller 并非 Kubernetes 的內置組件,有多種可選方案(例如 KONG、Nginx、Haproxy 等),且需要獨立安裝。為了保持演示項目的環境簡單,我選擇不使用 Ingress;其次,考慮到 Fenix's Bookstore 的前端項目存放在網關中,即使移除了 Zuul,仍然需要保持一個前端項目的存在。因此,移除 Zuul 并不能進一步減少項目數量,這減弱了移除 Zuul 的必要性
在 Fenix's Bookstore 的微服務架構更新中,服務熔斷功能仍然由 Hystrix 實現。由于 Kubernetes 本身不提供細致的服務治理能力,如熔斷、流量控制和監控等,我們計劃在后續基于 Istio 的服務網格架構中解決這些問題。在認證授權方面,我們繼續使用 Spring Security OAuth 2.0。雖然 Kubernetes 的 RBAC(基于角色的訪問控制)能夠處理服務層面的訪問控制問題,但 Spring Security 跨越了業務和技術的界限,其認證授權模塊仍然負責前端用戶的認證和授權,這是與業務直接相關的部分。
它是基于 Apache 2.0 協議授權的。遵守這一許可協議的條件下,你可以自由地對代碼進行修改和重新發布,甚至用于商業目的。但是,你需要遵守以下要求:署名在原始代碼及其衍生代碼中保留原作者的署名和代碼來源信息;保留許可證在原始代碼及其衍生代碼中保留 Apache 2.0 協議文件。