一個實現服務註冊發現原理的擴充套件模式,讓你的service mesh不再是雞肋

作者 | 姜橋       責編 | 歐陽姝黎

出品 | CSDN雲計算(ID:CSDNcloud)

在之前的<<乾貨|如何步入Service Mesh微服務架構時代>>一文中,實戰演練了Service Mesh微服務架構的具體玩法,該案例中透過Istio+Kubernetes的組合,一組以Spring Boot框架開發的服務,在自身沒有實現任何服務註冊發現邏輯的情況下,被部署到Kubernetes後便能透過服務名直接完成服務介面呼叫,並且還能對呼叫進行限流、熔斷及負載均衡等一系列服務治理操作。

這一切是怎麼發生的呢?在今天介紹Service Mesh微服務架構有關服務註冊發現原理的文章中將為大家揭曉答案。

傳統微服務註冊中心實現機制

在闡述Service Mesh服務註冊發現機制前,先簡單回顧下在以Spring Cloud為代表的傳統微服務中是如何實現服務註冊與發現的。

傳統微服務體系中,註冊中心無疑是整個微服務體系最重要的組成部分,沒有服務註冊中心提供的統一服務註冊發現功能,微服務本身就無從談起。不過相較於Service Mesh架構中服務註冊發現,很大程度上是依賴於基礎設施(例如資料面[envoy]、控制面[istio]以及Kubernetes叢集),而無需微服務親力親為。在Spring Cloud傳統架構中,服務註冊、發現及健康性檢查等服務治理邏輯則都需要微服務自己上下打點。

雖然不用重複造輪子,都有現成的服務治理元件及框架,但從應用執行形態上說,與服務註冊發現相關的邏輯都是微服務直接與註冊中心產生的互動。從系統架構上看,這種方式顯然是將服務治理邏輯與業務應用耦合了,其執行邏輯如下圖所示:

一個實現服務註冊發現原理的擴充套件模式,讓你的service mesh不再是雞肋

從上圖可以看到,微服務啟動時會透過整合的服務發現SDK,向註冊中心(應用配置的註冊中心地址)傳送註冊資訊,註冊中心在收到服務註冊請求後將儲存服務的基本資訊;之後,如有服務消費者要呼叫該服務,那麼呼叫方透過服務發現元件(例如Ribbon)就可以向註冊中心查詢目標微服務的地址列表,並透過獲取的服務地址列表,以某種負載策略向目標微服務發起呼叫了。

而當服務節點發現變更時,新的節點會被重新註冊,而下線的節點基於服務探活機制也會及時從服務註冊中心被踢除,基於這樣的服務註冊/發現機制,微服務之間的通訊順暢就能得到保證。從這個角度看,註冊中心與服務之間的健康性檢查就顯得很重要,如果註冊中心不能及時將下線或故障的節點從可用伺服器地址列表剔除,那麼就很可能會造成微服務某些呼叫的失敗。

那麼一般怎樣進行服務健康性檢查呢?從方式上看,主流的健康性檢查方式,主要有以下3種:

1、微服務主動探活

服務主動探活就是微服務定期向註冊中心傳送租約資訊以表面自己的存活。在實際場景中主動探活是我們使用註冊中心時用得最多的一種方式,如果服務規模不大,或者使用了類似於Eureka這樣的最終一致性註冊中心,那麼主動探活就是一種最佳選擇,它可以較大程度地避免服務部署在Kubernetes集群后,因為Pod IP重用而導致的舊有服務節點依然存活的問題,畢竟續約資訊都是帶著服務基礎資訊上報到註冊中心的。

但這種方式也有明顯的弊端,它會造成註冊中心寫操作壓力變大。如果大量的服務同時釋出,節點產生較大的變動,那麼就可能產生大量的通知事件,從而對整個微服務體系的穩定產生較大影響。

此外,主動續約,也並不完全說明服務是健康的,因為某些特殊情況下可能會存在雖然服務無法對外提供服務,但還能正常向註冊中心發出租約資訊的問題。

2、註冊中心發起健康性檢查

前面分析了微服務主動探活方式的優缺點,而如果由註冊中心發起健康性檢查會怎麼樣呢?

這種方式是指微服務在註冊時,同時向註冊中心暴露自己的健康檢查端點(如/actuator/health),註冊中心透過定期訪問來探測服務節點是否存活。

不過這種方式也不是完全沒有問題,例如前面提到的Pod IP重用問題,如果其他微服務重用了之前節點的IP,那麼就會發生失效節點被啟用的假象。當然也有相應的解決方案,例如後面會講到Istio微服務註冊發現時Envoy會對服務名稱進行二次Check的方式。

3、呼叫方負載均衡器進行健康性檢查

第3種方案比較極端,註冊中心不進行任何服務探活,全部由微服務呼叫方所在的負載均衡器進行探活。這種方案常見的一種場景就是 gRPC 的失效節點自動摘除功能。但這種方案依然具有 IP 被重用問題,所以此種方式在實際的場景中並不是很受歡迎。

前面講了微服務註冊發現常見的三種服務探活方式,其實三種方案各有利弊。以 Spring Cloud 微服務體系中,使用最廣泛的幾種註冊中心為例,如 Eureka、Consul、Nacos,它們的對比如下表所示:

一個實現服務註冊發現原理的擴充套件模式,讓你的service mesh不再是雞肋

從表格中可以看到,除了Eureka主要採用服務主動探活方式外,Consul 及 Nacos都採用了多種服務探活方式,從而儘量規避不同方式的弊端,這也是為什麼目前大部分實踐逐步拋棄Eureka而採用Consul或Nacos的原因。

除了健康性檢查外,上面表格還分別列出了這幾種註冊中心的一致性協議。這裡順帶普及下CAP理論。CAP是分散式系統中重要的一種概念,它分別由

一致性(Consistency)、可用性(Availability)、分割槽容錯性(Partition tolerance)三部分組成。

在CAP理論中,一個分散式系統不可能三種都滿足,一般只能同時滿足其中的兩種,例如Eureka和Nacos只能滿足可用性和分割槽容錯性,而無法滿足一致性;Consul則只能滿足一致性和分割槽容錯性,而無法完全滿足可用性。

在註冊中心的場景中,一致性一般要求並不高,只要能達到最終一致性即可。畢竟在微服務架構中,涉及節點的註冊和反註冊,註冊中心和客戶端之間的通訊需要一定時間,一致性本身也很難達到。所以在註冊中心的選型中,一般會優先選擇AP的系統,這也是目前還在以Spring Cloud構建微服務的實踐中,除了自研外,開源技術中會優先選擇Nacos作為服務註冊中心的原因。

Kubernetes服務註冊與自動發現

前面以一定篇幅回顧了 Spring Cloud 傳統微服務體系中,註冊中心有關的基本邏輯及常見開源技術。在具體講述 Service Mesh 架構中服務註冊發現的邏輯前,有必要先了解下Kubernetes容器編排中,與 Service 服務資源有關的概念。

因為最流行的 Service Mesh 方案(如Istio),大都選擇了與Kubernetes叢集相結合的方案,而其服務註冊邏輯也主要是利用了 Kubernetes 的內部服務發現機制。例如 Istio 就是透過監聽 Kubernetes Pod 的變化來實現服務發現的。當然這並不是說在 Service Mesh 中不能選擇傳統的註冊中心方案,只不過實施起來可能需要改造或自研註冊中心才能滿足需求(相容新舊微服務體系時,可能會這麼考慮)。如果是全新設計的 Service Mesh 微服務架構,最佳的方案還是選擇像 Istio 這樣直接利用 Kubernetes 自身功能實現服務發現。

在 Kubernetes 中能夠為 Service Mesh 微服務提供內部服務註冊發現的基礎就是 Service 型別的資源。Service 是 Kubernetes 抽象出來的服務概念,一組 Pod 的集合就是 Kubernetes 的 Service。在 Kubernetes 中 Pod 是最小的容器編排單元,一個 Pod 編排資源中可以定義多個容器,Pod 內的容器可以透過本地訪問在 Pod 中通訊,一組容器共享一個 Pod IP,這就是 Kubernetes 之所以被稱為容器編排平臺最核心的體現。

但 Pod 是有生命週期的,也就是說 Pod 的 IP 地址並不固定,它會隨著 Pod 生命週期的變化而變化,所以如果以 Pod 為服務呼叫物件,那麼IP地址的經常變化會導致呼叫方服務發現邏輯不穩定,從而使系統變得複雜。為了解決這個問題,Kubernetes 中就抽象出了 Service 資源型別,雖然 Pod的IP地址會變化,但是抽象的 Service 名稱卻是固定的,所以 Kubernetes 叢集中透過 Service 名稱就能訪問這些後端 IP,呼叫方則不用再直接感知這些後端Pod IP的變化了。具體 Pod 與 Service 之間的對映關係,則完全由Kubernetes叢集本身來實現。它們之間的關係如下圖所示:

一個實現服務註冊發現原理的擴充套件模式,讓你的service mesh不再是雞肋

如上圖所示,現在要負載均衡地訪問一組相同的服務副本——訂單,透過 Service 資源型別的定義,就可以將其對外表示成一個程序或服務資源物件,Kubernetes 會為其分配叢集內的固定IP地址,之後透過請求 Service(叢集內用名稱、叢集外可透過 Ingress 或 NodePort 方式暴露),Service 自己就會負載均衡地將請求轉發給相應的 Pod 節點。

Service 資源與 Pod 編排物件之間的關聯,雖說從實現上是由 Kubernetes 叢集自己管理的,但在服務釋出時則是需要我們自己透過資源定義進行關聯。以一段Kuberntes釋出程式碼為例:

apiVersion: v1

kind: Service

metadata:

name: micro-order

labels:

app: micro-order

service: micro-order

spec:

type: ClusterIP

ports:

- name: http

#此處設定80埠的原因在於改造的Mock FeignClient程式碼預設是基於80埠進行服務呼叫

port: 80

targetPort: 9091

selector:

app: micro-order

——-

apiVersion: apps/v1

kind: Deployment

metadata:

name: micro-order-v1

labels:

app: micro-order

version: v1

spec:

replicas: 2

selector:

matchLabels:

app: micro-order

version: v1

template:

metadata:

labels:

app: micro-order

version: v1

spec:

containers:

- name: micro-order

image: 10。211。55。2:8080/micro-service/micro-order:1。0-SNAPSHOT

imagePullPolicy: Always

tty: true

ports:

- name: http

protocol: TCP

containerPort: 19091

#環境引數設定(設定微服務返回gRPC服務端的地址+埠)

env:

- name: GRPC_SERVER_HOST

value: micro-pay

- name: GRPC_SERVER_PORT

value: “18888”

如上述 Kubernetes 釋出檔案中高亮的程式碼,在定義Pod編排物件時透過

metadata

標籤定義了這一組 Service 的

label

,然後透過

selector

標籤指定響應的標籤,這樣Service就可以訪問帶有這組標籤定義的Pod集合了。

以上就是 Kubernetes 實現服務註冊發現基本原理,其中涉及的邏輯將被利用在Service Mesh 微服務平臺 Istio 的設計實現中。

Istio

服務註冊發現

接下來我們以 Istio(基於istio1。9架構)為代表的 Service Mesh 架構為例,看看它是如何實現服務註冊發現的。經過前面的鋪墊,相信你多少已經瞭解了一點 Istio 實現服務註冊發現的基本原理:

“透過監聽 Kubernetes 節點變化及 Service 資源型別實現”。

接下來我們將進一步細化它,從執行邏輯的視角來分析下在Istio中

控制面

資料面

是如何配合實現微服務註冊發現的。具體如下圖所示:

一個實現服務註冊發現原理的擴充套件模式,讓你的service mesh不再是雞肋

上圖所描述的就是 Istio 基於 Kubernetes 實現微服務註冊發現的核心邏輯。Kubernetes 叢集環境安裝 Istio 後,Kubernetes 在建立 Pod 時,就會透過 Kube-APIserver 呼叫控制面元件的 Sidecar-Injector 服務、自動修改應用程式的描述資訊並將其注入 SideCar,之後在建立業務容器的 Pod 中同時建立 SideCar 代理容器。SideCar 則會透過 xDS 協議與Istio控制面各元件連線,這裡我們著重介紹Pilot控制面元件實現服務發現的內容。

首先明確的是在新版的 Istio中,服務發現的邏輯已經上移至控制面元件 Pilot,Pilot會監聽 Kube-APIServer 中關於

Service、Endpoint、Pod

等資源的變化,並實時下發給各微服務SideCar中的

Pilot Agent

代理元件。這個過程是透過

xDS 標準協議

下發的,而下發的基本邏輯是 Envoy 啟動時 Pilot 會把所有服務例項資訊推送給 Envoy,後續則是有更新就推送更新資料至所有 Envoy,這樣各個微服務的 Envoy 就能實時感知微服務例項節點的變化了。

這種方式保證各個微服務的 Envoy 代理能隨時感知到 Kubernetes 叢集中服務節點的變化,也天然實現了微服務的健康性檢測邏輯,當然為了防止 Pod IP 重用的問題,Envoy 在接收 Pilot 推送的變動例項資訊時也會對服務名稱進行二次 Check,如果發現 IP 所對應的 Service 名稱與之前不一致,則及時更新本地資料資訊,防止調用出錯。

透過上述

服務發現系統

的支援,服務消費方在呼叫目標微服務時,流量會被本 Pod 中的 Envoy 代理自動劫持,此時 Envoy 就會根據自身儲存的服務例項對映資訊,及控制面配置下發的服務治理規則,對呼叫執行負載均衡、熔斷、限流等服務治理規則。

這就是以 Istio 為代表的 Service Mesh 微服務架構實現服務註冊發現的基本邏輯,可以看到

Envoy 資料面

Pilot 控制面元件

的配合自動實現了服務發現邏輯,而這一切對微服務本身來說都是無感知的!

後記

本篇文章簡單總結和概述了從傳統微服務架構到 Service Mesh 有關服務註冊發現邏輯的變化,也進一步從側面也感受到了以 Istio 為代表的 Service Mesh  微服務架構,在架構設計理念上的進步。希望本篇文章的內容能夠對你有所啟發,並進一步開啟你進入 Service Mesh微服務架構時代的窗戶,在後面的文章中我們還會進一步分享與Service Mesh原理和實踐相關的知識,敬請期待!

參考文件:

#Istio 1。9流量管理官方文件(英文版)

https://istio。io/latest/docs/concepts/traffic-management/#introducing-istio-traffic-management

#Istio 1。9流量管理官方文件(中文版)

https://istio。io/latest/zh/docs/concepts/traffic-management/

#Sidecar資源隔離配置參考文件

https://istio。io/latest/docs/reference/config/networking/sidecar

作者簡介:

姜橋 畢業於長春工業大學,曾就職於摩拜單車,設計研發日訂單千萬級支付系統;現任職於馬蜂窩旅行網,負責業務系統的微服務架構轉型及中臺系統。

4月20日晚八點,歡迎來到CSDN悅讀時間直播間,與四位大咖一起探索UNIX傳奇往事的啟示,圍觀《UNIX傳奇》新書釋出會!

TAG: 服務註冊ServiceKubernetesPod