昨天寫了一篇<微服務的時間和成本去哪兒了>,有人在底下留言:
我的回答是:
"微服務可以不用服務發現和負載均衡嗎?它是微服務一個核心組件。怎么能說沒有關系?"
我覺得有必要來思考和總結一下服務發現技術是如何演進的。於是周末一通閱讀和消化,希望能掰開揉碎在這里講一下服務發現技術的演進歷史。
催生的背景
為了提升研發效能,賦能業務規模化創新。不管是一線互聯網企業還是傳統互聯網企業,將單塊架構解耦成微服務架構,已經成為企業目前數字化轉型的一個大趨勢。
微服務架構下的服務少則幾個,幾十個,多則上百個,每個服務一般都以集群HA的方式進行部署。
這個時候就出現了兩個問題:
- 一個是服務發現,也就是服務的消費方Consumer如何找到服務的提供方Provider。
- 另外一個是負載均衡,服務消費方如何以負載策略去訪問服務當中的服務提供方的實例。
通用解決思路~代理(proxy)
在服務發現演進過程中,先后出現了三代服務發現解決方案。這三代的核心都是代理,只不過代理在架構中出現的位置是不同的。通過在消費方和提供方中間增加一層代理,由代理來處理服務發現和負載均衡等功能,消費方通過代理間接去訪問服務實例。
三代服務發現方案
第一代:傳統集中式代理
- 該方案比較簡單,在消費和提供方中間部署一層代理服務。
- 常用的集中制代理有硬件負載均衡器,比如F5/A10;或者軟件的負載均衡器,比如Nginx,HAPROXY。也可以是軟硬結合,比如前面是F5,后面是Nginx,這種方法兼顧了配置的靈活性,因為Ngix比F5更容易配置。
- 這種配置需要引入DNS服務域名進行配合,每個服務都要申請注冊域名,並且每個域名都要在代理上配置域名和服務IP的映射關系。
- DNS和代理的配置一般由運維人員手工完成。
- 國內外的公司采用這種方式的有ebay,攜程,拍拍貸。
- 缺點是手工配置,效率不高也缺乏靈活性,對開發人員不友好。
第二代:客戶端嵌入式代理
隨着微服務和雲技術的興起,企業對服務發現的效率和靈活性提出了更高的要求,於是出現了第二代方案。
- 該方案將服務發現和負載均衡以客戶庫Library的形式嵌入到應用或服務程序中。
- 該方案需要獨立的服務注冊中心配合,服務啟動的時候將服務自動注冊到中心,並且定期的報心跳進行保活。客戶端通過服務注冊中心發現服務的ip列表,通過某種負載均衡策略選擇某個服務進行調用,這種對開發人員比較友好,可以做到開發自助,不需要太多的運維介入。
這種做法也是很多互聯網公司的一個主流,相應的開源也很多。比如:
- eureka就是一個注冊中心,配套ribbon客戶端代理。
- dubbo和近期開源的nacos
- consul+ribbon
缺點:
- 客戶端依賴語言棧,不同的語言需要開發不同的客戶端,在微服務下可能出現的多語言問題,顯然開發成本太高。
- 另外嵌入式代理也給客戶端增加了復雜性
第三代:主機獨立進程方案。
隨着容器和雲原生技術的興起,業界開始探索第三代的解決方案。
主機獨立進程方案是上面兩種方案的折中,代理既不集中部署,也不嵌入在客戶的應用程序當中,而是作為獨立的進程部署在每個主機上,這個主機可以是物理的或者虛擬的。一個主機上的多個消費者可以共享這個代理,實現服務發現和負載均衡。
第三代結構和第二代是類似的,也需要引入服務注冊中心進行配合,三代之間只不過代理的位置發生了變化。
這個方案目前有個更時髦的稱謂叫ServiceMesh。
開源產品:Envoy,Linkerd,Istio對應到服務注冊中心,當然K8S也內置支持服務發現機制,也是屬於第三代的主機獨立進程方案。
K8S服務發現機制
考慮到K8S在業界比較火,而且內部服務發現機制比較復雜,這里獨立出來就進行剖析。
如上圖所示,在K8S中一個服務是由一組Pod構成的服務集群。
- Pod是K8S當中最基本的調度單位,它相當於K8S集群當中虛擬機的概念。
- 每個Pod都有一個PodIP,並且相互之間是通過PodIP相互訪問,但是服務的PodIP在K8S當中是不固定的,可能會變(包括預期和非預期)。
- 為了屏蔽這種變化,K8S引入了服務Service這個概念,一方面實現服務發現和負載均衡,另一方面屏蔽PodIP可能的變更。
- 在服務發布的時候,K8S會為每個服務分配一個虛擬的ClusterIP
在K8S的worker節點上,有kubelet和kube-proxy,其中后者是實現服務發現的關鍵,上面是簡化的服務發現流程。
服務注冊階段:
- 其中kubelet負責啟動Pod服務實例--->
- 啟動完成后kubelet會把Pod的IP列表注冊到Master節點上-->
- 最后通過服務Service的發布,K8S會為服務分配相應的ClusterIP,相關信息也會記錄在Master上。
服務發現階段:
- kube-proxy會發現clusterIP和podIP列表之間的映射關系,並且修改本地iptables的轉發規則,進行負載均衡和轉發到對應的PodIP
- 當有消費者Pod要訪問某個目標服務實例Pod的時候,通過ClusterIP發起調用,這個ClusterIP會被本地的Iptables機制截獲,然后進行負載均衡,轉發到指定的Pod實例。
- 實際消費這Pod也是通過服務名進行訪問ClusterIP,因為ClusterIP本身也會變,為了屏蔽變化,K8S還引入了kubeDNS這個概念,它通過master可以發現服務名和ClusterIP之間的映射關系。這樣消費者Pod通過kubeDNS間接的發現服務的ClusterIP。
比較總結和選型
三種方案各有利弊,沒有絕對的好壞。他們都有大規模的成功落地的案例。架構師需要在理解這些方案優劣的基礎上,根據企業的實際上下文,綜合考量,做出技術選型。