天天看點

Istio入門實戰與架構原理——使用Docker Compose搭建Service Mesh

本文将介紹如何使用Docker Compose搭建Istio。Istio号稱支援多種平台(不僅僅Kubernetes)。然而,官網上非基于Kubernetes的教程仿佛不是親兒子,寫得非常随便,不僅缺了一些内容,而且還有坑。本文希望能補實這些内容。我認為在學習Istio的過程中,相比于Kubernetes,使用Docker Compose部署更能深刻地了解Istio各個元件的用處以及他們的互動關系。在了解了這些後,可以在其他環境,甚至直接在虛拟機上部署Istio。當然,生産環境建議使用Kubernetes等成熟的容器架構。

本文使用官方的Bookinfo示例。通過搭建Istio控制平面,部署Bookinfo應用,最後配置路由規則,展示Istio基本的功能和架構原理。

本文涉及的名詞、用到的端口比較多。Don't panic.

為了防止不提供原網址的轉載,特在這裡加上原文連結: https://www.cnblogs.com/skabyy/p/10668079.html

安裝Docker和Docker Compose。

安裝<code>kubectl</code>(Kubernetes的用戶端)。

下載下傳Istio release 1.1.0并解壓。注意,這裡下載下傳的是Linux的版本。即使你用的Windows或者OSX作業系統,也應該下載下傳Linux版本的Istio,因為我們要放到Docker容器裡去運作的。

在微服務架構中,通常除了實作業務功能的微服務外,我們還會部署一系列的基礎元件。這些基礎元件有些會入侵微服務的代碼。比如服務發現需要微服務啟動時注冊自己,鍊路跟蹤需要在HTTP請求的headers中插入資料,流量控制需要一整套控制流量的邏輯等。這些入侵的代碼需要在所有的微服務中保持一緻。這導緻了開發和管理上的一些難題。

為了解決這個問題,我們再次應用抽象和服務化的思想,将這些需要入侵的功能抽象出來,作為一個獨立的服務。這個獨立的服務被稱為sidecar,這種模式叫Sidecar模式。對每個微服務節點,都需要額外部署一個sidecar來負責業務邏輯外的公共功能。所有的出站入站的網絡流量都會先經過sidecar進行各種處理或者轉發。這樣微服務的開發就不需要考慮業務邏輯外的問題。另外所有的sidecar都是一樣的,隻需要部署的時候使用合适的編排工具即可友善地為所有節點注入sidecar。

Sidecar不會産生額外網絡成本。Sidecar會和微服務節點部署在同一台主機上并且共用相同的虛拟網卡。是以sidecar和微服務節點的通信實際上都隻是通過記憶體拷貝實作的。
Istio入門實戰與架構原理——使用Docker Compose搭建Service Mesh
圖檔來自:Pattern: Service Mesh

Sidecar隻負責網絡通信。還需要有個元件來統一管理所有sidecar的配置。在Service Mesh中,負責網絡通信的部分叫資料平面(data plane),負責配置管理的部分叫控制平面(control plane)。資料平面和控制平面構成了Service Mesh的基本架構。

Istio入門實戰與架構原理——使用Docker Compose搭建Service Mesh

Istio的資料平面主要由Envoy實作,控制平面則主要由Istio的Pilot元件實作。

如果你使用Linux作業系統,需要先配置<code>DOCKER_GATEWAY</code>環境變量。非Linux系統不要配。

到<code>install/consul</code>目錄下,使用<code>istio.yaml</code>檔案啟動控制平面:

根據自己的網絡情況(你懂得),可以把<code>istio.yaml</code>中的鏡像<code>gcr.io/google_containers/kube-apiserver-amd64:v1.7.3</code>換成<code>mirrorgooglecontainers/kube-apiserver-amd64:v1.7.3</code>。

用指令<code>docker-compose -f istio.yaml ps</code>看一下是不是所有元件正常運作。你可能(大機率)會看到pilot的狀态是<code>Exit 255</code>。使用指令<code>docker-compose -f istio.yaml logs | grep pilot</code>檢視日志發現,<code>pilot</code>啟動時通路<code>istio-apiserver</code>失敗。這是因為Docker Compose是同時啟動所有容器的,在<code>pilot</code>啟動時,<code>istio-apiserver</code>也是處于啟動狀态,是以通路<code>istio-apiserver</code>就失敗了。

等<code>istio-apiserver</code>啟動完成後,重新運作啟動指令就能成功啟動<code>pilot</code>了。你也可以寫一個腳本來自動跑兩次指令:

配置<code>kubectl</code>,讓<code>kubectl</code>使用我們剛剛部署的<code>istio-apiserver</code>作為服務端。我們後面會使用<code>kubectl</code>來執行配置管理的操作。

部署完成後,使用位址<code>localhost:8500</code>可以通路<code>consul</code>,使用位址<code>localhost:9411</code>可以通路<code>zipkin</code>。

在下一步之前,我們先來看一下控制平面都由哪些元件組成。下面是<code>istio.yaml</code>檔案的内容:

控制平面部署了這幾個元件(使用<code>istio.yaml</code>裡寫的名稱):

<code>etcd</code>:分布式key-value存儲。Istio的配置資訊存在這裡。

<code>istio-apiserver</code>:實際上是一個<code>kube-apiserver</code>,提供了Kubernetes格式資料的讀寫接口。

<code>consul</code>:服務發現。

<code>registrator</code>:監聽Docker服務程序,自動将容器注冊到<code>consul</code>。

<code>pilot</code>:從<code>consul</code>和<code>istio-apiserver</code>收集主機資訊與配置資料,并下發到所有的sidecar。

<code>zipkin</code>:鍊路跟蹤元件。與其他元件的關系相對獨立。

這些元件間的關系如下圖:

Istio入門實戰與架構原理——使用Docker Compose搭建Service Mesh

控制平面主要實作了以下兩個功能:

借用Kubernetes API管理配置資料。<code>etcd</code>和<code>kube-apiserver</code>的組合可以看作是一個對象存儲系統,它提供了讀寫接口和變更事件,并且可以直接使用<code>kubectl</code>作為用戶端友善地進行操作。Istio直接使用這個組合作為控制平面的持久化層,節省了重複開發的麻煩,另外也相容了Kubernetes容器架構。

使用Pilot-discovery将主機資訊與配置資料同步到Envoy。<code>pilot</code>容器中實際執行的是<code>pilot-discovery</code>(發現服務)。它從<code>consul</code>收集各個主機的域名和IP的對應關系,從<code>istio-apiserver</code>擷取流量控制配置,然後按照Envoy的xDS API規範生成Envoy配置,下發到所有sidecar。

接下來我們開始部署微服務。這裡我們使用Istio提供的例子,一個Bookinfo應用。

Bookinfo 應用分為四個單獨的微服務:

<code>productpage</code>:<code>productpage</code>微服務會調用<code>details</code>和<code>reviews</code>兩個微服務,用來生成頁面。

<code>details</code>:這個微服務包含了書籍的資訊。

<code>reviews</code>:這個微服務包含了書籍相關的評論。它還會調用<code>ratings</code>微服務。

<code>ratings</code>:<code>ratings</code>微服務中包含了由書籍評價組成的評級資訊。

<code>reviews</code>微服務有3個版本:

v1版本不會調用<code>ratings</code>服務。

v2版本會調用<code>ratings</code>服務,并使用1到5個黑色星形圖示來顯示評分資訊。

v3版本會調用<code>ratings</code>服務,并使用1到5個紅色星形圖示來顯示評分資訊。

Bookinfo應用的架構如下圖所示:

Istio入門實戰與架構原理——使用Docker Compose搭建Service Mesh
圖檔來自:Bookinfo應用

首先,我們切換到這個示例的目錄<code>samples/bookinfo/platform/consul</code>下。

使用<code>bookinfo.yaml</code>檔案啟動所有微服務:

這裡隻啟動了微服務,還需使用<code>bookinfo.sidecar.yaml</code>檔案啟動所有sidecar:

部署完畢。但是當我們通路時……

Bookinfo暴露到外面的端口是9081,使用位址<code>localhost:9081/productpage</code>通路<code>productpage</code>頁面。

Emmm……出錯了:

Istio入門實戰與架構原理——使用Docker Compose搭建Service Mesh

本來應該顯示<code>reviews</code>的部分報錯了,而<code>details</code>還是正常的。經過一番排查,我們發現,在所有微服務的容器上,不管你通路的是<code>productpage</code>、<code>details</code>、<code>reviews</code>還是<code>ratings</code>,網絡請求都會跑到<code>details</code>。

你的情況不一定是<code>details</code>,也有可能所有流量都跑到另外的某個服務。這是随機的。

不用懷疑部署的時候哪裡操作失誤了,就是官方的部署檔案有坑……

要解決這個問題,我們來看看sidecar的原理。

首先看看兩個部署用的yaml檔案都做了什麼。由于每個微服務的部署都大同小異,這裡隻貼出<code>productpage</code>相關的内容。

<code>bookinfo.yaml</code>:

<code>dns_search: - search.consul</code>。Docker Compose部署的這套樣例對短服務主機名的解析可能會有問題,是以這裡需要加個字尾。

<code>environment</code>環境變量的幾個設定。<code>registrator</code>會以這些環境變量為配置将服務注冊到<code>consul</code>。<code>SERVICE_NAME</code>是注冊的服務名,<code>SERVICE_TAGS</code>是注冊服務的<code>ServiceTags</code>,而<code>SERVICE_PROTOCOL=http</code>則會将<code>protocol: http</code>加入到<code>ServiceMeta</code>。

<code>bookinfo.sidecar.yaml</code>:

sidecar由兩部分組成,一個是負責初始化的<code>proxy_init</code>,這個容器執行完就退出了;另一個是實際的sidecar程式<code>proxy_debug</code>。

注意這兩個容器的<code>network_mode</code>,值為<code>container:consul_productpage-v1_1</code>。這是Docker的容器網絡模式,意思是這兩個容器和<code>productpage-v1</code>共用同一個虛拟網卡,即它們在相同網絡棧上。

sidecar的網絡代理一般是将一個端口轉發到另一個端口。是以微服務使用的端口就必須和對外暴露的端口不一樣,這樣一來sidecar就不夠透明。

為了使sidecar變得透明,以Istio使用<code>proxy_init</code>設定了iptables的轉發規則(<code>proxy_init</code>、<code>proxy_debug</code>和<code>productpage-v1</code>在相同的網絡棧上,是以這個配置對這三個容器都生效)。添加的規則為:

回環網絡的流量不處理。

使用者ID為1337的流量不處理。1337是Envoy程序的使用者ID,這條規則是為了防止流量轉發死循環。

所有出站入站的流量除了規則1和規則2外,都轉發到15001端口——這是Envoy監聽的端口。

比如<code>productpage</code>服務使用的9080端口,當其他服務通過9080端口通路<code>productpage</code>是,請求會先被iptables轉發到15001端口,Envoy再根據路由規則轉發到9080端口。這樣通路9080的流量實際上都在15001繞了一圈,但是對外部來說,這個過程是透明的。

Istio入門實戰與架構原理——使用Docker Compose搭建Service Mesh

<code>proxy_debug</code>有兩個程序:<code>pilot-agent</code>和<code>envoy</code>。<code>proxy_debug</code>啟動時,會先啟動<code>pilot-agent</code>。<code>pilot-agent</code>做的事很簡單,它生成了<code>envoy</code>的初始配置檔案<code>/var/lib/istio/envoy-rev0.json</code>,然後啟動<code>envoy</code>。後面的事就都交給<code>envoy</code>了。

使用下面指令導出初始配置檔案:

使用你心愛的編輯器打開初始配置檔案,可以看到有這麼一段:

這一段的意思是<code>envoy</code>會連接配接到<code>pilot</code>(控制平面的元件,忘記了請往上翻翻)的15010端口。這倆将按照xDS的API規範,使用GRPC協定實時同步配置資料。

xDS是Envoy約定的一系列發現服務(Discovery Service)的統稱。如CDS(Cluster Discovery Service),EDS(Endpoint Discovery Service),RDS(Route Discovery Service)等。Envoy動态配置需要從實作了xDS規範的接口(比如這裡的<code>pilot-discovery</code>)擷取配置資料。

總結一下,Envoy配置初始化流程為:

Istio入門實戰與架構原理——使用Docker Compose搭建Service Mesh
圖檔來自:Istio流量管理實作機制深度解析

那麼說<code>envoy</code>實際使用的路由配置并不在初始配置檔案中,而是<code>pilot</code>生成并推送過來的。如何檢視<code>envoy</code>的目前配置呢?還好<code>envoy</code>暴露了一個管理端口15000:

我們可以通過<code>/config_dump</code>接口導出<code>envoy</code>的目前配置:

打開這個配置,看到這麼一段:

猜一下也能知道,這一段的意思是,通路目标位址9080端口的出站流量,都會被路由到<code>details</code>。太坑了!!!

從上面原理分析可知,這個問題的根源應該在于<code>pilot</code>給Envoy生成的配置不正确。

檢視<code>pilot</code>源碼得知,<code>pilot</code>在生成配置時,用一個<code>map</code>儲存Listener資訊。這個map的key為<code>&lt;ip&gt;:&lt;port&gt;</code>。如果服務注冊的時候,沒有指明端口<code>&lt;port&gt;</code>上的協定的話,預設認為TCP協定。<code>pilot</code>會将這個Listener和路由寫入到這個<code>map</code>,并拒絕其他相同位址端口再來監聽。于是隻有第一個注冊的服務的路由會生效,所有流量都會走到那個服務。如果這個端口有指定使用HTTP協定的話,Pilot-discovery這裡生成的是一個RDS的監聽,這個RDS則根據域名路由到正确的位址。

簡單說就是所有微服務在注冊到<code>consul</code>時應該在<code>ServiceMeta</code>中說明自己9080端口的協定是<code>http</code>。

等等,前面的<code>bookinfo.yaml</code>配置裡,有指定9080端口的協定是了呀。我們通路一下<code>consul</code>的接口看下<code>ServiceMeta</code>是寫入了沒有:

Istio入門實戰與架構原理——使用Docker Compose搭建Service Mesh

果然沒有……看來Registrator注冊的時候出了岔子。網上搜了下,确實有Issue提到了這個問題:gliderlabs/registrator#633。<code>istio.yaml</code>中使用的<code>latest</code>版本的Registrator不支援寫入Consul的ServiceMeta。應該改為<code>master</code>版本。

修改一下<code>istio.yaml</code>配置。按照部署倒叙關閉sidecar、微服務,重新啟動控制平面,等<code>registrator</code>啟動完畢後,重新部署微服務和sidecar。

再通路<code>consul</code>的接口試試,有了(沒有的話可能是<code>registrator</code>沒啟動好導緻沒注冊到<code>consul</code>,再新部署下微服務和sidecar):

Istio入門實戰與架構原理——使用Docker Compose搭建Service Mesh

再通路頁面,OK了。目前沒有配置路由規則,<code>reviews</code>的版本是随機的。多重新整理幾次頁面,可以看到打星在“沒有星星”、“黑色星星”和“紅色星星”三種效果間随機切換。

使用位址<code>http://localhost:9411</code>能通路Zipkin鍊路跟蹤系統,檢視微服務請求鍊路調用情況。

我們來看看正确的配置是什麼内容。再取出Envoy的配置,<code>0.0.0.0_9080</code>的Listener内容變為:

9080端口的出站路由規則由一個名稱為<code>"9080"</code>的<code>route_config</code>定義。找一下這個<code>route_config</code>:

由于内容太長,這裡隻貼<code>details</code>和<code>productpage</code>的關鍵内容。可以看到,9080端口的出站流量會根據目标位址的域名正确地轉發到對應的微服務。

注意:本節工作目錄為<code>/samples/bookinfo/platform/consul</code>。

最後我們嘗試一下Istio的路由控制能力。在配置路由規則之前,我們要先使用DestinationRule定義各個微服務的版本:

DestinationRule:DestinationRule定義了每個服務下按照某種政策分割的子集。在本例子中按照版本來分子集,<code>reviews</code>分為v1、v2、v3三個版本的子集,其他微服務都隻有v1一個子集。

使用指令<code>kubectl get destinationrules -o yaml</code>可以檢視已配置的DestinationRule。

接下來我們使用VirtualService來配置路由規則。<code>virtual-service-all-v1.yaml</code>配置會讓所有微服務的流量都路由到v1版本。

VirtualService:定義路由規則,按照這個規則決定每次請求服務應該将流量轉發到哪個子集。

使用指令<code>kubectl get virtualservices -o yaml</code>可以檢視已配置的VirtualService。

再重新整理頁面,現在不管重新整理多少次,<code>reviews</code>都會使用v1版本,也就是頁面不會顯示星星。

下面我們試一下基于使用者身份的路由規則。配置檔案<code>virtual-service-reviews-test-v2.yaml</code>配置了<code>reviews</code>的路由,讓使用者<code>jason</code>的流量路由到v2版本,其他情況路由到v1版本。

執行指令後重新整理頁面,可以看到<code>reviews</code>都使用的v1版本,頁面不會顯示星星。點選右上角的<code>Sign in</code>按鈕,以jason的身份登入(密碼随便),可以看到<code>reviews</code>切換到v2版本了,頁面顯示了黑色星星。

檢視<code>virtual-service-reviews-test-v2.yaml</code>檔案内容可以看到,基于身份的路由是按照比對HTTP的headers實作的。當HTTP的headers有<code>end-user: jason</code>的内容時路由到v2版本,否則路由到v1版本。

<code>istio.yaml</code>引用的Registrator的<code>latest</code>版本不支援consul的ServiceMeta。要改為<code>master</code>版本。

第一次啟動<code>istio.yaml</code>後,因為啟動時<code>pilot</code>連不上<code>istio-apiserver</code>,<code>pilot</code>會失敗退出。等待<code>istio-apiserver</code>啟動完畢後再跑一次<code>istio.yaml</code>。

配置<code>kubectl</code>的<code>context</code>,讓<code>kubectl</code>使用<code>istio-apiserver</code>提供的Kubernetes API接口。

使用<code>bookinfo.yaml</code>啟動各個微服務後,還要運作<code>bookinfo.sidecar.yaml</code>以初始化和啟動sidecar。

Istio入門實戰與架構原理——使用Docker Compose搭建Service Mesh

Istio文檔

Pattern: Service Mesh

Envoy 的架構與基本術語

Understanding How Envoy Sidecar Intercept and Route Traffic in Istio Service Mesh

Istio Pilot與Envoy的互動機制解讀

Istio流量管理實作機制深度解析

繼續閱讀