Istio 現(xiàn)在是 Service Mesh 中最火熱的項目了息堂,它主要負(fù)責(zé)對服務(wù)網(wǎng)格中的流量進行管理,包括動態(tài)服務(wù)發(fā)現(xiàn)块促、服務(wù)路由储矩、彈性功能等感耙。它作為 Service Mesh 的控制平面,配合 Envoy 作為數(shù)據(jù)平面持隧,構(gòu)成了 Service Mesh 中流量管理體系即硼。
Istio 體系中流量管理配置以及相關(guān)機制比較冗雜,本文會盡量從整體上進行描述屡拨,不摳細節(jié)只酥,以求有一個宏觀的理解。
為什么需要 Service Mesh
我們首先要明白為什么會催生出 service mesh 這樣的架構(gòu)呀狼,而要理解這一點裂允,我們需要對比 2 種東西,即傳統(tǒng)微服務(wù)架構(gòu)以及原生 k8s 架構(gòu)哥艇。
傳統(tǒng)微服務(wù)
我們知道現(xiàn)有的微服務(wù)架構(gòu)已經(jīng)比較成熟了绝编,擁有完善的服務(wù)注冊發(fā)現(xiàn)與服務(wù)治理功能(包括限流熔斷、負(fù)載均衡貌踏、流量路由等)十饥,那么仍然困擾我們的是什么呢?
現(xiàn)有的微服務(wù)框架幾乎都是集成于客戶端 sdk 的祖乳,開發(fā)者在自己的應(yīng)用中集成微服務(wù)框架 sdk逗堵,從而具有服務(wù)治理相關(guān)功能,那么這會帶來 2 個幾乎無法避免的問題:應(yīng)用需要不斷更新代碼從而更新客戶端 sdk眷昆,而這中間可能出現(xiàn)各種依賴沖突蜒秤;應(yīng)用集成并使用客戶端 sdk 依賴于開發(fā)者的素質(zhì),常常導(dǎo)致效率低下亚斋。
這種情況下作媚,我們自然而然地會想到使用 sidecar,將原先在 sdk 中的邏輯搬到 sidecar 上帅刊,在運維的時候與應(yīng)用服務(wù)集成在一起掂骏,形成一種較 sdk 集成更先進的邊車模式。這種方式對應(yīng)用代碼幾乎沒有侵入厚掷,并且也不受開發(fā)者的水平限制弟灼,完全做到了控制與邏輯的分離。當(dāng)然作為易用性的代價冒黑,我們會失去一些性能田绑。
sidecar 模式是 service mesh 相較微服務(wù)架構(gòu)最主要的優(yōu)點之一,而這在 service mesh 架構(gòu)中也稱為"數(shù)據(jù)平面"抡爹。
原生 k8s
這里我們思考的是在 k8s 之上掩驱,我們還有什么不滿足?k8s 使用 kube-dns 以及 kube-proxy 配合 service 的概念支持了服務(wù)的注冊與發(fā)現(xiàn),意味著我們有了可以在 k8s 上構(gòu)建微服務(wù)的基礎(chǔ)欧穴。但在實際生產(chǎn)中民逼,我們往往需要對流量更精細的管控,我們希望對單個 pod 或者一組 pod 的流量進行管理涮帘,而不僅僅是 service 的維度拼苍。例如一個需求是我們需要將 endpoints 下的一部分 pod 作為 v1 版本,其余作為 v2 版本调缨,而/v1 前綴的請求都將調(diào)度到 v1 版本疮鲫,/v2 前綴的請求調(diào)度到 v2 版本。
sidecar 作為數(shù)據(jù)平面與每一個應(yīng)用部署在同一 pod 中弦叶,這意味著我們可以做到對 pod 級別的流量做管理了俊犯,而具體實現(xiàn)這一邏輯的就是 service mesh 中的另一層:"控制平面"了。
Istio 的架構(gòu)
Istio 是目前 Service Mesh 領(lǐng)域最火熱的架構(gòu)伤哺,我們來看看 Istio 的整體架構(gòu)燕侠。
Istio 分為數(shù)據(jù)平面以及控制平面,數(shù)據(jù)平面以 sidecar 的形式與應(yīng)用部署在一起立莉,承載其流量的發(fā)送與接收绢彤,而控制平面則是通過配置和控制消息來組織編排網(wǎng)絡(luò)的邏輯,并下發(fā)給數(shù)據(jù)平面桃序。
我們簡單講一下其中的組件:
Envoy: Envoy 是 c++開發(fā)的高性能代理杖虾,在 Istio 中被用于數(shù)據(jù)平面(圖中即 Proxy)烂瘫,控制應(yīng)用的入站和出站流量媒熊,而在 Istio 中,它擁有了動態(tài)服務(wù)發(fā)現(xiàn)坟比、負(fù)載均衡芦鳍、Http2/gRpc 代理、熔斷器葛账、健康檢查柠衅、故障注入等多種特性,當(dāng)然這些都需要控制平面配合下發(fā)指令實現(xiàn)籍琳。
Mixer: Mixer 是 Istio 控制平面的組件之一菲宴,用于執(zhí)行訪問控制和遙測(在最新版本已經(jīng)沒有了)
Pilot: Pilot 就是實現(xiàn)流量管理的關(guān)鍵組件了,為 Envoy 提供服務(wù)發(fā)現(xiàn)趋急、智能路由喝峦、彈性功能(超時/重試/熔斷),我們在后面也會詳細描述呜达。
Citadel: Citadel 通過內(nèi)置的身份和證書管理谣蠢,可以支持強大的服務(wù)到服務(wù)以及最終用戶的身份驗證和流量加密。
Galley: Galley 是 Istio 的配置驗證、提取眉踱、處理和分發(fā)組件挤忙。
Istio 中流量管理的基礎(chǔ)概念
流量管理是 Istio 中最基礎(chǔ)和最重要的功能了。我們之前說到我們的真實訴求往往是希望指定某個 pod 或者一組特定的 pod 接收流量谈喳,最基礎(chǔ)的方式是通過人肉運維搞定册烈,但更優(yōu)雅地是將其委托給 Istio 本身,Istio 會通過 Pilot 與 Envoy 代理搞定這一切叁执。
怎么搞定這一切呢茄厘,既然 kubernetes 中 service 的抽象概念滿足不了我們的需求,很自然地我們嘗試去抽象一些更上層的概念來解決問題谈宛。Istio 也是如此次哈,我們來講一下其中主要的一些基本概念(至于具體的配置,由于篇幅限制跳過):
- VirtualService: 顧名思義吆录,其實可以理解為對 service 的一層抽象窑滞,用于定義路由規(guī)則,控制流量路由到匹配的 service 子集(VirtualService)恢筝。同時你可以為每個 VirtualService 設(shè)置一些獨立的網(wǎng)絡(luò)彈性屬性哀卫,例如超時、重試等撬槽。
- DestinationRule: 一般是 VirtualService 路由生效后定義目的服務(wù)的策略此改,包括斷路器、負(fù)載均衡等侄柔。當(dāng)然它也可以定義可路由子集共啃,即 VirtualService 中的路由規(guī)則可以完全委托給 DestinationRule。
- ServiceEntry: 這是用于將 Istio 服務(wù)網(wǎng)格外部的服務(wù)添加到內(nèi)部服務(wù)注冊的暂题,也就是說你能訪問外部服務(wù)了移剪。
- Gateway: 用于控制南北流量的網(wǎng)關(guān)了,將 VirtualService 綁定到 Gateway 上薪者,就可以控制進入的 HTTP/TCP 流量了纵苛。
- EnvoyFilter: 主要為 Envoy 配置過濾器,用于動態(tài)擴展 Envoy 的能力言津。
控制面 Pilot 的流量管理
在 Istio 的控制平面中攻人,Pilot 是負(fù)責(zé)流量管理的組件,也是本文的主角悬槽。Pilot 整體架構(gòu)如下(來自old_pilot_repo怀吻,不過整體出入不大):
我們看到 pilot 主要包含 2 個組件,即 Discovery services 和 Agent陷谱。
Agent: 該進程對應(yīng)的是 pilot-agent烙博,負(fù)責(zé)生產(chǎn) Envoy 配置文件和管理 Envoy 生命周期瑟蜈。它和 Proxy(即 Envoy)以及具體應(yīng)用 Service A/B 在同一 Pod 中部署。
Discovery services: 該進程對應(yīng)的是 pilot-discovery渣窜,負(fù)責(zé) pilot 中最關(guān)鍵的邏輯铺根,即服務(wù)發(fā)現(xiàn)與流量管理。Discovery services 一般是和應(yīng)用分開使用單獨的 deployment 部署的乔宿。它會和 2 個類型的數(shù)據(jù)打交道位迂。一是圖中 k8s API Server 中的服務(wù)信息,即 service详瑞、endpoint掂林、pod、node 等資源坝橡。其二就是 K8s API Server 中的一些 CRD 資源泻帮,包括了上述的 VritualService、DestinationRule计寇、Gateway锣杂、ServiceEntry 等 Istio 控制面的流量規(guī)則配置信息。然后 Discovery services 會將這兩部分?jǐn)?shù)據(jù)轉(zhuǎn)換為數(shù)據(jù)面可以理解的格式番宁,并通過標(biāo)準(zhǔn)的 API 下發(fā)到各個數(shù)據(jù)面 (Envoy)sidecar 中元莫。
Pilot Agent
pilot agent 它不是本文的主角,我們簡單介紹一下蝶押。它主要的工作包括:
- 生成 envoy 的相關(guān)配置踱蠢,這里是指少部分的靜態(tài)配置,畢竟大多動態(tài)配置是通過標(biāo)準(zhǔn) xDS 接口從 Pilot 獲取的棋电。
- 負(fù)責(zé) envoy 進程的監(jiān)控與管理工作茎截,比如 envoy 掛了負(fù)責(zé)重啟 envoy,或者配置變更后負(fù)責(zé) reload envoy离陶。
- 啟動 envoy 進程
Pilot-discovery
pilot-discovery 是 pilot 中的關(guān)鍵組件稼虎,我們進行較為詳細的描述衅檀。
整體模型
pilot-discovery 的整體模型如下所示:
pilot-discovery 有兩部分輸入信息:
- 來自 istio 控制平面的信息招刨,也就是圖中的 Rules API,包括 VirtualService哀军、DestinationRule 等沉眶,這些信息以 kubernetes CRD 資源的形式保存在 kubernetes api server 中。
- 來自于服務(wù)注冊中心的服務(wù)注冊信息杉适,也就是圖中的 kubernetes谎倔、mesos、cloud foundry 等猿推。當(dāng)然我們默認(rèn)都認(rèn)為是 kubernetes片习,下文相關(guān)描述也默認(rèn) kubernetes捌肴。
為了支持對不同服務(wù)注冊中心的支持,所以需要有一個統(tǒng)一的數(shù)據(jù)存儲模型藕咏,即 Abstract Model状知,和一個轉(zhuǎn)換器 Platform Adapter 來實現(xiàn)各服務(wù)注冊中心數(shù)據(jù)到 Abstract Model 的數(shù)據(jù)轉(zhuǎn)換。當(dāng)然除了注冊中心數(shù)據(jù)外孽查,Platform Adapter 還需將 VirtualService饥悴、DestinationRule 等 CRD 資源信息轉(zhuǎn)換成 Abstract Model。
基于統(tǒng)一數(shù)據(jù)存儲模型 Abstract Model盲再,pilot-discovery 為數(shù)據(jù)面提供了控制信息服務(wù)西设,也就是所謂的 xds api 服務(wù),從而得以將控制信息下發(fā)到數(shù)據(jù)面 envoy答朋。
接下來我們講講 pilot-discovery 的一個整體流程贷揽。
初始化工作
pilot-discovery 首先干的事兒是進行一些初始化,初始化的工作內(nèi)容包括:
- 創(chuàng)建一個 kubernetes client梦碗。想要與 kubernetes api server 交互那自然需要 kubeClient擒滑,kubeClient 有 2 種創(chuàng)建方式。一種是使用特定的 kubeConfig 文件從而聯(lián)通 kubernetes api server叉弦。另一種是使用 in cluster config 方式丐一,也就是如果本身在 kubernetes 集群中,可以通過感知集群上下文的方式自動完成配置淹冰。
- 多集群配置】獬担現(xiàn)實情況下,我們可能會有多個 kubernetes 集群樱拴,一種方式是每個 kubernetes 集群搭建一套 Istio柠衍,但也可能是多個集群共用一個 Istio。那么 Istio 就需要連接其他遠程 kubernetes 集群了晶乔,它被稱為 remote cluster珍坊,Istio 在一個 map 中保存每個 remote cluster 的遠程訪問信息。
- 配置和 Mixer 相關(guān)的東西正罢,這里不詳細描述阵漏。
- 初始化和配置存儲中心的連接。之前說過 Istio 中的諸多流量管理相關(guān)的配置翻具,包括 VirtualService履怯、DestinationRule 等,這些都是需要保存在存儲中心(不是指 Etcd)的裆泳。Istio 支持將這些配置通過文件存儲或者 kubernetes CRD 的方式進行存儲叹洲,當(dāng)然基本上是后者。當(dāng) CRD 資源完成注冊之后工禾,還會創(chuàng)建一個 config controller 來對 CRD 資源的 crud 事件進行處理运提。
- 配置和注冊中心的連接蝗柔。這里的注冊中心基本上指的是 kubernetes,如上所述民泵,我們需要對 kubernetes 中的 pod诫咱、service、endpoints 等信息進行監(jiān)聽洪灯,當(dāng)然我們也需要一個 service controller 來進行事件處理坎缭。
- 初始化 pilot-discovery 服務(wù)。由于 envoy sidecar 是通過連接 pilot-discovery 服務(wù)來獲取服務(wù)注冊發(fā)現(xiàn)信息以及流量控制策略的签钩,所以這里需要初始化 discovery 服務(wù)掏呼,包括提供 REST 協(xié)議的服務(wù)以及提供 gRPC 協(xié)議的服務(wù)。
- 一些健康檢查以及監(jiān)控铅檩。
流量策略等 CRD 信息處理
我們之前講 Istio 中流量策略等相關(guān)規(guī)則都是以 kubernetes CRD 的形式保存在 kubernetes api server 背后的 Etcd 中的憎夷,包括 VirtualService、DestinationRule昧旨。這些 CRD 資源當(dāng)完成注冊后拾给,還創(chuàng)建了 config controller 來處理 CRD 的事件。
config controller 主要內(nèi)容包括對每個 CRD 資源實現(xiàn)一個 list/watch兔沃,然后為其 Add蒋得、Update、Delete 等事件創(chuàng)建一個統(tǒng)一的流程框架乒疏,即將 CRD 對象事件封裝成一個 Task 并 push 到一個 queue 里额衙。隨后啟動協(xié)程依次處理 CRD 資源事件,即從 queue 中取出 Task 對象怕吴,并調(diào)用其 ChainHandler窍侧。
服務(wù)注冊信息處理
服務(wù)注冊信息指的默認(rèn)為 kubernetes 服務(wù)注冊信息(當(dāng)然也支持其他注冊中心,但比較小眾)转绷,即 pod伟件、service、endpoints议经、node 等資源斧账。我們之前講 Pilot 創(chuàng)建了一個 service controller 來實現(xiàn)對 kubernetes 資源的監(jiān)控和處理。
service controller 的邏輯和 config controller 基本一致爸业。為每種 kubernetes 資源都實現(xiàn)了一個 list/watch其骄,然后將其 Add亏镰、Update扯旷、Delete 事件封裝成 Task 對象 push 到 queue 中,隨后啟動協(xié)程從 queue 中取出并調(diào)用 CHainHandler索抓。
暴露針對 Envoy 的控制信息服務(wù)
我們知道钧忽,Envoy 是通過與 Pilot 暴露的信息服務(wù)交互從而得到服務(wù)發(fā)現(xiàn)/路由策略等相關(guān)信息的毯炮。pilot-discovery 創(chuàng)建了 gRPC 協(xié)議的 discovery 服務(wù),通過 xDS api 與 Envoy 交互耸黑,包括 eds桃煎、cds、rds大刊、lds 等为迈。具體細節(jié)我們之后描述。
數(shù)據(jù)面 Envoy 與 xDS 服務(wù)
數(shù)據(jù)平面是 sidecar 方式部署的智能代理缺菌,在 Istio 體系中葫辐,數(shù)據(jù)面往往是由 Envoy 擔(dān)任。Envoy 可以調(diào)整控制服務(wù)網(wǎng)格之間的網(wǎng)絡(luò)通信伴郁,是控制面流量管理的實際執(zhí)行者耿战。
Envoy xDS 是為 Istio 控制平面與數(shù)據(jù)平面通信設(shè)計的一套 api 協(xié)議,也就是下發(fā)流量管理配置的關(guān)鍵焊傅。
Envoy 中的基本概念
首先在了解 xDS 之前剂陡,我們需要了解 Envoy 中的一些基本概念:
- Host: 能夠進行網(wǎng)絡(luò)通信的實體。在 Envoy 中主機是指邏輯網(wǎng)絡(luò)應(yīng)用程序狐胎,一塊物理硬件上可以運行多個主機鸭栖,只要它們是獨立尋址的。
- Downstream: 下游主機連接到 Envoy握巢,發(fā)送請求并接受響應(yīng)纤泵。
- Upstream: 上游主機獲取來自 Envoy 的連接請求和響應(yīng)。
- Cluster: 表示 Envoy 連接到的一組上游主機集群镜粤。Envoy 通過服務(wù)發(fā)現(xiàn)發(fā)現(xiàn)集群中的成員捏题,Envoy 可以通過主動運行狀況檢查來確定集群成員的健康狀況。
- Endpoint: 即上游主機標(biāo)識肉渴,包括端點的地址和健康檢查配置公荧。
- Listener: 監(jiān)聽器,Envoy 暴露一個或多個監(jiān)聽器給下游主機(downstream)連接同规,當(dāng)監(jiān)聽器監(jiān)聽到請求時候循狰,對請求的處理會全部抽象為 Filter,例如 ReadFilter券勺、WriteFilter绪钥、HttpFilter 等。
- Listener filter: 過濾器关炼,在 Envoy 指一些可插拔和可組合的邏輯處理層程腹,是 Envoy 的核心邏輯處理單元。
- Http Router Table: 即 Http 路由規(guī)則儒拂,例如請求的什么域名轉(zhuǎn)發(fā)到什么集群(cluster)寸潦。
Envoy 架構(gòu)
Envoy 整體架構(gòu)如下所示:
Envoy 的工作流程為下游主機(Host A)發(fā)送請求至上游主機(Host B/C/D)色鸳,Envoy 攔截請求(代理注入及流量劫持我們不詳細描述),Listener 監(jiān)聽到下游主機請求后將請求內(nèi)容抽象為 Filter Chains见转,并根據(jù)流量策略相關(guān)的配置信息路由至相應(yīng)的上游主機集群(Cluster)命雀,從而完成路由轉(zhuǎn)發(fā)、負(fù)載均衡斩箫、流量策略等能力吏砂。
上述的流量策略相關(guān)的配置信息主要以動態(tài)配置的方式由 xDS api 實現(xiàn),也是我們關(guān)注的重點乘客。此外赊抖,Envoy 作為流量代理還可以將部分配置以靜態(tài)配置的方式寫入文件中,啟動時候直接加載寨典。
xDS 服務(wù)
所謂 xDS氛雪,這里的 x 是一個代詞,在 Istio 中耸成,主要包括 cds(cluster discovery service)报亩、eds(endpoint discovery service)、rds(route discovery service)井氢、lds(listener discovery service)弦追,ads(aggregated discovery service)則是對這些 api 的一個封裝。
在以上 api 引入的概念中花竞,endpoint 表示一個具體的應(yīng)用實例劲件,對應(yīng) ip 和端口,類似 kubernetes 中的一個 pod约急。cluster 是一個應(yīng)用集群零远,對應(yīng)一個或多個 endpoint,類似 kubernetes 中 service 的概念(實際上應(yīng)該更小一些)厌蔽。route 即當(dāng)我們做類似灰發(fā)牵辣、金絲雀發(fā)布時,同一個服務(wù)會運行多個版本奴饮,每個版本對應(yīng)一個 cluster纬向,這時就需要通過 route 規(guī)則規(guī)定請求如何路由到某個版本的 cluster 上。
在實際請求過程中戴卜,xDS 接口采用的順序如下:
- CDS 首先更新 Cluster 數(shù)據(jù)
- EDS 更新相應(yīng) Cluster 的 Endpoint 信息
- LDS 更新 CDS/EDS 相應(yīng)的 Listener
- RDS 最后更新新增 Listener 相關(guān)的 Route 配置
- 刪除不再使用的 CDS/EDS 配置
不過目前這個流程都已整合在 ADS 中逾条,即聚合的服務(wù)發(fā)現(xiàn),ADS 通過一個 gRPC 流以保證各個 xDS 接口的調(diào)用順序投剥。
我們之前講過 pilot-discovery 的工作包括會創(chuàng)建一個 discovery 服務(wù)∈χ現(xiàn)在它會與每一個 Envoy 建立一個雙向 streaming 的 gRPC 連接,Envoy proxy 則會通過 ADS 接口按照上述的調(diào)用邏輯發(fā)起請求,并將 Pilot 的流量管理關(guān)鍵概念 VirtualService危彩、DestinationRule 等組裝成 cluster攒磨、endpoint泳桦、router汤徽、listener 等 envoy 配置。最終這些動態(tài)配置會作用在每一個 Envoy Proxy 上灸撰,當(dāng) Envoy listener 監(jiān)聽到下游主機請求時谒府,就可以根據(jù)這些配置完成實際的動態(tài)服務(wù)發(fā)現(xiàn)、流量管理等功能浮毯。