万字长文梳理:从0开始,步入Service Mesh微服务架构的世界
來源 | 無敵碼農
責編 |賈凱強
頭圖?| 下載于視覺中國
新一代微服務架構——Service Mesh已經引發了諸多關注。在微服務架構盛行的今天,像Spring Cloud這樣的微服務框架大家已然耳熟能詳?,因為大部分互聯網公司都在此基礎上構建過第一代微服務體系,所以對于做Java 的同學來說,Spring Cloud微服務體系應該是非常熟悉了。
這里并不是說其他語言棧就沒有構建微服務體系的框架,例如Go語言也有像Go-Micro這樣的微服務框架,只不過目前除了像頭條這樣重度使用Go語言的公司外,其他絕大多數互聯網公司的服務端語言依然還是Java的天下!所以對于目前大部分已經或打算采用微服務架構的公司來說,Spring Cloud框架依然是它們的首選!但如果我說,這套體系發展到今天已經快過時了,你會不會覺得我是在瞎掰呢?因為畢竟咱們現在天天玩的微服務還都是Spring Cloud這一套啊!
難道像Spring Cloud?GateWay、Zuul、Eureka、Consul、Nacos、Feign/Ribbon、Hystrix、Sentinel、Spring Cloud Config、Apollo...,這些涵蓋了微服務體系——服務注冊與發現、限流、熔斷降級、負載均衡、服務配置等服務治理各個方面的牛逼開發框架或服務組件們都快要過時了嗎?
雖然這很難讓人接受,畢竟這些技術才剛剛捂熱!但在下一代微服務架構Service Mesh 面前,它們中的絕大部分組件確實是快要過時了,倒不是說這些開源組件在技術上不牛逼或者沒有深入學習研究的價值了,而是它們所面向的微服務體系結構在設計理念上與Service Mesh已經存在代差,這種差距夸張點說就像殲-20與殲-10的差別。這聽起來可能有點聳人聽聞,但從目前微服務技術發展趨勢和實踐上看,這就是歷史潮流!接下來我將從理論和實踐層面對此進行分析和演示!
為什么要進入Service Mesh時代
前面我略微夸張的說到,以Spring Cloud為代表的微服務體系相比Service Mesh而言已經存在技術代差,那憑什么這么說呢?接下來,我們回顧下使用Spring Cloud構建微服務體系的大致技術流程!
要構建微服務體系,首先我們需要獨立部署一款實現服務注冊/發現功能的組件服務,目前可供選擇的主流方案一般有Eureka、Consul、Nacos等,搞定服務注冊/發現后,我們編寫一個Java微服務,此時為了將該服務注冊到服務注冊中心,一般會引入Spring Cloud提供的支持對應注冊中心接入的SDK,并在應用入口類中通過@EnableDiscoveryClient注解的方式標注,之后SDK中的邏輯就會在應用啟動時執行服務注冊動作,并提供給注冊中心相應地探測接口,以此實現微服務與服務注冊中心之間的連接。以此類推,我們可以通過這種方式將一組微服務都注冊到服務注冊中心!
而如果服務之間要互相調用怎么辦呢?一般我們會通過編寫FeignClient接口來實現微服務之間的調用,而其底層的邏輯則是通過Feign所集成的Ribbon組件去注冊中心中獲取目標服務的服務地址列表,之后Ribbon根據服務地址列表進行負載均衡調用。至于服務與注冊中心之間如何保證連接有效性,則依賴于服務注冊中心與其SDK之間的協作機制。
而高級一點,服務之間的調用除了實現負載均衡,還要實現熔斷限流、那么此時可以通過部署服務網關組件(例如Zuul/Spring Cloud GateWay)來實現微服務入口的熔斷限流、內部服務之間的限流熔斷則通過集成Hystrix或Sentinel組件,以客戶端本地配置或遠程配置中心的方式來實現。
上述過程基本就是我們使用Spring Cloud構建微服務體系的大致過程了!如果仔細思考下這個過程,我們會發現在該微服務體系的構造過程中,與服務治理相關的大部分邏輯都是以SDK的方式耦合在具體的微服務應用之中!服務注冊需要引入SDK、服務調用需要引入SDK、服務熔斷限流也需要SDK;除此之外,為了保證這套體系的正常運行,我們還需要額外維護服務注冊中心、服務網關這樣的基礎服務。這樣的結構會導致什么弊端呢?具體有以下幾點:
1、框架/SDK太多,后續升級維護困難
在這套體系中,與服務治理相關的邏輯都是以SDK代碼依賴的方式嵌入在微服務之中,如果某天我們想升級下服務注冊中心的SDK版本,或者熔斷限流組件Hystrix或Sentinel的版本,那么需要升級改造的微服務可能會是成百上千,且由于這些組件都與業務應用綁定在一起,在升級的過程中會不會影響業務穩定,這都是需要謹慎對待的事情,所以對SDK的升級難度可想而知的!
2、多語言微服務SDK維護成本高
試想下如果構建的微服務體系,也要支持像Go、Python或者其他語言編寫的微服務的話,那么上述這些微服務治理相關的SDK是不是得單獨再維護幾套呢?所以在這種體系結構中,對多語言微服務的支持就成了一個問題!
3、服務治理策略難以統一控制
基于該套體系構建的微服務體系,在對像熔斷、限流、負載均衡等服務治理相關的策略管理上,都是比較分散的,可能有人會寫到自己的本地配置文件,有人會硬編碼到代碼邏輯中,也可能有人會將其配置到遠程配置中心,總之對于服務治理策略邏輯都是由對應的開發人員自己控制,這樣就很難形成統一的控制體系!
4、服務治理邏輯嵌入業務應用,占有業務服務資源
在這套微服務體系中,服務治理相關的邏輯都是在微服務應用進程中寄生運行的,這多少會占有寶貴的業務服務器資源,影響應用性能的發揮!
5、額外的服務治理組件的維護成本
無論是服務注冊中心、還是服務網關,這些除了微服務應用本身之外服務治理組件,都需要我們以中間件基礎服務的方式進行維護,需要額外的人力、額外的服務器成本!
以上就是以Spring Cloud為代表的傳統微服務體系的弊端,如果我說在Service Mesh體系下,以上幾點都不再是問題,甚至都不需要研發人員再進行任何關注!我們只需要寫一個普通的Spring Boot服務,也不需要引入服務注冊SDK、熔斷限流SDK組件,總之你寫一個普普通通的服務,就能實現像之前Spring Cloud微服務體系所能支持的大部分服務治理功能,你會相信嗎?你會不會覺得這跟之前寫單體應用沒啥差別了呢?
不管你怎么想,這些就是Service Mesh要干的事情!Service Mesh的目標就是要將微服務治理體系下沉為一套與業務無關的基礎設施。從這個角度看,如果咱們不認真學習下Service Mesh,那么以后將變得越來越低智,因為Spring Cloud好歹還能讓我們感知下微服務的存在,而在Service Mesh中,微服務治理體系作為基礎設施的一部分,對普通研發人員將越來越透明!
Service Mesh的解決方案是什么
在前面我們說到,Service Mesh的目標是要將微服務治理體系下沉為與業務無關的基礎設施。這句話怎么理解呢?實際上Service Mesh微服務治理技術的誕生也不是憑空產生的,而是在以Kubernetes為代表的容器編排技術逐步成為軟件運行主流基礎環境的背景下,以及以Spring Cloud框架為代表的傳統微服務技術體系弊端逐步顯現的情況下技術自然迭代發展的結果。總之,就是有點萬事具備,只欠東風的感覺!
所以,我們看到目前落地的Service Mesh方案中大多都是與Kubernetes深度結合的方案,例如最受矚目的Istio!接下來我們具體看看在Service Mesh中微服務治理的核心邏輯是怎么實現的(以Istio+Envoy為例)!
要理解在Service Mesh中的微服務治理邏輯的具體實現,就不得不上一張關于服務網格概念很經典但剛開始看著又很難理解的圖,如下:
如果你之前大致了解過Service Mesh的概念,那么這張圖相信你一定見過。其中綠色的正方形表示正常部署的微服務,而藍色的正方形表示一個網絡代理,也就是大家通常所說的SideCar。在Service Mesh架構下,每部署一個微服務,都需要部署一個與之相對應的代理服務,所有與微服務本身的交互都通過SideCar代理,而SideCar之間會形成一張形似網格的交互鏈路,這就是服務網格名稱的來由!
在Service Mesh中,當我們將一個服務部署在Kubernetes之后,安裝在Kubernetes中的Service Mesh組件(例如Istio)就會自動在該微服務的同一個Pod之中啟動一個與之對應的代理進程(例如istio-proxy),這個保姆式的代理進程會代替微服務本身去實現原先在Spring Cloud體系中需要微服務自身完成的服務注冊、負載均衡、熔斷限流等微服務治理功能。并且,這些代理進程并不是孤軍奮戰,而是會通過像xDS協議(Service Mesh中數據面與控制面通信的通用協議)與Service Mesh控制組件保持連接。
這也就引出了Service Mesh架構中關鍵的兩個概念:控制面與數據面。前面我們所示的Sidecar(例如istio-proxy,實際上是envoy)就是數據面,與微服務治理邏輯相關的信息都存儲在數據面中,而控制面則是Service Mesh的中心控制組件(例如Istio中的Pilot組件),控制面可以通過xDS協議(具體又分為LDS、CDS...)向數據面下發各種服務治理相關的規則,例如限流規則、路由規則、服務節點更新信息等等。
這種設計方式就是Service Mesh最核心的設計邏輯——通過Sidecar的方式代理微服務進行服務治理邏輯(數據面),通過控制面感知外界環境的變化并通過xDS協議支持各種微服務治理策略規則的集中管理和下發,而這里的控制面和數據面都會被融合進像Kubernetes這樣的基礎架構環境中,對于普通微服務的開發,研發人員要做的只是將一個應用以編排的方式部署進k8s集群即可!而所有與微服務治理相關的邏輯都由代理數據面與控制面協作完成。
這里我們以Service Mesh最著名的開源方案Istio的架構圖來解釋上面所說的邏輯,具體如下:
其中服務注冊發現可以直接利用Kubernetes的內部發現機制,通過監聽Kubernetes Pod的變化來實現,具體示意圖如下:
而微服務治理相關的邏輯,以Istio為例,流程大致是這樣的:
管理員通過Pilot配置治理規則,并通過xDS協議向Envoy下發治理規則,而Envoy從Pilot獲取微服務治理規則后,就可以在流量訪問的時候按照規則執行相應的限流、路由等微服務治理邏輯了!
Istio+Envoy的Service Mesh架構玩法
前面我們從原理層面大致介紹了Service Mesh微服務架構的核心概念及流程邏輯,如果你玩過Service Mesh架構,那么理解起來是很容易的!但是如果沒有具體實踐過,特別是如果對Kubernetes沒有基本的了解,那么上面的概念可能也并不好理解,例如你可能會竭力地想象"我到底應該怎么部署那個所謂的Sidecar代理?","在Service Mesh架構下怎么去開發服務?"等這樣的問題!
而如果我寫到這里不寫了,那么這篇文章也只是與大部分介紹過Service Mesh的文章一樣,要么就是各種高大上不接地氣的原理介紹,要么就是翻過來覆過去的概念介紹,或者好不容易找到一篇帶有示例的文章,但大多數也是基于Istio官方Demo的演示!
而對于開發過Spring Cloud微服務應用的同學來說,其實并不是很好理解!所以接下來的玩法實踐,我將以最接近實際開發場景的方式、站在一個曾經使用Spring Cloud框架開發過微服務的研發人員角度,來整體介紹如何用平常工作中所使用的Java流行框架(如Spring Boot)來開發基于Service Mesh體系的微服務應用!
01 k8s環境準備及Istio安裝
具體過程及步驟如下:
要玩轉Service Mesh微服務架構,基本的前提是需要一個功能完整的Kubernetes環境,這里我所使用的k8s環境是在開發本上安裝一個Linux虛擬機并在其之上部署一個只有Master節點的Kubernetes單節點集群,另外由于Istio對Kubernetes的版本是有要求的,這里所使用的k8s版本是v1.18.6。如果對安裝Kubernetes環境還有疑惑也可以參考本號早前發布的文章《如何部署一個Kubernetes集群》!
這里我先假設你已經搞定了Kubernetes環境,接下來開始安裝Istio,選擇的版本是istio-1.8.4,具體步驟如下:
1)、下載Istio發布包
由于官方提供的下載腳本執行速度比較慢,可以直接在github找到相應的istio發布版本后通過wget命令將其下載至主機指定目錄(能正常連接k8s集群):
wget?https://github.com/istio/istio/releases/download/1.8.4/istio-1.8.4-linux-amd64.tar.gz下載成功后,解壓安裝包:
進入解壓安裝包目錄:
2)、將istioctl客戶端添加到系統可執行路徑
在具體安裝istio時需要使用istioctl命令,因此需要先將該命令加入系統可執行路徑,命令如下:
export?PATH=$PWD/bin:$PATH3)、執行安裝istio命令
這里使用istioctl命令執行安裝命令,具體如下:
istioctl?install?--set?profile=demo這里"--set profile=demo"表示安裝一個istio測試環境!成功安裝后的信息輸出如下:
Detected?that?your?cluster?does?not?support?third?party?JWT?authentication.?Falling?back?to?less?secure?first?party?JWT.?See?https://istio.io/v1.8/docs/ops/best-practices/security/#configure-third-party-service-account-tokens?for?details. This?will?install?the?Istio?1.8.4?demo?profile?with?["Istio?core"?"Istiod"?"Ingress?gateways"?"Egress?gateways"]?components?into?the?cluster.?Proceed??(y/N)?Y ??Istio?core?installed?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ??Istiod?installed?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ??Ingress?gateways?installed???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ??Egress?gateways?installed????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ??Installation?complete??如果安裝成功,則可以通過kubectl命令查看istio相關組件是否已經安裝在Kubernetes環境之中,命令如下:
kubectl?get?svc?-n?istio-system NAME???????????????????TYPE???????????CLUSTER-IP???????EXTERNAL-IP???PORT(S)??????????????????????????????????????????????????????????????????????AGE istio-egressgateway????ClusterIP??????10.101.134.226???<none>????????80/TCP,443/TCP,15443/TCP?????????????????????????????????????????????????????8m12s istio-ingressgateway???LoadBalancer???10.96.167.106????<pending>?????15021:31076/TCP,80:31032/TCP,443:31438/TCP,31400:32751/TCP,15443:31411/TCP???8m11s istiod?????????????????ClusterIP??????10.102.112.111???<none>????????15010/TCP,15012/TCP,443/TCP,15014/TCP??此時可以看到istio的核心組件istiod,以及入口網關ingressgateway、出口網關egressgateway已經成功以Service資源的方式運行在了Kuberntes集群之中!
4)、k8s默認命名空間開啟自動注入Envoy?Sidecar
這是一個關鍵的步驟,如果我們的微服務應用未來是默認部署在k8s的default命名空間,那么在安裝istio是需要開啟該空間的Sidecar自動注入功能。這是我們前面提到每啟動一個微服務應用,k8s就會默認在相同的Pod中自動啟動一個代理進程的關鍵設置!
具體命令如下:
$?kubectl?label?namespace?default?istio-injection=enabled namespace/default?labeled5)、Istio可觀測性部署
Kiali是一個基于服務網格的Istio管理控制臺,它提供了一些數據儀表盤和可觀測能力,同時也可以讓我們去操作網格的配置。使用如下方式快速部署一個用于演示的Kiali,命令如下:
其中具體會安裝部署Promethues、Grafana、Zipkin等指標及鏈路采集服務!因為安裝的組件比較多,也比較耗費資源,如果集群資源不是很充足,可能會出現啟動比較慢的情況。如果正常部署成功,可以查看Pod狀態,命令如下:
由于前面安裝istio時,我們并沒有在istio-system空間開啟自動注入Sidecar(其label istio-injection=disabled),這里為了在k8s集群之外正常訪問Kiali、Prometheus、Granfana、Tracing的控制面板(它們共同組成了Service Mesh的可觀測體系),可以通過nodePort的方式對外暴露端口。
Kiali的NodePort訪問操作方式:
將部署的Kiali的Service文件導出到主機的某個目錄,例如:
kubectl?get?svc?-n?istio-system?kiali?-o?yaml?>?kiali-nodeport.yaml之后編輯導出的文件,刪除metadata下的annotation、resourceVersion、selfFlink、uid等信息;并修改下spec下的type類型值,將ClusterIP修改為NodePort,并指定nodePort端口信息;同時刪除status狀態字段即可。具體如下:
編輯完成后執行執行命令:
kubectl?apply?-f?kiali-nodeport.yaml之后查看服務端口,命令如下:
此時通過k8s的集群外部IP+31001端口就能訪問Kiali控制面板了,效果如下圖所示:
與Kiali的操作方式類似,我們也可以通過獲取修改部署的Promethues、Granfana、Tracing、Zipkin等服務的發布文件,通過設置NodePort端口,從而在k8s集群外部進行可觀測界面的訪問!例如:
#Prometheus kubectl?get?svc?-n?istio-system?prometheus?-o??yaml?>?prometheus-nodeport.yaml kubectl?apply?-f?prometheus-nodeport.yaml?#Granfana kubectl?get?svc?-n?istio-system?grafana?-o?yaml?>?grafana-nodeport.yaml kubectl?apply?-f?grafana-nodeport.yaml#Jaeger(分布式鏈路) kubectl?get?svc?-n?istio-system?tracing?-o?yaml?>?tracing-nodeport.yaml kubectl?apply?-f?tracing-nodeport.yaml ...其中Granfana的訪問效果示意圖如下:
02 Spring Boot微服務開發
經過前面的步驟,我們已經從基礎架構環境的角度完成了基于Istio的Service Mesh微服務體系的構建!如果類比之前基于Spring Cloud框架的微服務開發體驗,那么在Istio體系下應該如何進行微服務應用的開發呢?
接下來我們通過一個實際的應用示例來演示,如何開發基于Istio的Service Mesh微服務應用,服務鏈路如下:
如上所示鏈路,具體說明如下:
1)、為了完整演示在Service Mesh架構下微服務的研發過程,這里我們定義3個微服務,其中micro-api服務是面向外部客戶端接入的Api服務提供Http協議訪問;
2)、而micro-api與micro-order之間則基于微服務的注冊發現機制進行內部服務調用,具體采用Http協議;
3)、而micro-order與micro-pay之間也基于微服務注冊發現機制進行內部微服務調用,為了演示更多的研發場景,這兩個微服務之間的通信我們采用Grpc協議;
規劃好了微服務應用架構,接下來就可以具體開發了!具體的服務代碼層面的構建,這里并不需要做任何微服務框架的引入,你只需要通過Spring Boot構建幾個基本的Spring Boot應用即可,不需要引入任何服務治理相關的組件,只是一個簡單且單純的Spring Boot應用,不需要連接注冊中心,也不需要引入什么OpenFeign、Hystrix、Sentinel之類的組件。
具體的代碼結構如下圖所示:
可以看到應用的入口類中已經沒有服務發現之類的注解!接下來我們講重點:
首先,在之前基于Spring Cloud的微服務調用中,如果通過Http協議進行服務調用,一般我們是通過引入OpenFeign來實行,服務方提供一個FeignClient接口定義,調用方代碼直接引入即可,而具體的運行邏輯,則是OpenFeign中集成的Ribbon組件會從注冊中心獲取目標服務地址列表,然后進行負載均衡調用。
但在Service Mesh架構下負載均衡及服務發現的邏輯已經由Istio中的Sidecar幫我們干了,所以在這里就不能還像以前一樣引入OpenFeign了!那么怎么辦呢?為了延續之前的編程風格及服務通信代碼的簡易性,這里我們需要自己定制一個類似于OpenFeign的框架,可以基于OpenFeign的源碼進行改造,但是要去掉其中關于服務負載均衡、熔斷限流等服務治理相關的邏輯,讓它變成一個只是簡單進行Http服務調用的框架。
目前市面上并沒有這樣一個官方的適配框架,所以一些落地Service Mesh架構的公司為了兼容Spring Cloud微服務體系的遷移,也是自己單獨改造和封裝的,這里我從github上找了一個個人改造的代碼并進行了適配修改,測試是可以的!其具備的能力說明如下:
1、支持在istio服務網格體系下,完成服務間的快速調用(體驗和原先Spring Cloud Feign類似);
2、支持多環境配置,例如本地環境微服務的調用地址可配置為本地,其他環境默認為Kubernetes集群中的服務;
3、支持鏈路追蹤,默認透傳如下Header,可以自動支持jaeger、zipkin鏈路追蹤等,如下:
`"x-request-id",?"x-b3-traceid",?"x-b3-spanid",?"x-b3-sampled",?"x-b3-flags",?"x-b3-parentspanid","x-ot-span-context",?"x-datadog-trace-id",?"x-datadog-parent-id",?"x-datadog-sampled",?"end-user",?"user-agent"`最后的實際編程風格是這樣的:
@FakeClient(name?=?"micro-order") @RequestMapping("/order") public?interface?OrderServiceClient?{/***?訂單創建*/@PostMapping("/create")ResponseResult<CreateOrderBO>?create(@RequestBody?CreateOrderDTO?createOrderDTO); }
這里是micro-order微服務給micro-api所提供的接口調用代碼,micro-api服務引入調用即可,從編程風格上與之前Spring Cloud微服務的開發方式十分類似。只不過到這里為止,你并沒有能看到任何與服務注冊發現相關的邏輯!
其次,服務治理的核心邏輯都是由Istio及Sidecar代理完成的,完成應用開發后,只需要編寫k8s部署文件將服務部署進安裝了Istio環境的Kubernetes集群即可,而將編寫的Java服務部署到k8s集群的流程,涉及"Docker鏡像打包->鏡像倉庫發布->k8s部署拉取鏡像"這一套CI/CD操作流程,如果不明白可以看看本號之前的文章《Kubernetes微服務自動化發布系統》!
接下來重點演示micro-api及micro-order的k8s發布文件,看看它們有什么特別之處:
micro-order服務k8s發布文件(micro-order.yaml):
apiVersion:?v1 kind:?Service metadata:name:?micro-orderlabels:app:?micro-orderservice:?micro-order spec:type:?ClusterIPports:-?name:?httpport:?80targetPort:?9091selector:app:?micro-order--- apiVersion:?apps/v1 kind:?Deployment metadata:name:?micro-order-v1labels:app:?micro-orderversion:?v1 spec:replicas:?2selector:matchLabels:app:?micro-orderversion:?v1template:metadata:labels:app:?micro-orderversion:?v1spec:containers:-?name:?micro-orderimage:?10.211.55.2:8080/micro-service/micro-order:1.0-SNAPSHOTimagePullPolicy:?Alwaystty:?trueports:-?name:?httpprotocol:?TCPcontainerPort:?19091如上所示,這是micro-order服務的k8s部署文件,就是正常定義了該應用的Service資源及Deployment編排資源;為了后面演示服務的負載均衡調用,這里我特地將該應用部署成了2個副本!
接下來繼續看看調用方micro-api服務的k8s發布文件(micro-api.yaml):
apiVersion:?v1 kind:?Service metadata:name:?micro-api spec:type:?ClusterIPports:-?name:?httpport:?19090targetPort:?9090selector:app:?micro-api--- apiVersion:?apps/v1 kind:?Deployment metadata:name:?micro-api spec:replicas:?1selector:matchLabels:app:?micro-apitemplate:metadata:labels:app:?micro-apispec:containers:-?name:?micro-apiimage:?10.211.55.2:8080/micro-service/micro-api:1.0-SNAPSHOTimagePullPolicy:?Alwaystty:?trueports:-?name:?httpprotocol:?TCPcontainerPort:?19090與micro-order一樣也只是定義了該應用的k8s正常發布資源,到這里也并沒有體現出micro-api是怎么調用micro-order服務的!接下來我們通過這個文件將服務發布至k8s集群中(注意,是開啟了Sidecar自動注入的默認命名空間)!
部署成功后,查看Pods信息,具體如下:
#?kubectl?get?pods? NAME??????????????????????????????????????READY???STATUS????RESTARTS???AGE micro-api-6455654996-57t4z????????????????2/2?????Running???4??????????28h micro-order-v1-84ddc57444-dng2k???????????2/2?????Running???3??????????23h micro-order-v1-84ddc57444-zpmjl???????????2/2?????Running???4??????????28h如上所示,可以看到一個micro-api Pod兩個micro-order Pod都已經正常運行起來了!但不知你發現沒有,每個Pod中READY字段顯示的都是2/2,這意味著每個Pod中都啟動了兩個容器,一個是微服務應用本身,另外一個就是自動注入啟動的Sidecar代理進程。為了更深理解這個邏輯,我們可以通過命令查看下Pod的描述信息:
可以看到在開啟了Sidecar自動注入的命名空間中,每啟動一個Pod,Istio都會將Sidecar代理以初始化容器(Init Containers)的方式,自動啟動一個對應地istio-proxy代理進程(Envoy),到這里你應該真實感到到Sidecar到底是一個什么樣的存在了吧!
03 部署Istio微服務網關
前面的步驟中我們已經完成了微服務應用的開發,并且也已經將其部署到了k8s集群,Sidecar代理也正常啟動了,那么怎么訪問呢?
一般來說如果要訪問Kubernetes集群中的Service,可以通過NodePort端口映射及Ingress的方式向k8s集群外暴露訪問端口。但在Istio中采用了一種新的模型——Istio Gateway來代替Kubernetes中的Ingress資源類型。在Istio微服務體系中,所有外部流量的訪問都應該通過Gateway進來,并由Gateway轉發到對應的內部微服務!
而基于統一的控制面配置,Istio也可以集中管理Gateway網關的流量訪問規則,實現對外部流量訪問的整體管控!在前面部署Istio時,"istio-ingressgateway"入口流量網關已經作為Istio體系的一部分運行在k8s集群中了,如下:
#?kubectl?get?svc?-n?istio-system|grep?istio-ingressgateway istio-ingressgateway???LoadBalancer???10.100.69.24?????<pending>?????15021:31158/TCP,80:32277/TCP,443:30508/TCP,31400:30905/TCP,15443:30595/TCP???46h接下來我們需要設置通過該網關訪問micro-api微服務的邏輯,編寫網關部署文件(micro-gateway.yaml):
apiVersion:?networking.istio.io/v1alpha3 kind:?Gateway metadata:name:?micro-gateway spec:selector:istio:?ingressgatewayservers:-?port:number:?80name:?httpprotocol:?HTTPhosts:-?"*" --- apiVersion:?networking.istio.io/v1alpha3 kind:?VirtualService metadata:name:?micro-gateway spec:hosts:-?"*"gateways:-?micro-gatewayhttp:-?match:-?uri:exact:?/api/order/createroute:-?destination:host:?micro-apiport:number:?19090如上所示,該部署文件中定義了路由匹配規則,凡事訪問/api/order/create地址的請求都會被轉發到micro-api服務的19090端口!
配置完上述網關路由轉發規則后,我們嘗試通過訪問istio-ingressgateway來到達訪問微服務接口的效果,具體鏈路是:"外部調用->istio-ingressgateway->micro-api->micro-order"。
但是對于istio-ingressgateway的訪問,由于也是k8s內部pod,所以暫時先配置一個NodePort端口映射,具體可以通過以下命令進行操作:
export?INGRESS_PORT=$(kubectl?-n?istio-system?get?service?istio-ingressgateway?-o?jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}') export?SECURE_INGRESS_PORT=$(kubectl?-n?istio-system?get?service?istio-ingressgateway?-o?jsonpath='{.spec.ports[?(@.name=="https")].nodePort}') export?INGRESS_HOST=127.0.0.1 export?GATEWAY_URL=$INGRESS_HOST:$INGRESS_PORT以上分別設置了istio-ingressgateway的http/https的NodePort訪問端口,設置完成后具體查看nodePort端口映射情況:
#?kubectl?get?svc?-n?istio-system|grep?istio-ingressgateway istio-ingressgateway???LoadBalancer???10.100.69.24?????<pending>?????15021:31158/TCP,80:32277/TCP,443:30508/TCP,31400:30905/TCP,15443:30595/TCP???46h可以看到通過http的32277以及https的30508端口可以訪問istio-ingressgateway。具體訪問url是:http://{k8s集群IP}:32277/接口url。具體訪問效果如下:
從調用效果上可以看到,基于Istio的Service Mesh微服務體系已經運行成功!而從編程體驗上看,你似乎已經快感覺不出微服務的存在了!反正稀里糊涂的服務就調通了,服務發現怎么做到的?負載均衡怎么做到的?這些問題在不需要你關心的同時,可能也引起了你的疑惑!接下來我們通過調用日志簡單感知下調用鏈路所經過的邏輯!
04 鏈路調用日志原理
通過Postman調用返回結果后,我們分別看下鏈路所經過的服務日志!先看看istio-ingressgateway的容器日志,具體如下:
#?kubectl?logs?istio-ingressgateway-74ccb8977c-gnhbb?-n?istio-system... 2021-03-18T08:02:30.863243Z????info????xdsproxy????Envoy?ADS?stream?established 2021-03-18T08:02:30.865335Z????info????xdsproxy????connecting?to?upstream?XDS?server:?istiod.istio-system.svc:15012 [2021-03-18T08:14:00.224Z]?"POST?/api/order/create?HTTP/1.1"?200?-?"-"?66?75?7551?6144?"10.32.0.1"?"PostmanRuntime/7.26.8"?"8e8bad1d-5dd9-954b-b218-15f8c9595a24"?"10.211.55.12:32277"?"10.32.0.10:9090"?outbound|19090||micro-api.default.svc.cluster.local?10.32.0.8:57460?10.32.0.8:8080?10.32.0.1:33229?-?- [2021-03-18T08:14:32.465Z]?"POST?/api/order/create?HTTP/1.1"?200?-?"-"?66?75?3608?3599?"10.32.0.1"?"PostmanRuntime/7.26.8"?"ccf56049-88e8-9170-a1f5-93affbf6e098"?"10.211.55.12:32277"?"10.32.0.10:9090"?outbound|19090||micro-api.default.svc.cluster.local?10.32.0.8:57460?10.32.0.8:8080?10.32.0.1:33229?-?- [2021-03-18T08:16:37.242Z]?"POST?/api/order/create?HTTP/1.1"?200?-?"-"?66?75?68?67?"10.32.0.1"?"PostmanRuntime/7.26.8"?"98ecbd52-91a0-97c6-9ce6-d8f6094560e0"?"10.211.55.12:32277"?"10.32.0.10:9090"?outbound|19090||micro-api.default.svc.cluster.local?10.32.0.8:57460?10.32.0.8:8080?10.32.0.1:33229?-?-如上所示,從istio-ingressgateway的網關日志中,可以看到/api/order/create接口的訪問情況,確實是被轉發到了micro-api所在的pod ip,符合前面配置的網關路由規則。
接下來我們查看micro-api的istio-proxy代理的日志:
#?kubectl?logs?micro-api-6455654996-57t4z?istio-proxy ... [2021-03-18T08:41:10.750Z]?"POST?/order/create?HTTP/1.1"?200?-?"-"?49?75?19?18?"-"?"PostmanRuntime/7.26.8"?"886390ea-e881-9c45-b859-1e0fc4733680"?"micro-order"?"10.32.0.7:9091"?outbound|80||micro-order.default.svc.cluster.local?10.32.0.10:54552?10.99.132.246:80?10.32.0.10:39452?-?default [2021-03-18T08:41:10.695Z]?"POST?/api/order/create?HTTP/1.1"?200?-?"-"?66?75?104?103?"10.32.0.1"?"PostmanRuntime/7.26.8"?"886390ea-e881-9c45-b859-1e0fc4733680"?"10.211.55.12:32277"?"127.0.0.1:9090"?inbound|9090||?127.0.0.1:52782?10.32.0.10:9090?10.32.0.1:0?outbound_.19090_._.micro-api.default.svc.cluster.local?default ... [2021-03-18T08:47:22.215Z]?"POST?/order/create?HTTP/1.1"?200?-?"-"?49?75?78?70?"-"?"PostmanRuntime/7.26.8"?"9bbd3a3c-86c4-943f-999a-bc9a1dc02c35"?"micro-order"?"10.32.0.9:9091"?outbound|80||micro-order.default.svc.cluster.local?10.32.0.10:54326?10.99.132.246:80?10.32.0.10:44338?-?default [2021-03-18T08:47:22.173Z]?"POST?/api/order/create?HTTP/1.1"?200?-?"-"?66?75?134?129?"10.32.0.1"?"PostmanRuntime/7.26.8"?"9bbd3a3c-86c4-943f-999a-bc9a1dc02c35"?"10.211.55.12:32277"?"127.0.0.1:9090"?inbound|9090||?127.0.0.1:57672?10.32.0.10:9090?10.32.0.1:0?outbound_.19090_._.micro-api.default.svc.cluster.local?default這里我們訪問了兩次接口情況,可以看到micro-api的Sidecar代理以負載均衡的方式,分別調用了micro-order服務的兩個不同實例(打下劃線IP)!
而訪問micro-order的istio-proxy代理日志:
#?kubectl?logs?micro-order-v1-84ddc57444-dng2k?istio-proxy ... 2021-03-18T08:33:06.146178Z????info????xdsproxy????Envoy?ADS?stream?established 2021-03-18T08:33:06.146458Z????info????xdsproxy????connecting?to?upstream?XDS?server:?istiod.istio-system.svc:15012 [2021-03-18T08:34:59.055Z]?"POST?/order/create?HTTP/1.1"?200?-?"-"?49?75?8621?6923?"-"?"PostmanRuntime/7.26.8"?"b1685670-9e54-9970-a915-5c5dd18debc8"?"micro-order"?"127.0.0.1:9091"?inbound|9091||?127.0.0.1:36420?10.32.0.7:9091?10.32.0.10:54552?outbound_.80_._.micro-order.default.svc.cluster.local?default [2021-03-18T08:41:10.751Z]?"POST?/order/create?HTTP/1.1"?200?-?"-"?49?75?17?16?"-"?"PostmanRuntime/7.26.8"?"886390ea-e881-9c45-b859-1e0fc4733680"?"micro-order"?"127.0.0.1:9091"?inbound|9091||?127.0.0.1:41398?10.32.0.7:9091?10.32.0.10:54552?outbound_.80_._.micro-order.default.svc.cluster.local?default ....可以看到,請求通過micro-order的istio-proxy代理被轉到了具體的micro-order實例!
通過上面日志的分析,雖然很細節的原理可能還是有疑問,但至少可以得到一個結論,那就是在Istio的Service Mesh微服務架構中,服務的轉發、路由邏輯的確都是由Sidecar代理來干的,而且從日志中可以看到Envoy代理時刻都在保持著同控制面服務istiod的連接,并隨時通過xDS協議更新著服務治理規則!
后記
本文從Service Mesh的大致原理出發,以實際的開發案例演示了如何開發一套基于Service Mesh架構的微服務體系!應該算是能夠讓大家入門Service Mesh了!也多少彌補了一點目前網絡上Service Mesh具體實踐文章幾近空白的情況!
但不得不說,雖然Service Mesh微服務架構體系,極大的簡化了研發人員開發微服務應用的成本;但另一方面Service Mesh在將微服務治理體系下沉為基礎設施一部分的同時,也增加了對Devops工程師的要求!畢竟要玩好Service Mesh架構,不僅需要開發技能,還需要對Service Mesh的架構體系及其框架源碼有深刻的理解。除此之外,還需要對Kubernetes基礎設施特別熟悉!
總之,Service Mesh雖然先進,但是在團隊技能知識儲備尚不具備的情況下,貿然將這套體系引入生產環境,也是有風險的!所以這只是一個開始,在后面的時間里,我還會繼續分享有關Service Mesh及Istio的實踐及原理,感興趣的朋友可以持續關注下!
最后,如果需要本文所演示的示例代碼,地址如下:https://github.com/manongwudi/istio-micro-service-demo
參考文檔:
https://www.sohu.com/a/314752747_825425
https://github.com/WenDev/spring-boot-istio-demo
https://github.com/ring1012/istio-fake
更多精彩推薦 ?三個月前被 K8S 棄用,Docker 火了!獲 2300 萬美元融資?一招上手!這樣設計扛住億級流量活動系統?Kubernetes 穩定性保障手冊(極簡版)總結
以上是生活随笔為你收集整理的万字长文梳理:从0开始,步入Service Mesh微服务架构的世界的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 腾讯TAD Sim2.0领跑自动驾驶仿真
- 下一篇: 全流分析取证:高级威胁哪里跑?!