歡迎光臨
每天分享高質量文章

深入解讀Service Mesh背後的技術細節

概念的多,講技術細節的少,所以專門寫一篇文章,來解析Service Mesh背後的技術細節。

Service Mesh是Kubernetes支撐微服務能力拼圖的最後一塊

在上一篇文章《為什麼Kubernetes天然適合微服務?》中我們提到,Kubernetes是一個奇葩所在,他的元件複雜,概念複雜,在沒有實施微服務之前,你可能會覺得為什麼Kubernetes要設計的這麼複雜,但是一旦你要實施微服務,你會發現Kubernetes中的所有概念,都是有用的。
在我們微服務設計的是個要點中,我們會發現Kubernetes都能夠有相應的元件和概念,提供相應的支援。
其中最後的一塊拼圖就是服務發現,與熔斷限流降級。
眾所周知,Kubernetes的服務發現是透過Service來實現的,服務之間的轉發是透過kube-proxy下發iptables規則來實現的,這個只能實現最基本的服務發現和轉發能力,不能滿足高併發應用下的高階的服務特性,比較SpringCloud和Dubbo有一定的差距,於是Service Mesh誕生了,他期望講熔斷,限流,降級等特性,從應用層,下沉到基礎設施層去實現,從而使得Kubernetes和容器全面接管微服務。


以Istio為例講述Service Mesh中的技術關鍵點

就如SDN一樣,Service Mesh將服務請求的轉發分為控制面和資料面,因而分析他,也是從資料面先分析轉發的能力,然後再分析控制面如何下發命令。今天這篇文章重點講述兩個元件Envoy和Pilot。

一切從Envoy開始
我們首先來看,如果沒有融入Service Mesh,Envoy本身能夠做什麼事情呢?
Envoy是一個高效能的C++寫的proxy轉發器,那Envoy如何轉發請求呢?需要定一些規則,然後按照這些規則進行轉發。
規則可以是靜態的,放在配置檔案中的,啟動的時候載入,要想重新載入,一般需要重新啟動,但是Envoy支援熱載入和熱重啟,一定程度上緩解了這個問題。
當然最好的方式是規則設定為動態的,放在統一的地方維護,這個統一的地方在Envoy眼中看來稱為Discovery Service,過一段時間去這裡拿一下配置,就修改了轉發策略。
無論是靜態的,還是動態的,在配置裡面往往會配置四個東西。
一是Listener,也即Envoy既然是Proxy,專門做轉發,就得監聽一個埠,接入請求,然後才能夠根據策略轉發,這個監聽的埠稱為Listener。
二是Endpoint,是標的的IP地址和埠,這個是Proxy最終將請求轉發到的地方。
三是Cluster,一個Cluster是具有完全相同行為的多個Endpoint,也即如果有三個容器在執行,就會有三個IP和埠,但是部署的是完全相同的三個服務,他們組成一個Cluster,從Cluster到Endpoint的過程稱為負載均衡,可以輪詢等。
四是Route,有時候多個Cluster具有類似的功能,但是是不同的版本號,可以透過route規則,選擇將請求路由到某一個版本號,也即某一個Cluster。
這四個的靜態配置的例子如下:
如圖所示,Listener被配置為監聽本地127.0.0.1的10000介面,Route配置為某個url的字首轉發到哪個Cluster,Cluster裡面配置負載均衡策略,hosts裡面是所有的Endpoint。
如果你想簡單的將Envoy使用起來,不用什麼Service Mesh,一個行程,加上這個配置檔案,就可以了,就能夠轉發請求了。
對於動態配置,也應該配置發現中心,也即Discovery Service,對於上述四種配置,各對應相應的DS,所以有LDS、RDS、CDS、EDS。
動態配置的例子如下:

控制面Pilot的工作樣式
資料面Envoy可以透過加裝靜態配置檔案的方式執行,而動態資訊,需要從Discovery Service去拿。
Discovery Service就是部署在控制面的,在Istio中,是Pilot。
如圖為Pilot的架構,最下麵一層是Envoy的API,就是提供Discovery Service的API,這個API的規則由Envoy定,但是不是Pilot呼叫Envoy,而是Envoy去主動呼叫Pilot的這個API。
Pilot最上面一層稱為Platform Adapter,這一層是乾什麼的呢?這一層不是Kubernetes,Mesos呼叫Pilot,而是Pilot透過呼叫Kubernetes來發現服務之間的關係。
這是理解Istio比較繞的一個點。也即Pilot使用Kubernetes的Service,僅僅使用它的服務發現功能,而不使用它的轉發功能,Pilot透過在Kubernetes裡面註冊一個Controller來監聽事件,從而獲取Service和Kubernetes的Endpoint以及Pod的關係,但是在轉發層面,就不會再使用kube-proxy根據Service下發的iptables規則進行轉發了,而是將這些對映關係轉換成為Pilot自己的轉發模型,下發到Envoy進行轉發,Envoy不會使用kube-proxy的那些iptables規則。這樣就把控制面和資料面徹底分離開來,服務之間的相互關係是管理面的事情,不要和真正的轉發系結在一起,而是繞到Pilot後方。
Pilot另外一個對外的介面是Rules API,這是給管理員的介面,管理員透過這個介面設定一些規則,這些規則往往是應用於Routes,Clusters,Endpoints的,而都有哪些Clusters和Endpoints,是由Platform Adapter這面透過服務發現得到的。
自動發現的這些Clusters和Endpoints,外加管理員設定的規則,形成了Pilot的資料模型,其實就是他自己定義的一系列資料結構,然後透過Envoy API暴露出去,等待Envoy去拉取這些規則。
常見的一種人工規則是Routes,透過服務發現,Pilot可以從Kubernetes那裡知道Service B有兩個版本,一般是兩個Deployment,屬於同一個Service,管理員透過呼叫Pilot的Rules API,來設定兩個版本之間的Route規則,一個佔99%的流量,一個佔1%的流量,這兩方面資訊形成Pilot的資料結構模型,然後透過Envoy API下發,Envoy就會根據這個規則設定轉發策略了。
另一個常用的場景就是負載均衡,Pilot透過Kubernetes的Service發現Service B包含一個Deployment,但是有三個副本,於是透過Envoy API下發規則,使得Envoy在這三個副本之間進行負載均衡,而非透過Kubernetes本身Service的負載均衡機制。


以Istio為例解析Service Mesh的技術細節

瞭解了Service Mesh的大概原理,接下來我們透過一個例子來解析其中的技術細節。
凡是試驗過Istio的同學都應該嘗試過下麵這個BookInfo的例子,不很複雜,但是麻雀雖小五臟俱全。
在這個例子中,我們重點關註ProductPage這個服務,對Reviews服務的呼叫,這裡涉及到路由策略和負載均衡。

ProductPage就是個Python程式
ProductPage是一個簡單的用Python寫的提供RESTful API的程式。
在裡面定義了很多的Route,來接收API請求,並做相應的操作。
在需要請求其他服務,例如reviews、ratings的時候,則需要向後方發起RESTful呼叫。
從程式碼可以看出,ProductPage對於後端的呼叫,都是透過域名來的。
對於ProductPage這個程式來講,他覺得很簡單,透過這個域名就可以呼叫了,既不需要透過服務發現系統獲取這個域名,也不需要關心轉發,更意識不到自己是部署在Kubernetes上的,是否用了Service Mesh,所以服務之間的通訊完全交給了基礎設施層。
透過Kubernetes編排ProductPage
有了ProductPage程式,接下來就是將他部署到Kubernetes上,這裡沒有什麼特殊的,用的就是Kubernetes預設的編排檔案。
首先定義了一個Deployment,使用bookinfo的容器映象,然後定義一個Service,用於這個Deployment的服務發現。
透過Kubernetes編排reviews

這個稍微有些複雜,定義了三個Deployment,但是版本號分別為V1、V2、V3,但是label都是app: reviews。
最後定義了一個Service,對應的label是app: reviews,作為這三個Deployment的服務發現。
istioctl對ProductPage進行定製化之一:嵌入proxy_init作為InitContainer

到目前為止,一切正常,接下來就是見證奇跡的時刻,也即Istio有個工具istioctl可以對於yaml檔案進行定製化。
定製化的第一項就是添加了一個initContainer,這種型別的Container可以做一些初始化的工作後,成功退出,Kubernetes不會保持他長期執行。
在這個InitContainer裡面做什麼事情呢?
我們登入進去發現,在這個InitContainer裡面運行了一個shell指令碼。
就是這個shell指令碼在容器裡面寫入了大量的iptables規則。
首先定義的一條規則是ISTIO_REDIRECT轉發鏈,這條鏈不分三七二十一,都將網路包轉發給Envoy的15000埠。
但是一開始這條鏈沒有被掛到iptables預設的幾條鏈中,所以不起作用。
接下來就是在PREROUTING規則中,使用這個轉發鏈,從而進入容器的所有流量,都被先轉發到Envoy的15000埠。
Envoy作為一個代理,已經被配置好了,將請求轉發給ProductPage程式。
ProductPage程式接受到請求,會轉向呼叫外部的reviews或者ratings,從上面的分析我們知道,ProductPage只是做普通的域名呼叫。
當ProductPage往後端進行呼叫的時候,就碰到了output鏈,這個鏈會使用轉發鏈,將所有出容器的請求都轉發到Envoy的15000埠。
這樣無論是入口的流量,還是出口的流量,全部用Envoy做成了漢堡包。
Envoy根據服務發現的配置,知道reviews或者ratings如何訪問,於是做最終的對外呼叫。
這個時候iptables規則會對從Envoy出去的流量做一個特殊處理,允許他發出去,不再使用上面的output規則。
istioctl對ProductPage進行定製化之二:嵌入Proxy容器作為SideCar

Istioctl做的第二項定製化是,嵌入Proxy容器作為SideCar。
這個似乎看起來更加複雜,但是進入容器我們可以看到,啟動了兩個行程。
一個是我們熟悉的Envoy,他有一個配置檔案是/etc/istio/proxy/envoy-rev0.json。
我們再前面講述Envoy的時候說過,有了配置檔案,Envoy就能夠轉發了,我們先來看看配置檔案裡面都有啥。
在這裡面配置了Envoy的管理埠,等一下我們會透過這個埠檢視Envoy被Pilot下發了哪些轉發策略。
然後就是動態資源,也即從各種discovery service去拿轉發策略。
還有就是靜態資源,也即靜態配置的,需要重啟才能載入的。
這就是pilot-agent的作用,他是Envoy的一個簡單的管理器,因為有些靜態資源,如果TLS的證書,Envoy還不支援動態下發,因而需要重新靜態配置,然後pilot-agent負責將Envoy進行熱重啟載入。
好在Envoy有良好的熱重啟機制,重啟的時候,會先啟動一個備用行程,將轉發的統計資料透過shared memory在兩個行程間共享。
深入解析Pilot的工作機制

Pilot的工作機制展開後如圖所示。
Istio config是管理員透過管理介面下發的轉發規則。
Service Discovery模組對於Kubernetes來講,就是建立了一個Controller來監聽Service建立和刪除的事件,當Service有變化時,會通知Pilot,Pilot會根據變化更新下發給Envoy的規則。
Pilot將管理員輸入的轉發策略配置和服務發現的當前狀態,變成Pilot自己的資料結構模型,然後暴露成Envoy的API,由於是Envoy來呼叫,因而要實現一個服務端,這裡有lds、rds、cds、eds。
接下來我們看,在Pilot上配置Route之後會發生什麼?
如圖,我們將所有的流量都發給版本1。
我們檢視Envoy的管理埠,可以看到只配置了reviews的v1。
當我們修改路由為v1和v3比例是五十比五十。
可以看到Envoy的管理埠,路由有了兩個版本的配置,也對應後端的兩個IP地址。
本文轉載自公眾號: 劉超的通俗雲端計算,點選檢視原文

Kubernetes專案實戰訓練營

Kubernetes專案實戰訓練將於2018年8月17日在深圳開課,3天時間帶你係統掌握Kubernetes本次培訓包括:Docker介紹、Docker映象、網路、儲存、容器安全;Kubernetes架構、設計理念、常用物件、網路、儲存、網路隔離、服務發現與負載均衡;Kubernetes核心元件、Pod、外掛、微服務、雲原生、Kubernetes Operator、叢集災備、Helm等,點選下方圖片檢視詳情。

贊(0)

分享創造快樂