教程|使用Istio實現一個Service Mesh以簡化微服務間的通訊模式

ServiceMesher發表於2018-09-05

本文為譯文,英文原文地址:kublr.com/blog/implem…

本文轉載自:www.servicemesher.com/blog/hands-…

更多 Service Mesh 文章請訪問:www.servicemesher.com

本文為該教程的第1部分。

如果你之前沒有聽說過Service Mesh,不用擔心。雖然從可用文件、公開討論和Github活躍度來看,它是一個相對較新的技術,與基於容器和微服務架構相似還沒有被廣泛採用,但是它將會對軟體架構帶來深遠影響。本文將幫助您瞭解Service Mesh的基礎知識和教程,以及如何實現它並從基礎架構獲益。

Service Mesh的兩個主要目標是允許洞察先前不可見的服務通訊層,並獲取對所有微服務間像動態服務發現、負債均衡、超時、回退、重試、斷路器、分散式呼叫鏈路追蹤和安全策略的執行等通訊邏輯的完全控制。更多細節請檢視Istio流量審計和分散式鏈路追蹤相關資料。

Kubernetes已經擁有開箱即用的“Service Mesh”。它的“service”資源,提供了針對指定需要的pod的服務發現功能和請求的負載均衡。通過在叢集的每個主機上配置管理iptables規則使一個“service”生效,只允許輪詢式負載均衡途徑,沒有重試或回退邏輯,除此之外沒有其他我們可能想要的用一個現代的Service Mesh解決的功能。然而,若在叢集中實現一個功能齊全的Service Mesh系統(Linkerd、Istio或Conduit),將為您提供以下可能性:

  • 允許在應用層上服務間通過簡單的http協議通訊而不用https:Service Mesh代理將在傳送端管理HTTPS封裝並在接收端實現TLS終止,允許應用程式元件僅需要使用簡單的http、gRPC或其他協議而不用去操心在傳輸途中的加密實現,Service Mesh代理將為實現加密功能。
  • 執行安全策略:代理知道那些service可以訪問另外一些service和endpoint並拒絕未授權的流量。
  • 斷路器:訪問具有高延遲的過載service或者endpoint回退,防止更多的請求落在該service或endpoint上導致請求無響應。
  • 延遲感知負載平衡:而代替使用輪詢式負載均衡(忽略每個目標延遲),使用根據每個後端目標的響應時間更智慧的負載均衡,這應該是現代服務網格的一個非常重要的特徵。
  • 負債均衡佇列深度:根據最少訪問量路由當前請求。Service Mesh精確知道所有已經傳送請求,以及它們是正在處理還是已經完成。它會根據該邏輯將新的傳入請求傳送到具有最小佇列的節點進行處理。
  • 請求路由:把根據具有特定http頭標記的請求路由到負債均衡後面的特定節點。允許簡單的金絲雀部署測試和其他創造性用例。這是Service Mesh提供的最強大功能之一。
  • 健康檢查,重試預算和驅逐行為異常的節點
  • 度量標準和跟蹤:報告每個target的請求量、延遲指標、成功率和錯誤率。

下面是兩種部署Service Mesh的方式:

作為主機共享代理,Kubernetes術語中的DaemonSet。如果同一主機上存在許多容器,並且還可能利用連線池來提高吞吐量,則此類部署將使用較少的資源。但是,如果一個代理中的故障將搞垮該主機上的整個容器佇列,而不是破壞單個服務(如果它被用作sidecar代理)。

作為容器sidecar,將代理注入到每個pod定義中與主服務一起執行。如果使用像Linkerd這樣更加“重量級”的代理,這個部署將為每個pod增加約200MB的記憶體。但如果使用較新的Conduit,每個pod只需10MB左右。Conduit還沒有Linkerd的所有功能,所以我們還沒有看到兩者的最終比較。通常,“每個pod中一個sidecar”是一個不錯的選擇,這樣儘可能的將代理故障限制在單個pod中,不要影響同一主機上的其他pod。

為什麼需要建立Service Mesh架構?讓我們看一下不同型別的應用程式架構的兩個圖表來說明需求。

第一個示例是一個老式基於MVC架構的Web服務,是作為單體架構all-in-one應用程式。可能每天服務數百萬個請求,但沒有複雜的功能,並且底層服務的通訊簡單明瞭:Nginx均衡Apache例項的所有流量,Apache又從資料庫/檔案儲存中獲取資料並返回請求頁面。這個示例所採用的架構不會從服務網格中獲取太多收益。由於單體應用沒有采用服務呼叫的方式,所以所有功能是耦合在一塊的,開發者沒有開發處理服務間路由和通訊的程式碼。在單體應用,所有核心元件都位於同一臺機器上,不通過網路進行通訊,沒有REST API或gRPC。所有“業務邏輯”都在一個應用程式中,在每個Apache Web伺服器上作為整體部署。

第二個例子是一個基於現代微服務架構的應用程式,它有很多程式和幕後邏輯。它做了很多事情,比如學習訪問者模式和偏好來個性化他們在網站上的體驗,通知使用者他們最喜歡的topic更新,等等。您可以想象在所有這些微服務之間發生的許多複雜過程,分佈在數千個容器和數百個節點上。請注意,我們的插圖非常簡化。實際上,我們顯示大型雲原生應用程式的真實架構中簡化了很多細節。

在這個例項程式中我們的每個微服務都有一些程式碼用於處理彼此間的通訊,設定重試策略、超時、異常處理等等(在網路故障的情況下)。我們還看到這是一個多語言環境,其中不同團隊使用Scala、Golang、Node.js或Python開發自己的服務元件。所有元件都可以通過REST API或gRPC相互通訊,每個團隊都花費時間和精力在他們自己的元件中實現通訊邏輯,使用他們各自的語言選擇,因此他們不能共享彼此的庫和函式,至少可以節省時間並使用插入應用程式的所有元件的統一解決方案作為依賴。此外,查詢服務發現機制的函式(如Consul或ZooKeeper)或讀取外部傳遞給應用程式的一些配置,需要向Prometheus/InfluxDB報告延遲和響應相關指標。這包括有關快取響應時間(redis或memcached快取)的資訊,該快取響應時間通常位於另一個節點上,或者作為整個單獨的群集,可能會過載並導致高延遲。除了團隊爆炸日誌和截止日期臨近之外,所有這些都是服務程式碼的一部分,需要維護。開發人員不願花時間在程式碼的運維相關部分任務上,例如新增分散式追蹤和監控指標(不喜歡排除故障和分析)或處理可能的網路故障,實施回退和重試預算。

在這種環境中,Service Mesh將節省開發時間,並允許以統一的方式以集中式地控制通訊。那我們如何將這種通訊層機制改為統一的“Service Mesh”?我們把微服務間通訊、路由、服務發現、延遲指標、請求追蹤、和微服務中的一些相似程式碼完全抽取到服務外邊,搞一個能夠處理這些甚至更多功能的單例程式為每個微服務去處理這些公共邏輯。幸運的是這些工具已經存在,像Twitter、Lyft、Netflix這樣的公司已經開源了自己的工具,其他貢獻者也可以基於這些庫開發自己的工具。目前為止我們有Linkerd、Conduit、Istio和Envoy供選擇。Istio基於Envoy構建的,它是一個控制平面,Envoy和Linkerd都可以用作它的資料平面代理。控制平面允許叢集運維人員以集中式地設定特定設定,然後將其分佈在資料平面代理上並重新配置它們。

Linkerd和Conduct由Buoyant開發,開發者是一些曾經在Twitter工作的工程師。目前Linkerd是最常用的Service Mesh之一,而Conduit是從頭開始專門為Kubernetes構建的輕量級sidecar,非常快速且非常適合Kubernetes環境。在撰寫本文時,Conduit仍處於積極發展階段。

讓我們看一下從依賴於應用程式的通訊邏輯到“Service Mesh”架構的變化。

最值得注意的是,所有代理都可以在同一個地方配置和更新,通過他們的控制平面(或通過某些儲存庫中的配置檔案, 取決於所選的工具和部署方法),我們可以在數千個代理配置特定規則。因此,路由、負載均衡、度量指標收集、安全策略實施、斷路器、資料傳輸加密,所有這些操作都將嚴格遵循由叢集管理員應用的一系列規則。

Service Mesh適合您嗎?

乍一看,這種將微服務通訊機制分離到單獨的架構層的新概念引入了一個問題:是否值得配置和維護一整套複雜的特殊代理?要回答這個問題,您需要估算應用程式規模和複雜程度。如果您只有幾個微服務和資料儲存端點(例如,一個用於記錄的ElasticSearch叢集,一個用於度量的Prometheus叢集,具有兩個或三個主應用程式資料的資料庫),那麼實現服務網格可能對您的環境來說沒有太大必要。但是,如果您的應用程式元件分佈在數百或數千個節點上,並且具有20+微服務,採用Service Mesh你將受益匪淺。

即使在較小的環境中,如果您希望將重試和斷路行為與應用程式本身分離(例如,從管理連線和退出的程式碼,以避免重試導致其他服務或資料庫過載),您可以使用服務網格 從您的應用程式開發人員中刪除此網路邏輯維護負擔,你可以使用服務網格降低應用程式開發人員維護網路邏輯的負擔。因此,他們將更多地關注業務邏輯,而不是參與管理和調整所有微服務的相互通訊。

運維團隊一旦配置服務網路,就可以集中調整,最大限度地減少在應用程式元件通訊上花費的精力。

Istio是一個集中所有Service Mesh特性的完美例子,它有幾個“主元件”來管理所有“資料平面”代理(這些代理可以是Envoy或Linkerd,但預設情況下,它是Envoy,這是我們在教程中使用的內容,而Linkerd整合仍在進行中)。

以下是官方網站上Istio架構的圖表:

譯者注:圖中的istio-auth現已改名為citadel

您可以在官方文件中閱讀更多內容,但是出於本教程的目的,以下是Istio元件及其功能的摘要:

控制平面

  • Pilot:向Envoy代理提供路由規則和服務發現資訊。
  • Mixer:從每個Envoy代理收集遙測並執行訪問控制策略。
  • Citadel:提供“服務間”和“使用者到服務”認證,並且可以將未加密的流量基於TLS加密。很快就能提供訪問稽核資訊(正在進行的工作)。

資料平面

  • Envoy:功能豐富的代理,由控制平面元件管理。攔截進出服務的流量,並按照控制平面中設定的規則應用所需的路由和訪問策略。

教程

在下面的教程中,我們將使用Istio來演示一個最強大的功能:“按請求路由”。如前面說的那樣,它允許將選定HTTP頭標記的特定請求路由到僅可通過第7層代理實現的特定目標。沒有第4層負載均衡器或代理可以實現該功能。

對於本教程,我們假設您正在執行Kubernetes叢集(提示:您可以在幾分鐘內遵循這些說明或啟動新叢集,或者使用“Kublr-in-a-box”通過幾個簡單的步驟設定本地群集)。對於本教程,有1個主節點和2個工作節點的小型叢集應該足夠了。

教程第1階段:安裝Istio控制平面

按官方教程安裝在Kubernetes叢集中安裝控制平面。這個安裝步驟依賴你的本地環境(windows、Linux還是MAC),所以我們不能複製使用本地標準指令設定應用程式,我們使用istioct和kubectl兩個CLI工具管理庫爾netes和istio。請安裝下面簡明扼要的描述去做(如果不起作用,請逐步使用官方說明):

  1. 設定kubernetes叢集(使用上面列出的方法,或使用您現有的測試/開發群集)

  2. 下載kubectl並配置到環境環境(用它管理你的kubernetes環境)

  3. 下載istioctl並配置到環境變數(使用它把Envoy代理注入到每個pod中設定路由和策略)下面是簡單安裝說明:

(1)在MAC或Linux命令列上實行

curl -L https://git.io/getLatestIstio | sh -
複製程式碼

(2)在windows上下載istio.zip並解壓檔案,將檔案路徑配置到你的環境變數中

(3)切換到解壓環境上面檔案解壓路徑中,並執行

kubectl apply -f install/kubernetes/istio-demo.yaml
複製程式碼

另一種安裝方式是使用Kublr安裝你的kubernetes叢集環境——一個簡單的方法是通過雲提供商(阿里雲騰訊雲awsazuregcp或者quick start)上拉起一個kubernetes叢集。 kublr

找到%USERPROFILE%/.kube/config檔案拷貝到你的宿主機目錄下(~/.kube/config),調到如下頁面:

使用配置檔案中的管理員賬號和密碼登陸到kubernetes dashboard,你應該能夠看到這個儀表盤,點選側邊欄顯示的default這個 namespace:

Istio元件將安裝到它們自己的namespace中。調到istio下載目錄,並執行命令:

kubectl apply -f install/kubernetes/istio-demo.yaml
複製程式碼

你將看到一些列的元件被建立,詳情請看官方文件或者你也可以開啟yaml檔案檢視相應元件,每個資源都記錄在該檔案中。然後我們可以瀏覽namespace並檢視所有已成功建立的內容:

在元件建立期間點選istio-system檢視是否有錯誤或者issue,看起來應該和下面類似:

從圖中可以看到有50個事件,你能滾動螢幕去看“成功”狀態,並注意有些地方可能存在錯誤。如果有錯誤,你可以去github上提交issue。

我們需要找到istio-ingress服務的入口,去了解那裡傳送流量。回到kubernetes dashboard的側邊欄並跳轉到istio-system這個namespace下。如果建立後在這個namespace下不可見,重新整理瀏覽器試試。點選“Services”找到external endpoint,如下圖所示:

在我們的例子中,這是AWS彈性負載均衡器,但你可能會看到IP地址,具體取決於叢集設定。我們將使用此端點地址訪問我們的演示Web服務。

教程第2階段:使用Envoy Sidecar部署演示Web服務

這是本教程中最好玩的部分。我們來檢查一下這個Service Mesh的路由功能。首先我們將像前面一樣通過藍綠髮布我們的demo例項服務。將以下內容複製到名為的my-websites.yaml檔案中。

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: web-v1
  namespace: default
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: website
        version: website-version-1
    spec:
      containers:
      - name: website-version-1
        image: aquamarine/kublr-tutorial-images:v1
        resources:
          requests:
            cpu: 0.1
            memory: 200
---
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: web-v2
  namespace: default
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: website
        version: website-version-2
    spec:
      containers:
      - name: website-version-2
        image: aquamarine/kublr-tutorial-images:v2
        resources:
          requests:
            cpu: 0.1
            memory: 200
---
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: web-v3
  namespace: default
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: website
        version: website-version-3
    spec:
      containers:
      - name: website-version-3
        image: aquamarine/kublr-tutorial-images:v3
        resources:
          requests:
            cpu: 0.1
            memory: 200
---
apiVersion: v1
kind: Service
metadata:
  name: website
spec:
  ports:
  - port: 80
    targetPort: 80
    protocol: TCP
    name: http
  selector:
    app: website
複製程式碼

在你的pod和Envoy代理一起使用時請注意,“app”這個label的存在(它用於請求跟蹤功能),在服務中“spec.ports.name”的值要拼寫正確(http、http2、grpc、redis、mongo),Enovy將像對待普通TCP一樣代理這些服務,你不能對這些服務使用L7路由功能。pod在叢集中只提供同一服務。從檔案可以看到這個服務有三個版本(v1/v2/v3)。服務的每個版本都有對應的Deployment。

現在我們新增針對此pod的Envoy代理配置到這個檔案中。使用“istioctl kube-inject”命令,它將生成一個可供kubectl部署使用包含Envoy代理額外元件的新yaml檔案,執行命令:

 istioctl kube-inject -f my-websites.yaml -o my-websites-with-proxy.yaml
複製程式碼

輸出檔案將包含額外配置,你能檢視my-websites-with-proxy.yaml檔案。此命令採用預定義的ConfigMap “istio-sidecar-injector”(它在定義istio之前已經定義)。併為我們的deployment定義新增了所需的sidecar配置和引數。當我們部署新檔案“my-websites-with-proxy.yaml”時,每個pod將有兩個容器,一個我們的例項程式,一個Envoy代理。執行下面命令部署我們的服務程式和sidecar:

kubectl create -f my-websites-with-proxy.yaml
複製程式碼

如果它按預期工作,您將看到此輸出:

deployment "web-v1" created
deployment "web-v2" created
deployment "web-v3" created
service "website" created
Let’s inspect the pods to see that the Envoy sidecar is present:  kubectl get pods
複製程式碼

我們可以看到每個pod有兩個容器,一個是網站容器,另一個是代理sidecar:

我們能夠通過執行如下命令檢視Envoy執行日誌:

kubectl logs <your pod name> istio-proxy
複製程式碼

您將看到很多輸出,最後幾行與此類似:

add/update cluster outbound|80|version-1|website.default.svc.cluster.local starting warming
add/update cluster outbound|80|version-2|website.default.svc.cluster.local starting warming
add/update cluster outbound|80|version-3|website.default.svc.cluster.local starting warming
warming cluster outbound|80|version-3|website.default.svc.cluster.local complete
warming cluster outbound|80|version-2|website.default.svc.cluster.local complete
warming cluster outbound|80|version-1|website.default.svc.cluster.local complete
複製程式碼

這意味著sidecar在pod中執行良好。

現在我們需要部署最小的Istio配置資源,需要將路由流量到我們的service和pod。請把下面的檔案儲存到website-routing.yaml檔案。

---
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: website-gateway
spec:
  selector:
    # Which pods we want to expose as Istio router
    # This label points to the default one installed from file istio-demo.yaml
    istio: ingressgateway
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    # Here we specify which Kubernetes service names
    # we want to serve through this Gateway
    hosts:
    - "*"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: website-virtual-service
spec:
  hosts:
  - "*"
  gateways:
  - website-gateway
  http:
  - route:
    - destination:
        host: website
        subset: version-1
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: website
spec:
  host: website
  subsets:
  - name: version-1
    labels:
      version: website-version-1
  - name: version-2
    labels:
      version: website-version-2
  - name: version-3
    labels:
      version: website-version-3
複製程式碼

該檔案定義了Gateway、VirtualService和DestinationRule。這些是自定義Istio資源,用於管理和配置istio-ingressgateway pod的ingress行為。我們將在下一個教程中更深入地描述它們,這些教程將闡述Istio配置的技術細節。現在,部署這些資源以便能夠訪問我們的示例網站:

kubectl create -f website-routing.yaml
複製程式碼

下一步是訪問我們的演示網站。我們部署了三個版本,每個都顯示不同的頁面文字和顏色,但目前我們只能通過Istio ingress訪問v1。讓我們訪問我們的服務確保Web服務被部署了。

通過執行如下命令檢視外部端點:

kubectl get services istio-ingressgateway -n istio-system
複製程式碼

或者通過瀏覽istio-ingressgateway服務找到它,如下所示(我們也在本教程的開頭看到過它)

img

通過點選它訪問外部節點。您可能會看到多個連結,因為一個連結指向HTTPS,另一個連結指向負載均衡器的HTTP埠。如果是這樣,請僅使用HTTP連結,因為我們沒有為本教程設定TLS,您應該看到演示網站的v1頁面:

為我們demo示例明確配置kubernetes service指向單一部署istio VirtualService。它指明Envoy將訪問網站的流量全部路由到v1版本(如果沒有Envoy路由策略,kubernetes將會在三本版本的pods輪詢請求)。您可以通過更改VirtualService配置的以下部分並重新部署它來更改我們看到的網站版本:

  http:
  - route:
    - destination:
        host: website
        subset: version-1
複製程式碼

“subset”是我們選擇要路由到的DestinationRule的正確地方。我們將在下一個教程中深入學習這些資源。

通常,當需要使用少量流量測試新版本的應用程式時(金絲雀部署)。vanilla Kubernetes方法使用新的Docker映象,相同的pod標籤,建立第二個deployment,將流量路由到有這個label標記的服務上。這不像Istio解決方案那樣靈活。您無法輕鬆將10%的流量指向新deployment(為了達到精確的10%,您需要根據所需的百分比保持兩個deployment之間的pod複製比例,例如9個“v1 pod”和1個“v2 pod”,或18個“v1 pod”和2個“v2 pod ”),並且不能使用HTTP頭標記來將請求路由到特定版本。

在我們的下一篇文章中,與Istio一起實踐的金絲雀部署,我們將自定義http頭路由請求到正確的服務版本。通過這樣做,我們將完全控制流量,並將分析Zipkin儀表板中的分散式追蹤結果。

Istio的部署十分依賴Kubernetes,為了大家更好的理解 Kubernetes 原理,我推薦學習深入剖析Kubernetes by 張磊,極客時間出品

ServiceMesher社群資訊

微信群:聯絡我入群

社群官網:www.servicemesher.com

Slack:servicemesher.slack.com 需要邀請才能加入

Twitter: twitter.com/servicemesh…

GitHub:github.com/servicemesh…

更多Service Mesh諮詢請掃碼關注微信公眾號ServiceMesher。

教程|使用Istio實現一個Service Mesh以簡化微服務間的通訊模式



相關文章