再見(jiàn)Nacos,我要玩Service Mesh了!

前面的文章<<干貨|如何步入Service Mesh微服務(wù)架構(gòu)時(shí)代>>實(shí)戰(zhàn)演練了Service Mesh微服務(wù)架構(gòu)的具體玩法,該案例中通過(guò)Istio+Kubernetes的組合,一組以Spring Boot框架開(kāi)發(fā)的服務(wù),在自身沒(méi)有實(shí)現(xiàn)任何服務(wù)注冊(cè)發(fā)現(xiàn)邏輯的情況下,被部署到Kubernetes后便能通過(guò)服務(wù)名直接完成服務(wù)接口調(diào)用,并且還能對(duì)調(diào)用進(jìn)行限流、熔斷及負(fù)載均衡等一系列服務(wù)治理操作。
這一切是怎么發(fā)生的呢?在今天介紹Service Mesh微服務(wù)架構(gòu)有關(guān)服務(wù)注冊(cè)發(fā)現(xiàn)原理的文章中將為大家揭曉答案。
傳統(tǒng)微服務(wù)注冊(cè)中心實(shí)現(xiàn)機(jī)制

在闡述Service Mesh服務(wù)注冊(cè)發(fā)現(xiàn)機(jī)制前,先簡(jiǎn)單回顧下在以Spring Cloud為代表的傳統(tǒng)微服務(wù)中是如何實(shí)現(xiàn)服務(wù)注冊(cè)與發(fā)現(xiàn)的。
傳統(tǒng)微服務(wù)體系中,注冊(cè)中心無(wú)疑是整個(gè)微服務(wù)體系最重要的組成部分,沒(méi)有服務(wù)注冊(cè)中心提供的統(tǒng)一服務(wù)注冊(cè)發(fā)現(xiàn)功能,微服務(wù)本身就無(wú)從談起。不過(guò)相較于Service Mesh架構(gòu)中服務(wù)注冊(cè)發(fā)現(xiàn),很大程度上是依賴于基礎(chǔ)設(shè)施(例如數(shù)據(jù)面[envoy]、控制面[istio]以及Kubernetes集群),而無(wú)需微服務(wù)親力親為。在Spring Cloud傳統(tǒng)架構(gòu)中,服務(wù)注冊(cè)、發(fā)現(xiàn)及健康性檢查等服務(wù)治理邏輯則都需要微服務(wù)自己上下打點(diǎn)。
雖然不用重復(fù)造輪子,都有現(xiàn)成的服務(wù)治理組件及框架,但從應(yīng)用運(yùn)行形態(tài)上說(shuō),與服務(wù)注冊(cè)發(fā)現(xiàn)相關(guān)的邏輯都是微服務(wù)直接與注冊(cè)中心產(chǎn)生的交互。從系統(tǒng)架構(gòu)上看,這種方式顯然是將服務(wù)治理邏輯與業(yè)務(wù)應(yīng)用耦合了,其運(yùn)行邏輯如下圖所示:

從上圖可以看到,微服務(wù)啟動(dòng)時(shí)會(huì)通過(guò)集成的服務(wù)發(fā)現(xiàn)SDK,向注冊(cè)中心(應(yīng)用配置的注冊(cè)中心地址)發(fā)送注冊(cè)信息,注冊(cè)中心在收到服務(wù)注冊(cè)請(qǐng)求后將存儲(chǔ)服務(wù)的基本信息;之后,如有服務(wù)消費(fèi)者要調(diào)用該服務(wù),那么調(diào)用方通過(guò)服務(wù)發(fā)現(xiàn)組件(例如Ribbon)就可以向注冊(cè)中心查詢目標(biāo)微服務(wù)的地址列表,并通過(guò)獲取的服務(wù)地址列表,以某種負(fù)載策略向目標(biāo)微服務(wù)發(fā)起調(diào)用了。
而當(dāng)服務(wù)節(jié)點(diǎn)發(fā)現(xiàn)變更時(shí),新的節(jié)點(diǎn)會(huì)被重新注冊(cè),而下線的節(jié)點(diǎn)基于服務(wù)探活機(jī)制也會(huì)及時(shí)從服務(wù)注冊(cè)中心被踢除,基于這樣的服務(wù)注冊(cè)/發(fā)現(xiàn)機(jī)制,微服務(wù)之間的通信順暢就能得到保證。從這個(gè)角度看,注冊(cè)中心與服務(wù)之間的健康性檢查就顯得很重要,如果注冊(cè)中心不能及時(shí)將下線或故障的節(jié)點(diǎn)從可用服務(wù)器地址列表剔除,那么就很可能會(huì)造成微服務(wù)某些調(diào)用的失敗。
那么一般怎樣進(jìn)行服務(wù)健康性檢查呢?從方式上看,主流的健康性檢查方式,主要有以下3種:
服務(wù)主動(dòng)探活就是微服務(wù)定期向注冊(cè)中心發(fā)送租約信息以表面自己的存活。在實(shí)際場(chǎng)景中主動(dòng)探活是我們使用注冊(cè)中心時(shí)用得最多的一種方式,如果服務(wù)規(guī)模不大,或者使用了類似于Eureka這樣的最終一致性注冊(cè)中心,那么主動(dòng)探活就是一種最佳選擇,它可以較大程度地避免服務(wù)部署在Kubernetes集群后,因?yàn)镻od IP重用而導(dǎo)致的舊有服務(wù)節(jié)點(diǎn)依然存活的問(wèn)題,畢竟續(xù)約信息都是帶著服務(wù)基礎(chǔ)信息上報(bào)到注冊(cè)中心的。
但這種方式也有明顯的弊端,它會(huì)造成注冊(cè)中心寫(xiě)操作壓力變大。如果大量的服務(wù)同時(shí)發(fā)布,節(jié)點(diǎn)產(chǎn)生較大的變動(dòng),那么就可能產(chǎn)生大量的通知事件,從而對(duì)整個(gè)微服務(wù)體系的穩(wěn)定產(chǎn)生較大影響。
此外,主動(dòng)續(xù)約,也并不完全說(shuō)明服務(wù)是健康的,因?yàn)槟承┨厥馇闆r下可能會(huì)存在雖然服務(wù)無(wú)法對(duì)外提供服務(wù),但還能正常向注冊(cè)中心發(fā)出租約信息的問(wèn)題。
前面分析了微服務(wù)主動(dòng)探活方式的優(yōu)缺點(diǎn),而如果由注冊(cè)中心發(fā)起健康性檢查會(huì)怎么樣呢?
這種方式是指微服務(wù)在注冊(cè)時(shí),同時(shí)向注冊(cè)中心暴露自己的健康檢查端點(diǎn)(如/actuator/health),注冊(cè)中心通過(guò)定期訪問(wèn)來(lái)探測(cè)服務(wù)節(jié)點(diǎn)是否存活。
不過(guò)這種方式也不是完全沒(méi)有問(wèn)題,例如前面提到的Pod IP重用問(wèn)題,如果其他微服務(wù)重用了之前節(jié)點(diǎn)的IP,那么就會(huì)發(fā)生失效節(jié)點(diǎn)被激活的假象。當(dāng)然也有相應(yīng)的解決方案,例如后面會(huì)講到Istio微服務(wù)注冊(cè)發(fā)現(xiàn)時(shí)Envoy會(huì)對(duì)服務(wù)名稱進(jìn)行二次Check的方式。
第3種方案比較極端,注冊(cè)中心不進(jìn)行任何服務(wù)探活,全部由微服務(wù)調(diào)用方所在的負(fù)載均衡器進(jìn)行探活。這種方案常見(jiàn)的一種場(chǎng)景就是gRPC的失效節(jié)點(diǎn)自動(dòng)摘除功能。但這種方案依然具有IP被重用問(wèn)題,所以此種方式在實(shí)際的場(chǎng)景中并不是很受歡迎。
前面講了微服務(wù)注冊(cè)發(fā)現(xiàn)常見(jiàn)的三種服務(wù)探活方式,其實(shí)三種方案各有利弊。以Spring Cloud微服務(wù)體系中,使用最廣泛的幾種注冊(cè)中心為例,如Eureka、Consul、Nacos,它們的對(duì)比如下表所示:
從表格中可以看到,除了Eureka主要采用服務(wù)主動(dòng)探活方式外,Consul及Nacos都采用了多種服務(wù)探活方式,從而盡量規(guī)避不同方式的弊端,這也是為什么目前大部分實(shí)踐逐步拋棄Eureka而采用Consul或Nacos的原因。
除了健康性檢查外,上面表格還分別列出了這幾種注冊(cè)中心的一致性協(xié)議。這里順帶普及下CAP理論。CAP是分布式系統(tǒng)中重要的一種概念,它分別由一致性(Consistency)、可用性(Availability)、分區(qū)容錯(cuò)性(Partition tolerance)三部分組成。在CAP理論中,一個(gè)分布式系統(tǒng)不可能三種都滿足,一般只能同時(shí)滿足其中的兩種,例如Eureka和Nacos只能滿足可用性和分區(qū)容錯(cuò)性,而無(wú)法滿足一致性;Consul則只能滿足一致性和分區(qū)容錯(cuò)性,而無(wú)法完全滿足可用性。
在注冊(cè)中心的場(chǎng)景中,一致性一般要求并不高,只要能達(dá)到最終一致性即可。畢竟在微服務(wù)架構(gòu)中,涉及節(jié)點(diǎn)的注冊(cè)和反注冊(cè),注冊(cè)中心和客戶端之間的通信需要一定時(shí)間,一致性本身也很難達(dá)到。所以在注冊(cè)中心的選型中,一般會(huì)優(yōu)先選擇AP的系統(tǒng),這也是目前還在以Spring Cloud構(gòu)建微服務(wù)的實(shí)踐中,除了自研外,開(kāi)源技術(shù)中會(huì)優(yōu)先選擇Nacos作為服務(wù)注冊(cè)中心的原因。
Kubernetes服務(wù)注冊(cè)與自動(dòng)發(fā)現(xiàn)

前面以一定篇幅回顧了Spring Cloud傳統(tǒng)微服務(wù)體系中,注冊(cè)中心有關(guān)的基本邏輯及常見(jiàn)開(kāi)源技術(shù)。在具體講述Service Mesh架構(gòu)中服務(wù)注冊(cè)發(fā)現(xiàn)的邏輯前,有必要先了解下Kubernetes容器編排中,與Service服務(wù)資源有關(guān)的概念。
因?yàn)樽盍餍械腟ervice Mesh方案(如Istio),大都選擇了與Kubernetes集群相結(jié)合的方案,而其服務(wù)注冊(cè)邏輯也主要是利用了Kubernetes的內(nèi)部服務(wù)發(fā)現(xiàn)機(jī)制。例如Istio就是通過(guò)監(jiān)聽(tīng)Kubernetes Pod的變化來(lái)實(shí)現(xiàn)服務(wù)發(fā)現(xiàn)的。當(dāng)然這并不是說(shuō)在Service Mesh中不能選擇傳統(tǒng)的注冊(cè)中心方案,只不過(guò)實(shí)施起來(lái)可能需要改造或自研注冊(cè)中心才能滿足需求(兼容新舊微服務(wù)體系時(shí),可能會(huì)這么考慮)。如果是全新設(shè)計(jì)的Service Mesh微服務(wù)架構(gòu),最佳的方案還是選擇像Istio這樣直接利用Kubernetes自身功能實(shí)現(xiàn)服務(wù)發(fā)現(xiàn)。
在Kubernetes中能夠?yàn)镾ervice Mesh微服務(wù)提供內(nèi)部服務(wù)注冊(cè)發(fā)現(xiàn)的基礎(chǔ)就是Service類型的資源。Service是Kubernetes抽象出來(lái)的服務(wù)概念,一組Pod的集合就是Kubernetes的Service。在Kubernetes中Pod是最小的容器編排單元,一個(gè)Pod編排資源中可以定義多個(gè)容器,Pod內(nèi)的容器可以通過(guò)本地訪問(wèn)在Pod中通信,一組容器共享一個(gè)Pod IP,這就是Kubernetes之所以被稱為容器編排平臺(tái)最核心的體現(xiàn)。
但Pod是有生命周期的,也就是說(shuō)Pod的IP地址并不固定,它會(huì)隨著Pod生命周期的變化而變化,所以如果以Pod為服務(wù)調(diào)用對(duì)象,那么IP地址的經(jīng)常變化會(huì)導(dǎo)致調(diào)用方服務(wù)發(fā)現(xiàn)邏輯不穩(wěn)定,從而使系統(tǒng)變得復(fù)雜。為了解決這個(gè)問(wèn)題,Kubernetes中就抽象出了Service資源類型,雖然Pod的IP地址會(huì)變化,但是抽象的Service名稱卻是固定的,所以Kubernetes集群中通過(guò)Service名稱就能訪問(wèn)這些后端IP,調(diào)用方則不用再直接感知這些后端Pod IP的變化了。具體Pod與Service之間的映射關(guān)系,則完全由Kubernetes集群本身來(lái)實(shí)現(xiàn)。它們之間的關(guān)系如下圖所示:

如上圖所示,現(xiàn)在要負(fù)載均衡地訪問(wèn)一組相同的服務(wù)副本——訂單,通過(guò)Service資源類型的定義,就可以將其對(duì)外表示成一個(gè)進(jìn)程或服務(wù)資源對(duì)象,Kubernetes會(huì)為其分配集群內(nèi)的固定IP地址,之后通過(guò)請(qǐng)求Service(集群內(nèi)用名稱、集群外可通過(guò)Ingress或NodePort方式暴露),Service自己就會(huì)負(fù)載均衡地將請(qǐng)求轉(zhuǎn)發(fā)給相應(yīng)的Pod節(jié)點(diǎn)。
Service資源與Pod編排對(duì)象之間的關(guān)聯(lián),雖說(shuō)從實(shí)現(xiàn)上是由Kubernetes集群自己管理的,但在服務(wù)發(fā)布時(shí)則是需要我們自己通過(guò)資源定義進(jìn)行關(guān)聯(lián)。以一段Kuberntes發(fā)布代碼為例:
apiVersion: v1
kind: Service
metadata:
name: micro-order
labels:
app: micro-order
service: micro-order
spec:
type: ClusterIP
ports:
- name: http
#此處設(shè)置80端口的原因在于改造的Mock FeignClient代碼默認(rèn)是基于80端口進(jìn)行服務(wù)調(diào)用
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
#環(huán)境參數(shù)設(shè)置(設(shè)置微服務(wù)返回gRPC服務(wù)端的地址+端口)
env:
- name: GRPC_SERVER_HOST
value: micro-pay
- name: GRPC_SERVER_PORT
value: "18888"
如上述Kubernetes發(fā)布文件中高亮的代碼,在定義Pod編排對(duì)象時(shí)通過(guò)metadata標(biāo)簽定義了這一組Service的label,然后通過(guò)selector標(biāo)簽指定響應(yīng)的標(biāo)簽,這樣Service就可以訪問(wèn)帶有這組標(biāo)簽定義的Pod集合了。
以上就是Kubernetes實(shí)現(xiàn)服務(wù)注冊(cè)發(fā)現(xiàn)基本原理,其中涉及的邏輯將被利用在Service Mesh微服務(wù)平臺(tái)Istio的設(shè)計(jì)實(shí)現(xiàn)中。
Istio服務(wù)注冊(cè)發(fā)現(xiàn)

接下來(lái)我們以Istio(基于istio1.9架構(gòu))為代表的Service Mesh架構(gòu)為例,看看它是如何實(shí)現(xiàn)服務(wù)注冊(cè)發(fā)現(xiàn)的。經(jīng)過(guò)前面的鋪墊,相信你多少已經(jīng)了解了一點(diǎn)Istio實(shí)現(xiàn)服務(wù)注冊(cè)發(fā)現(xiàn)的基本原理:“通過(guò)監(jiān)聽(tīng)Kubernetes節(jié)點(diǎn)變化及Service資源類型實(shí)現(xiàn)”。
接下來(lái)我們將進(jìn)一步細(xì)化它,從運(yùn)行邏輯的視角來(lái)分析下在Istio中控制面與數(shù)據(jù)面是如何配合實(shí)現(xiàn)微服務(wù)注冊(cè)發(fā)現(xiàn)的。具體如下圖所示:

上圖所描述的就是Istio基于Kubernetes實(shí)現(xiàn)微服務(wù)注冊(cè)發(fā)現(xiàn)的核心邏輯。Kubernetes集群環(huán)境安裝Istio后,Kubernetes在創(chuàng)建Pod時(shí),就會(huì)通過(guò)Kube-APIserver調(diào)用控制面組件的Sidecar-Injector服務(wù)、自動(dòng)修改應(yīng)用程序的描述信息并將其注入SideCar,之后在創(chuàng)建業(yè)務(wù)容器的Pod中同時(shí)創(chuàng)建SideCar代理容器。SideCar則會(huì)通過(guò)xDS協(xié)議與Istio控制面各組件連接,這里我們著重介紹Pilot控制面組件實(shí)現(xiàn)服務(wù)發(fā)現(xiàn)的內(nèi)容。
首先明確的是在新版的Istio中,服務(wù)發(fā)現(xiàn)的邏輯已經(jīng)上移至控制面組件Pilot,Pilot會(huì)監(jiān)聽(tīng)Kube-APIServer中關(guān)于Service、Endpoint、Pod等資源的變化,并實(shí)時(shí)下發(fā)給各微服務(wù)SideCar中的Pilot Agent代理組件。這個(gè)過(guò)程是通過(guò)xDS標(biāo)準(zhǔn)協(xié)議下發(fā)的,而下發(fā)的基本邏輯是Envoy啟動(dòng)時(shí)Pilot會(huì)把所有服務(wù)實(shí)例信息推送給Envoy,后續(xù)則是有更新就推送更新數(shù)據(jù)至所有Envoy,這樣各個(gè)微服務(wù)的Envoy就能實(shí)時(shí)感知微服務(wù)實(shí)例節(jié)點(diǎn)的變化了。
這種方式保證各個(gè)微服務(wù)的Envoy代理能隨時(shí)感知到Kubernetes集群中服務(wù)節(jié)點(diǎn)的變化,也天然實(shí)現(xiàn)了微服務(wù)的健康性檢測(cè)邏輯,當(dāng)然為了防止Pod IP重用的問(wèn)題,Envoy在接收Pilot推送的變動(dòng)實(shí)例信息時(shí)也會(huì)對(duì)服務(wù)名稱進(jìn)行二次Check,如果發(fā)現(xiàn)IP所對(duì)應(yīng)的Service名稱與之前不一致,則及時(shí)更新本地?cái)?shù)據(jù)信息,防止調(diào)用出錯(cuò)。
通過(guò)上述服務(wù)發(fā)現(xiàn)系統(tǒng)的支持,服務(wù)消費(fèi)方在調(diào)用目標(biāo)微服務(wù)時(shí),流量會(huì)被本Pod中的Envoy代理自動(dòng)劫持,此時(shí)Envoy就會(huì)根據(jù)自身存儲(chǔ)的服務(wù)實(shí)例映射信息,及控制面配置下發(fā)的服務(wù)治理規(guī)則,對(duì)調(diào)用執(zhí)行負(fù)載均衡、熔斷、限流等服務(wù)治理規(guī)則。
這就是以Istio為代表的Service Mesh微服務(wù)架構(gòu)實(shí)現(xiàn)服務(wù)注冊(cè)發(fā)現(xiàn)的基本邏輯,可以看到Envoy數(shù)據(jù)面與Pilot控制面組件的配合自動(dòng)實(shí)現(xiàn)了服務(wù)發(fā)現(xiàn)邏輯,而這一切對(duì)微服務(wù)本身來(lái)說(shuō)都是無(wú)感知的!
后記

本篇文章簡(jiǎn)單總結(jié)和概述了從傳統(tǒng)微服務(wù)架構(gòu)到Service Mesh有關(guān)服務(wù)注冊(cè)發(fā)現(xiàn)邏輯的變化,也進(jìn)一步從側(cè)面也感受到了以Istio為代表的Service Mesh微服務(wù)架構(gòu),在架構(gòu)設(shè)計(jì)理念上的進(jìn)步。希望本篇文章的內(nèi)容能夠?qū)δ阌兴鶈l(fā),并進(jìn)一步打開(kāi)你進(jìn)入Service Mesh微服務(wù)架構(gòu)時(shí)代的窗戶,在后面的文章中我們還會(huì)進(jìn)一步分享與Service Mesh原理和實(shí)踐相關(guān)的知識(shí),敬請(qǐng)期待!
—————END—————
推薦閱讀
干貨|如何步入Service Mesh微服務(wù)架構(gòu)時(shí)代
實(shí)戰(zhàn)|Service Mesh微服務(wù)架構(gòu)實(shí)現(xiàn)服務(wù)間gRPC通信
Kubernetes微服務(wù)自動(dòng)化發(fā)布系統(tǒng)
Kubernetes學(xué)習(xí)環(huán)境難搭建?Mac筆記本上安裝一個(gè)!
參考文檔:
#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
