在雲端環境中,我們通常在叢集內執行各種服務和應用程式。Istio 能夠解決服務間通訊的許多挑戰,特別是在叢集內部或跨叢集的服務間通訊方面表現出色。

然而,在服務開始相互通訊之前,必須有某種觸發器來啟動這些互動。例如,終端使用者購買商品、客戶端查詢 API 等。這些觸發器的共同點是它們來自叢集外部。這就引出了一個問題:我們如何將外部流量引入叢集內部?

本章將回答這個問題,探討如何為叢集外的客戶端建立安全連線到叢集內服務的入口點。

流量入口的概念

虛擬 IP:簡化服務存取

網路社群使用「入口點」(ingress points)這個術語來描述透過建立良好的入口點連線網路的方式。入口流量是指源自網路外部但目的地在網路內部的流量。

讓我們深入瞭解流量如何路由到網路的入口點。假設我們有一個服務,希望在 api.istioinaction.io/v1/products 上暴露給外部系統,以取得產品目錄列表。當客戶端嘗試查詢該端點時,客戶端的網路堆積積疊首先嘗試將網域名稱解析為 IP 地址,這是透過 DNS 伺服器完成的。

將網域名稱直接對映到服務的單一例項(單一 IP)是非常脆弱的。如果該特定服務例項發生故障,客戶端將看到許多錯誤,直到我們將 DNS 對映更改為具有正常工作端點的新 IP 地址。但這種方法緩慢、容易出錯與可用性低。

將網域名稱對映到代表我們服務的虛擬 IP 地址,並將流量轉發到實際的服務例項,這提供了更高的可用性和靈活性。虛擬 IP 繫結到一種稱為反向代理的入口點。反向代理負責將請求分發到後端服務,並提供負載平衡等功能。

虛擬主機:從單一存取點提供多個服務

例如,我們可以讓 prod.istioinaction.io 和 api.istioinaction.io 都解析到同一個虛擬 IP 地址。這意味著對這兩個主機名的請求都會到達同一個虛擬 IP,因此同一個入口反向代理會路由請求。如果反向代理足夠人工智慧,它可以使用 HTTP Host 標頭進一步區分哪些請求應該去往往哪組服務。

在單個入口點託管多個不同服務被稱為虛擬主機託管。我們需要一種方法來決定特定請求應該路由到哪個虛擬主機組。對於 HTTP/1.1,我們可以使用 Host 標頭;對於 HTTP/2,我們可以使用 :authority 標頭;對於 TCP 連線,我們可以依賴 TLS 的伺服器名稱指示(SNI)。

Istio 入口閘道器

Istio 有入口閘道器的概念,它扮演網路入口點的角色,負責守衞和控制從叢集外部發起的流量進入叢集。此外,Istio 的入口閘道器還處理負載平衡和虛擬主機路由。

Istio 使用單個 Envoy 代理作為入口閘道器。Envoy 不僅是一個能夠服務間代理,還可以用於負載平衡和將流量從服務網格外部路由到服務網格內部執行的服務。

設定 Gateway 資源

要在 Istio 中設定入口閘道器,我們使用 Gateway 資源並指定要開放的連線埠和這些連線埠接受的虛擬主機。以下是一個簡單的 Gateway 資源範例:

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: coolstore-gateway
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "webapp.istioinaction.io"

這個 Gateway 資源設定 Envoy 在連線埠 80 上監聽並期望 HTTP 流量。

使用虛擬服務進行閘道器路由

當流量進入閘道器時,我們需要一種方法將其路由到服務網格內的特定服務;為此,我們使用 VirtualService 資源。在 Istio 中,VirtualService 資源定義了客戶端如何透過其完全限定網域名稱與特定服務通訊、服務的哪些版本可用以及其他路由屬性(如重試和請求超時)。

以下是一個 VirtualService 範例,它將虛擬主機 webapp.istioinaction.io 的流量路由到佈署在服務網格中的服務:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: webapp-vs-from-gw
spec:
  hosts:
  - "webapp.istioinaction.io"
  gateways:
  - coolstore-gateway
  http:
  - route:
    - destination:
        host: webapp
        port:
          number: 8080

流量流程概述

Gateway 資源定義了我們希望在服務網格叢集邊緣監聽的連線埠、協定和虛擬主機。VirtualService 資源定義了一旦流量被允許進入邊緣後應該去向何處。

Istio 入口閘道器與 Kubernetes Ingress 的比較

當在 Kubernetes 上執行時,您可能會問:“為什麼 Istio 不直接使用 Kubernetes Ingress v1 資源來指定入口?” Istio 確實支援 Kubernetes Ingress v1 資源,但 Kubernetes Ingress v1 規範有顯著的限制:

  1. Kubernetes Ingress v1 是一個非常簡單的規範,針對 HTTP 工作負載。Ingress 規範只考慮連線埠 80 和連線埠 443 作為入口點,這嚴重限制了叢集操作員可以允許進入服務網格的流量型別。

  2. Kubernetes Ingress v1 資源嚴重欠缺規範。沒有通用的方式來指定複雜的流量路由規則、流量分割或流量影子等功能。

  3. 由於規範不足,大多數供應商選擇透過佈署上的特定註解來暴露設定。不同供應商之間的註解各不相同與不可移植。

最終,Istio 決定為構建入口模式提供一個全新的起點,並特別將第 4 層(傳輸)和第 5 層(工作階段)屬性與第 7 層(應用程式)路由關注點分開。Istio Gateway 處理 L4 和 L5 關注點,而 VirtualService 處理 L7 關注點。

Istio 入口閘道器與 API 閘道器的比較

API 閘道器允許組織將消費一組服務的客戶端(在網路或架構邊界內)從這些服務的實作細節中抽象出來。例如,客戶端可能呼叫一組預期有良好檔案、以向後和向前相容語義演進的 API,並提供自助服務和其他使用機制。

Istio 的入口閘道器不提供這些開箱即用的功能。對於更強大的 API 閘道器(甚至是根據 Envoy 代理構建的),可以考慮像 Solo.io Gloo Edge 這樣的解決方案。

保護閘道器流量

使用 TLS 的 HTTP 流量

為了防止中間人(MITM)攻擊並加密進入服務網格的所有流量,我們可以在 Istio 閘道器上設定 TLS,以便任何傳入流量都透過 HTTPS 提供(對於 HTTP 流量)。

要為入口流量啟用 HTTPS,我們需要指定閘道器應使用的正確私鑰和證書。證書基本上是伺服器的公鑰,已由評價良好的機構(也稱為證書頒發機構,CA)簽名。

以下是設定 TLS 的 Gateway 資源範例:

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: coolstore-gateway
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "webapp.istioinaction.io"
  - port:
      number: 443
      name: https
      protocol: HTTPS
    tls:
      mode: SIMPLE
      credentialName: webapp-credential
    hosts:
    - "webapp.istioinaction.io"

HTTP 重定向到 HTTPS

如果我們希望強制所有流量始終使用 TLS,可以設定 Gateway 資源以強制將 HTTP 流量重定向到 HTTPS:

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: coolstore-gateway
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "webapp.istioinaction.io"
    tls:
      httpsRedirect: true
  - port:
      number: 443
      name: https
      protocol: HTTPS
    tls:
      mode: SIMPLE
      credentialName: webapp-credential
    hosts:
    - "webapp.istioinaction.io"

使用相互 TLS 的 HTTP 流量

在標準 TLS 中,伺服器向客戶端證明其身份。但如果我們希望叢集在接受來自叢集外部的流量之前驗證客戶端的身份呢?

使用相互 TLS(mTLS)協定,客戶端和伺服器都驗證對方的證書,換句話説,相互認證。證書用於加密流量。

以下是設定相互 TLS 的 Gateway 資源範例:

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: coolstore-gateway
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "webapp.istioinaction.io"
  - port:
      number: 443
      name: https
      protocol: HTTPS
    tls:
      mode: MUTUAL
      credentialName: webapp-credential-mtls
    hosts:
    - "webapp.istioinaction.io"

使用 TLS 提供多個虛擬主機

Istio 的入口閘道器可以從同一個 HTTPS 連線埠(連線埠 443)提供多個虛擬主機,每個主機都有自己的證書和私鑰對。例如:

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: coolstore-gateway
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 443
      name: https-webapp
      protocol: HTTPS
    tls:
      mode: SIMPLE
      credentialName: webapp-credential
    hosts:
    - "webapp.istioinaction.io"
  - port:
      number: 443
      name: https-catalog
      protocol: HTTPS
    tls:
      mode: SIMPLE
      credentialName: catalog-credential
    hosts:
    - "catalog.istioinaction.io"

TCP 流量

Istio 的閘道器強大到足以提供不僅是 HTTP/HTTPS 流量,還可以提供透過 TCP 存取的任何流量。例如,我們可以透過入口閘道器暴露資料函式庫(如 MongoDB)或訊息佇列(如 Kafka)。

在 Istio 閘道器上暴露 TCP 連線埠

以下是暴露 TCP 連線埠的 Gateway 資源範例:

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: echo-tcp-gateway
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 31400
      name: tcp-echo
      protocol: TCP
    hosts:
    - "*"

我們還需要使用 VirtualService 資源將流量從閘道器路由到服務:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: tcp-echo-vs-from-gw
spec:
  hosts:
  - "*"
  gateways:
  - echo-tcp-gateway
  tcp:
  - match:
    - port: 31400
    route:
    - destination:
        host: tcp-echo-service
        port:
          number: 2701

使用 SNI 透傳的流量路由

我們還可以結合前面提到的兩種功能:根據 SNI 主機名路由 TCP 流量,而不在 Istio 入口閘道器上終止流量。所有閘道器要做的就是檢查 SNI 標頭並將流量路由到特定的後端,然後由後端終止 TLS 連線。連線將"透傳"閘道器並由實際服務處理,而不是由閘道器處理。

以下是設定為使用 PASSTHROUGH 作為路由機制的 Gateway 定義:

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: sni-passthrough-gateway
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 31400
      name: tcp-sni
      protocol: TLS
    hosts:
    - "simple-sni-1.istioinaction.io"
    tls:
      mode: PASSTHROUGH

操作技巧

分割閘道器責任

雖然我們將入口閘道器定位為單一入口點,但您可以(有時應該)擁有多個入口點。您可能希望佈署另一個入口點來分割流量並隔離各種服務之間的流量路徑。

無論出於什麼原因,允許多個入口閘道器與各種邊界(合規性、域、團隊等)對齊可能是個好主意。

閘道器注入

另一種允許使用者建立自己的閘道器而不必給予他們對 IstioOperator 資源的完全存取許可權的方法是透過閘道器注入。使用閘道器注入,您佈署一個存根閘道器佈署,Istio 填充其餘部分,類別似於邊車注入的方式。

入口閘道器存取日誌

代理的常見功能是記錄它處理的每個單獨請求。這些存取日誌對於故障排除非常有用。Istio 的代理(Envoy)可以生成存取日誌。在演示安裝設定檔案中,入口閘道器和服務代理被設定為將存取日誌列印到標準輸出流。

減少閘道器設定

預設情況下,Istio 設定每個代理以瞭解網格中的每個服務。如果您的網格中有許多服務,資料平面代理的設定可能會變得非常大。這種大型設定可能導致資源膨脹、效能問題和可擴充套件性問題。

  • 入口閘道器提供對進入服務網格的流量的精細控制。
  • 使用 Gateway 資源,我們可以設定為特定主機允許進入網格的流量型別。
  • 與網格中的任何服務一樣,它使用 VirtualService 資源來路由流量。
  • TLS 模式可以按主機設定,模式之一:
    • 使用 SIMPLE TLS 模式加密並防止中間人攻擊。
    • 使用 MUTUAL TLS 模式相互認證伺服器和客戶端。
    • 使用 PASSTHROUGH TLS 模式透過 SNI 標頭接受和反向代理加密流量。
  • Istio 支援純 TCP 流量,用於當前不支援的 L7 協定。但是,純 TCP 無法使用許多功能,如重試、複雜路由等。
  • 我們可以使用閘道器注入使團隊能夠管理自己的閘道器。

流量控制:精細的流量路由

降低佈署新程式碼的風險

在第一章中,我們介紹了 ACME Inc. 遷移到雲平台並採用幫助降低佈署程式碼風險的實踐的場景。ACME 嘗試的模式之一是藍/綠佈署,以引入應用程式變更。

藍/綠佈署有所幫助,但當我們從 v1 切換到 v2 時,我們仍然經歷一個"大爆炸",一次性釋放所有程式碼變更。讓我們看如何進一步降低佈署風險。

佈署與發布的區別

讓我們使用我們虛構的目錄服務來説明佈署和發布之間的區別。假設目錄服務的 v1 目前在生產環境中執行。如果我們想對目錄服務引入程式碼變更,我們期望使用持續整合系統構建它,用新版本(假設是 v1.1)標記它,然後佈署它並在預生產環境中測試它。

當我們進行生產佈署時,我們將新程式碼安裝到生產資源上(伺服器、容器等),但不向其傳送任何流量。在這一點上,我們可以在生產中執行新佈署的測試,以驗證它是否按預期工作。

一旦我們在生產中佈署了程式碼,我們可以做出業務決策,如何將其發布給我們的使用者。發布程式碼意味著將實時流量帶到我們的新佈署。但這不是一個全有或全無的命題。

我們可以決定只向內部員工發布新軟體。這些內部員工可以控制流量,使他們暴露於軟體的新版本。作為軟體的操作者,他們可以觀察(使用日誌和指標收集)並驗證程式碼變更是否產生了預期的效果。

現在,我們軟體的舊版本承擔了大部分實時流量,而較新的版本承擔了一小部分流量。這種方法被稱為金絲雀發布(使用煤礦中金絲雀的比喻)。基本上,我們選擇一小群使用者暴露於我們程式碼的新版本,並觀察它的行為。如果它有意外行為,我們可以撤回發布並將流量重定向到我們服務的先前版本。

如果我們對新程式碼變更的行為和效能感到滿意,我們可以進一步開啟發布的範圍。我們可能希望允許非付費客戶或銀級(相對於金級或白金級)客戶現在看到這些變更。

我們繼續這種迭代方法來發布和觀察,直到我們所有的客戶都暴露於這些新程式碼變更。

在過去,ACME 將佈署和發布這兩個概念結合起來。為了將程式碼變更帶到生產中,公司啟動了一個滾動升級,有效地用服務的新版本替換了舊版本。一旦新版本被引入叢集,它就會接收生產流量。這種方法將使用者暴露於程式碼的新版本以及它帶來的任何錯誤或問題。

解耦佈署和發布允許我們更精細地控制如何以及哪些使用者暴露於新變更。這允許我們降低將新程式碼帶到生產的風險。

使用 Istio 路由請求

在第 2 章中,我們使用 Istio 控制到目錄服務的流量。我們使用 Istio VirtualService 資源來指定如何路由流量。讓我們仔細看它是如何工作的。我們將根據請求的內容(透過評估其標頭)控制請求的路由。透過這種方式,我們可以使用稱為暗發布的技術使佈署對某些使用者可用。在暗發布中,大部分使用者被傳送到服務的已知工作版本,而某些類別的使用者被傳送到較新的版本。

佈署目錄服務的 v1

讓我們佈署我們的目錄服務的 v1。從書的原始碼的根目錄執行以下命令:

kubectl apply -f services/catalog/kubernetes/catalog.yaml

接下來,讓我們使用 Istio Gateway 資源將目錄服務暴露給叢集外部的客戶端:

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: catalog-gateway
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "catalog.istioinaction.io"

然後,我們需要建立一個 VirtualService 資源,將流量路由到我們的目錄服務:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: catalog-vs-from-gw
spec:
  hosts:
  - "catalog.istioinaction.io"
  gateways:
  - catalog-gateway
  http:
  - route:
    - destination:
        host: catalog

佈署目錄服務的 v2

為了看到 Istio 的流量控制功能,讓我們佈署目錄服務的 v2:

kubectl apply -f services/catalog/kubernetes/catalog-deployment-v2.yaml

將所有流量路由到目錄服務的 v1

讓我們將所有流量路由到目錄服務的 v1。這是在開始暗發布之前的通常流量模式。我們需要給 Istio 一個提示,關於如何識別哪些工作負載是 v1,哪些是 v2。我們建立一個 DestinationRule,將這些不同版本指定為子集:

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: catalog
spec:
  host: catalog
  subsets:
  - name: version-v1
    labels:
      version: v1
  - name: version-v2
    labels:
      version: v2

現在,讓我們更新我們的 VirtualService,將所有流量路由到目錄的 v1:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: catalog-vs-from-gw
spec:
  hosts:
  - "catalog.istioinaction.io"
  gateways:
  - catalog-gateway
  http:
  - route:
    - destination:
        host: catalog
        subset: version-v1

將特定請求路由到 v2

也許我們希望將包含 HTTP 標頭 x-istio-cohort: internal 的任何流量路由到目錄的 v2。我們可以在 Istio VirtualService 資源中指定這種請求路由:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: catalog-vs-from-gw
spec:
  hosts:
  - "catalog.istioinaction.io"
  gateways:
  - catalog-gateway
  http:
  - match:
    - headers:
        x-istio-cohort:
          exact: "internal"
    route:
    - destination:
        host: catalog
        subset: version-v2
  - route:
    - destination:
        host: catalog
        subset: version-v1

在呼叫圖深處路由

到目前為止,我們已經看到了如何使用 Istio 進行請求路由,但我們一直在從邊緣/閘道器進行路由。這些流量規則也可以應用於呼叫圖的深處。

流量轉移

在本文中,我們看另一種方式來"金絲雀"或增量發布佈署。在上一節中,我們展示了根據標頭比對的路由,以實作某些使用者組的暗發布。在本文中,我們根據權重將所有實時流量分配到特定服務的一組版本。例如,如果我們已經向內部員工暗發布了目錄服務的 v2,並且希望慢向所有人發布這個版本,我們可以指定 10% 的路由權重到 v2:10% 的所有目的地為目錄的流量將去往往 v2,90% 的流量仍然去往往 v1。

讓我們將 10% 的流量路由到目錄的 v2:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: catalog
spec:
  hosts:
  - catalog
  gateways:
  - mesh
  http:
  - route:
    - destination:
        host: catalog
        subset: version-v1
      weight: 90
    - destination:
        host: catalog
        subset: version-v2
      weight: 10

如果我們想要 50/50 分割流量,我們只需要更新路由上的權重:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: catalog
spec:
  hosts:
  - catalog
  gateways:
  - mesh
  http:
  - route:
    - destination:
        host: catalog
        subset: version-v1
      weight: 50
    - destination:
        host: catalog
        subset: version-v2
      weight: 50

使用 Flagger 進行金絲雀發布

Istio 為操作者提供了一些強大的功能來控制流量路由,但我們必須手動進行路由更改並從 CLI 應用新設定。我們還建立了設定的多個版本,這意味著更多的工作和錯誤設定的機會。

我們希望避免這種手動人為干預來驅動金絲雀,因為可能同時進行數百個發布,我們希望減少錯誤的機會。我們可以使用像 Flagger 這樣的工具自動發布服務。Flagger 是一個金絲雀自動化工具,允許您指定關於如何執行發布的引數、何時向更多使用者開放發布以及何時在發布引入問題時回復。Flagger 建立所有適當的設定來驅動發布。

進一步降低風險:流量映象

使用前面兩種技術的請求級路由和流量轉移,我們可以降低做發布的風險。這兩種技術都使用實時流量和請求,並可能影響使用者,即使您可以控制潛在負面影響的範圍有多廣。另一種方法是將生產流量映象到一個新的佈署,該佈署複製生產流量並將其傳送到新的佈署,而不影響任何客戶流量。

使用映象方法,我們可以將真實的生產流量引導到我們的佈署,並獲得關於新程式碼將如何表現的真實反饋,而不影響使用者。Istio 支援映象流量,這可以比其他兩種方法更降低佈署和發布的風險。

要將流量映象到目錄服務的 v2,我們首先將所有流量重置為 v1。然後,讓我們看需要進行映象的 VirtualService:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: catalog
spec:
  hosts:
  - catalog
  gateways:
  - mesh
  http:
  - route:
    - destination:
        host: catalog
        subset: version-v1
      weight: 100
    mirror:
      host: catalog
      subset: version-v2

使用這個 VirtualService 定義,我們將 100% 的實時流量路由到目錄服務的 v1,但我們也將流量映象到 v2。映象是以即發即忘的方式完成的,其中建立請求的副本並傳送到映象叢集(在這種情況下,是目錄的 v2)。這個映象請求不能影響真實請求,因為執行映象的 Istio 代理忽略來自映象叢集的任何回應(成功/失敗)。

使用 Istio 的服務發現路由到叢集外的服務

預設情況下,Istio 允許任何流量離開服務網格。例如,如果應用程式嘗試與服務網格不管理的外部網站或服務通訊,Istio 允許此流量出去。由於所有流量首先透過服務網格邊車代理(Istio 代理)傳遞,我們可以控制流量路由,我們可以更改 Istio 的預設策略並拒絕所有嘗試離開網格的流量。

阻止所有離開網格的流量是一種基本的深度防禦姿態,以防止如果網格內的服務或應用程式受到損害,惡意行為者打電話回家。

讓我們設定 Istio 阻止外部流量,離開網格提供一個簡單的保護層。執行以下命令將 Istio 的預設設定從 ALLOW_ANY 更改為 REGISTRY_ONLY。這意味著我們只允許流量離開網格,如果它在服務網格登入檔中明確列入白名單:

istioctl install --set profile=demo --set meshConfig.outboundTrafficPolicy.mode=REGISTRY_ONLY

由於並非所有服務都存在於服務網格中,我們需要一種方式讓網格內的服務與網格外的服務通訊。這些可能是現有的 HTTP 服務,或者更可能是基礎設施服務,如資料函式庫或快取。我們仍然可以為居住在 Istio 外部的服務實作複雜的路由,但首先我們必須引入 ServiceEntry 的概念。

Istio 建立了一個內部服務登入檔,包含網格已知的所有服務,這些服務可以在網格記憶體取。您可以將此登入檔視為服務發現登入檔的規範表示,網格內的服務可以使用它來查詢其他服務。

在我們虛構的商店中,我們希望提供最好的客戶服務,並允許客戶直接相互給予反饋或分享想法。為此,我們將我們的使用者與一個線上論壇連線,該論壇在我們的服務網格叢集外部構建和佈署。在這種情況下,我們的論壇位於 URL jsonplaceholder.typicode.com。

Istio ServiceEntry 資源封裝了我們可以用來在 Istio 的服務登入檔中插入條目的登入檔中繼資料。以下是一個例子:

apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
  name: jsonplaceholder
spec:
  hosts:
  - jsonplaceholder.typicode.com
  ports:
  - number: 80
    name: http
    protocol: HTTP
  resolution: DNS
  location: MESH_EXTERNAL

這個 ServiceEntry 資源在 Istio 的服務登入檔中插入一個條目,明確表示網格內的客戶端被允許使用主機 jsonplaceholder.typicode.com 呼叫 JSON Placeholder。

  • 工作負載可以使用 DestinationRules 分成更小的子集,如版本 v1 和版本 v2。
  • VirtualServices 使用這些子集以精細的方式路由流量。
  • VirtualServices 根據應用層訊息(如 HTTP 標頭)設定路由決策。這使得暗發布技術成為可能,將特定的使用者集(如 beta 測試者)傳送到服務的新版本進行測試。
  • 使用加權路由(使用 VirtualService 資源設定)的服務代理可以逐漸將流量路由到新佈署,啟用金絲雀佈署(又稱流量轉移)等方法。
  • 流量轉移可以使用 Flagger 自動化,這是一個開放原始碼解決方案,使用收集的指標逐漸增加路由到新佈署的流量。
  • 將 outboundTrafficPolicy 設定為 REGISTRY_ONLY 可以透過阻止所有離開叢集的流量來防止惡意行為者打電話回家。
  • 當出站流量設定為 REGISTRY_ONLY 時,ServiceEntry 可以允許流量到外部服務。

彈性:解決應用網路挑戰

在應用程式中建立彈性

微服務必須將彈性作為首要考慮因素來構建。“只構建它使它不會失敗"的世界不是真實的;當失敗發生時,我們冒著使所有服務當機的風險。當我們構建服務透過網路通訊的分散式系統時,我們冒著建立更多失敗點和可能發生災難性故障的風險。服務擁有者應該在其應用程式和服務中一致地採用一些彈性模式。

如果服務 A 呼叫服務 B,並在傳送到服務 B 的特定端點的請求中經歷延遲,我們希望它主動識別這一點並路由到其他端點、其他可用區或甚至其他區域。如果服務 B 經歷間歇性錯誤,我們可能希望重試失敗的請求。同樣,如果我們在呼叫服務 B 時遇到問題,我們可能希望退後,直到它能夠從可能遇到的任何問題中還原。如果我們繼續對服務 B 施加負載(在某些情況下,當我們重試請求時放大負載),我們冒著使服務過載的風險。這種過載可能波及到服務 A 和任何依賴這些服務的人,並導致顯著的級聯錯誤。

解決方案是構建我們的應用程式以期望失敗,並有一種方式讓它們在服務請求時自動嘗試補救或回退到替代路徑。例如,當服務 A 呼叫服務 B 並開始遇到問題時,我們可以重試請求、超時我們的請求或使用斷路器模式取消任何進一步的出站請求。在本章中,我們探討 Istio 如何被用來透明地解決這些問題,以便應用程式無論用什麼程式設計語言編寫,都能正確與一致地實作彈性關注點。

在應用程式函式庫中建立彈性

在服務網格技術廣泛可用之前,作為服務開發人員,我們必須將許多這些基本彈性模式寫入我們的應用程式碼中。開放原始碼社群中出現了一些框架,幫助解決這些問題。Twitter 在 2011 年開放原始碼了其彈性框架 Finagle。Twitter Finagle 是一個 Scala/Java/JVM 應用程式函式庫,可用於實作各種遠端過程呼叫(RPC)彈性模式,如超時、重試和斷路。不久之後,Netflix 開放原始碼了其彈性框架的元件,包括 Hystrix 和 Ribbon,分別提供斷路和客戶端負載平衡。

這些框架的問題是,在不同的語言、框架和基礎設施的排列組閤中,我們將有不同的實作。Twitter Finagle 和 NetflixOSS 對 Java 開發人員來説很棒,但 Node.js、Go 和 Python 開發人員必須找到或實作自己的變體。在某些情況下,這些函式庫對應用程式碼也是侵入性的,因此網路程式碼散佈在周圍並掩蓋了實際的業務邏輯。最後,在多種語言和框架中維護這些函式庫會給執行微服務的操作方面帶來壓力:我們必須嘗試同時修補和維護所有組合的功能奇偶性。

使用 Istio 解決這些問題

如我們在前幾章所見,Istio 的服務代理位於應用程式旁邊,處理所有進出應用程式的網路流量。使用 Istio,由於服務代理解應用程式級請求和訊息(如 HTTP 請求),我們可以在代理中實作彈性功能。例如,我們可以設定 Istio 在遇到 HTTP 503 錯誤時重試失敗的請求最多三次。我們可以精確設定要重試哪些失敗、我們希望的重試次數以及每次重試的超時。由於服務代理是按服務例項佈署的,我們可以有非常細粒度的重試行為,定製以適應用程式的特定需求。對於 Istio 的所有彈性設定也是如此。Istio 的服務代理開箱即用地實作了這些基本彈性模式:

  • 客戶端負載平衡
  • 位置感知負載平衡
  • 超時和重試
  • 斷路

彈性的分散實作

使用 Istio,我們看到資料平面代理,透過它應用程式的請求遍歷,與應用程式例項共同定位,不需要集中式閘道器。如果我們使用將這些彈性模式的處理共同定位到程式碼中的應用程式函式庫,我們會得到相同的架構。在解決一些這些跨切面分散式系統的先前迭代中,我們已經將昂貴的、難以更改的、集中式的硬體裝置和其他軟體中介軟體放入請求的路徑中(硬體負載平衡器、訊息系統、企業服務匯流排、API 管理等)。這些早期的實作,為更靜態的環境構建,不能很好地擴充套件或回應高度動態、彈性的雲架構和基礎設施。在解決一些這些彈性模式時,我們應該選擇分散式實作。

客戶端負載平衡

客戶端負載平衡是一種做法,告知客戶端服務可用的各種端點,並讓客戶端選擇特定的負載平衡演算法,以最佳分配請求到端點。這減少了對集中式負載平衡的依賴,集中式負載平衡可能建立瓶頸和故障點,並允許客戶端直接、有意地向特定端點發出請求,而不必採取不必要的額外跳躍。因此,我們的客戶端和服務可以更好地擴充套件並處理不斷變化的拓撲。

Istio 使用服務和端點發現來裝備服務間通訊的客戶端代理,提供正確和最新的訊息。服務的開發人員和操作人員可以透過 Istio 設定這種客戶端負載平衡行為。

服務操作人員和開發人員可以透過定義 DestinationRule 資源來設定客戶端使用的負載平衡演算法。Istio 的服務代理根據 Envoy,支援 Envoy 的負載平衡演算法,其中一些包括:

  • 輪詢(預設)
  • 隨機
  • 加權最少請求

測試各種客戶端負載平衡策略

輪詢和隨機都是簡單的負載平衡演算法。它們實作簡單,理解簡單。輪詢(或迴圈)按順序將請求傳遞給端點。隨機均勻地隨機選擇一個端點。對於這兩種策略,您可以期望類別似的分佈。這些策略的挑戰在於,負載平衡器池中的端點通常不是均勻的,即使它們由相同的服務和資源支援。正如我們在測試中模擬的那樣,這些端點中的任何一個都可能經歷垃圾收集或資源爭用,引入高延遲,而輪詢和隨機不考慮任何執行時行為。

最少連線負載平衡器(在 Envoy 中,它被實作為最少請求)確實考慮了特定端點的延遲。當它向端點傳送請求時,它監控佇列深度,跟蹤活動請求,並選擇活動請求最少的端點。使用這種型別的演算法,我們可以避免向表現不佳的端點傳送請求,並偏好那些回應更快的端點。

位置感知負載平衡

控制平面(如 Istio 的)的一個角色是理解服務的拓撲以及該拓撲可能如何演變。理解服務網格中服務的整體拓撲的一個優勢是自動根據服務和對等服務位置等啟發式方法做出路由和負載平衡決策。

Istio 支援一種負載平衡型別,它給路由賦予權重,並根據特定工作負載的位置做出路由決策。例如,Istio 可以識別特定服務佈署的區域和可用區,並優先考慮更近的服務。如果 simple-backend 服務佈署在多個區域(us-west、us-east、europe-west),則有多種選擇來呼叫它。如果 simple-web 佈署在 us-west 區域,我們希望從 simple-web 到 simple-backend 的呼叫保持在 us-west 本地。如果我們平等對待所有端點,我們可能會在跨區域或區域時遇到高延遲和成本。

位置負載平衡的實際操作

當在 Kubernetes 中佈署時,區域和區域訊息可以增加到 Kubernetes 節點上的標籤中。例如,標籤 failure-domain.beta.kubernetes.io/region 和 failure-domain.beta.kubernetes.io/zone 允許我們分別指定區域和區域。通常,這些標籤由 Google Cloud 和 Amazon Web Services(AWS)等雲提供商自動增加。Istio 拾取這些節點標籤並用位置訊息豐富 Envoy 負載平衡端點。

透過加權分佈更多地控制位置負載平衡

在前一節中,我們看到了位置感知負載平衡的實際操作。位置感知負載平衡的最後一個方面是,您可以控制它的工作方式。預設情況下,Istio 的服務代理將所有流量傳送到同一位置的服務,只有在出現故障或不健康的端點時才會溢位。我們可以影響這種行為,在我們可能希望在多個位置區域之間負載平衡一些流量的情況下,也稱為跨位置的加權分佈。當我們預期特定位置的服務由於峰值或季節性流量而過載時,我們可能希望這樣做。

透明超時和重試

在構建依賴於分佈在網路上的元件的系統時,最大的問題包括延遲和故障。我們在前面的部分中看到

微服務架構中的安全通訊挑戰

在微服務架構中,服務間通訊的安全性是一個核心問題。隨著服務數量的增加,服務間的互動變得更加複雜,安全風險也隨之提高。傳統的網路邊界安全已不足以應對這種分散式架構的挑戰,我們需要一種更加細粒度、更加靈活的安全模型。

在這篇文章中,我將探討如何利用 Istio 服務網格來實作微服務間的安全通訊,包括服務間身份驗證、使用者身份驗證與授權策略的實作。

應用網路安全的基本需求

應用安全包含所有保護關鍵應用資料不被未授權使用者存取、竊取或破壞的活動。為了保護使用者資料,我們需要:

  1. 身份驗證與授權:在允許存取資源前,對使用者進行身份驗證和授權
  2. 傳輸加密:確保資料在傳輸過程中不被竊聽

身份驗證是客戶端或伺服器證明其身份的過程,可以透過以下方式實作:

  • 知道的東西(密碼)
  • 擁有的東西(裝置、證書)
  • 自身特徵(指紋等生物特徵)

授權則是在身份驗證之後,決定已驗證的使用者是否有權執行特定操作(如建立、讀取、更新或刪除資源)的過程。

服務間身份驗證的重要性

在微服務架構中,服務應該對其互動的其他服務進行身份驗證。換句話説,只有在對方提供可驗證的身份檔案後,服務才應該信任對方。通常,這個檔案需要由可信的第三方進行驗證。

在本文中,我將展示 Istio 如何使用 SPIFFE(Secure Production Identity Framework for Everyone)框架自動為服務頒發身份,並使用這些身份進行服務間的相互認證。

單體應用與微服務的安全對比

單體應用和微服務都需要實作終端使用者和服務間的身份驗證與授權。然而,微服務有更多的互連和網路請求需要保護。相比之下,單體應用的連線較少,通常執行在更靜態的基礎設施上,如虛擬機器或物理機。

在靜態基礎設施中,IP 地址是一個良好的身份來源,常用於證書認證和網路防火牆規則。然而,微服務可能發展到數百甚至數千個服務,使得在靜態環境中執行變得不可行。因此,團隊利用動態環境如雲端運算和容器協調,服務被排程到眾多伺服器上與生命週期短暫。這使得傳統方法如使用 IP 地址作為身份來源變得不可靠。

更糟的是,服務不一定執行在同一網路中,可能跨越不同的雲提供商,甚至在本地執行。為瞭解決這些挑戰並在高度動態和異構環境中提供身份,Istio 使用了 SPIFFE 規範。

Istio 安全機制概述

Istio 的安全機制建立在 SPIFFE 身份模型之上。SPIFFE 身份是一個符合 RFC 3986 的 URI,格式為 spiffe://trust-domain/path,其中:

  • trust-domain 代表身份的發行者,如個人或組織
  • path 在信任域內唯一標識一個工作負載

Istio 使用工作負載執行的服務帳戶來填充這個路徑。這個 SPIFFE 身份被編碼在 X.509 證書中,也稱為 SPIFFE 可驗證身份檔案(SVID),由 Istio 的控制平面為工作負載生成。這些證書用於透過加密傳輸中的資料來保護服務間通訊。

從服務網格操作者的角度來看,Istio 安全性透過以下資源進行設定:

  • PeerAuthentication:設定代理以驗證服務間流量。成功驗證後,代理提取對等證書中編碼的訊息,並使其可用於授權請求。
  • RequestAuthentication:設定代理以驗證終端使用者憑證。成功驗證後,也提取憑證中編碼的訊息,使其可用於授權請求。
  • AuthorizationPolicy:設定代理根據前兩個資源提取的資料來授權或拒絕請求。

自動相互 TLS(mTLS)實作

Istio 預設對注入了 sidecar 代理的服務之間的流量進行加密和相互認證。擁有一個自動化的證書頒發和輪換過程非常重要,因為歷史上這個過程由人工管理時容易出錯,導致不必要的代價高昂的停機,而這些本可以透過 Istio 實作的自動化過程避免。

設定環境

讓我們透過一個實際例子來展示相互 TLS 的功能。我們將設定三個服務:

  1. webapp 服務
  2. catalog 服務
  3. sleep 服務(代表一個遺留工作負載,沒有 sidecar 代理,因此無法相互認證)

首先,執行以下命令清理環境:

kubectl config set-context $(kubectl config current-context) --namespace=istioinaction
kubectl delete virtualservice,deployment,service,destinationrule,gateway --all

然後安裝服務:

kubectl label namespace istioinaction istio-injection=enabled
kubectl apply -f services/catalog/kubernetes/catalog.yaml
kubectl apply -f services/webapp/kubernetes/webapp.yaml
kubectl apply -f services/webapp/istio/webapp-catalog-gw-vs.yaml
kubectl apply -f ch9/sleep.yaml -n default

透過從遺留的 sleep 工作負載向 webapp 工作負載執行明文請求來驗證服務是否正確設定:

kubectl -n default exec deploy/sleep -c sleep -- curl -s webapp.istioinaction/api/catalog -o /dev/null -w "%{http_code}"

如果回傳 200,表示服務設定正確,webapp 服務接受了來自 sleep 服務的明文請求。預設情況下,Istio 允許明文請求,以便團隊可以逐步採用服務網格,直到所有工作負載都遷移到網格中。

理解 Istio 的 PeerAuthentication 資源

PeerAuthentication 資源允許設定工作負載嚴格要求 mTLS 或寬容地接受明文流量,分別使用 STRICT 或 PERMISSIVE 認證模式。相互認證模式可以在不同範圍內設定:

  1. 網格範圍:適用於服務網格中的所有工作負載
  2. 名稱空間範圍:適用於名稱空間中的所有工作負載
  3. 工作負載特定:適用於與策略中指定的選擇器比對的所有工作負載

使用網格範圍策略拒絕所有非認證流量

為了提高網格的安全性,我們可以透過建立一個強制執行 STRICT 相互認證模式的網格範圍策略來禁止明文流量:

apiVersion: "security.istio.io/v1beta1"
kind: "PeerAuthentication"
metadata:
  name: "default"
  namespace: "istio-system"
spec:
  mtls:
    mode: STRICT

網格範圍的 PeerAuthentication 策略必須滿足兩個條件:必須應用於 Istio 安裝名稱空間,並且必須命名為 “default”。

應用此策略後,來自 sleep 服務的明文請求將不再被允許:

kubectl apply -f ch9/meshwide-strict-peer-authn.yaml
kubectl -n default exec deploy/sleep -c sleep -- curl -s webapp.istioinaction/api/catalog

這將回傳錯誤,驗證明文請求被拒絕。

針對特定工作負載的 PeerAuthentication 策略

為了只針對 webapp,我們可以更新 PeerAuthentication 策略以指定工作負載選擇器:

apiVersion: "security.istio.io/v1beta1"
kind: "PeerAuthentication"
metadata:
  name: "webapp"
  namespace: "istioinaction"
spec:
  selector:
    matchLabels:
      app: "webapp"
  mtls:
    mode: PERMISSIVE

這樣,寬容的相互認證策略只適用於 webapp 工作負載,而不適用於 catalog 工作負載。應用策略後,明文請求將被 webapp 接受:

kubectl apply -f ch9/workload-permissive-peer-authn.yaml
kubectl -n default exec deploy/sleep -c sleep -- curl -s webapp.istioinaction/api/catalog

這將回傳成功回應。使用網格範圍策略,我們應用了嚴格的預設設定。但對於某些服務(落後者),我們使用工作負載特定策略允許非相互認證流量,直到它們遷移到網格中。

使用 tcpdump 監聽服務間流量

為了驗證使用相互 TLS 時流量是加密的,我們可以使用 tcpdump 命令列工具捕捉和分析網路流量:

istioctl install -y --set profile=demo --set values.global.proxy.privileged=true
kubectl delete po -l app=webapp -n istioinaction
kubectl -n istioinaction exec deploy/webapp -c istio-proxy -- sudo tcpdump -l --immediate-mode -vv -s 0 '(((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)'

在另一個終端中觸發請求:

kubectl -n default exec deploy/sleep -c sleep -- curl -s webapp.istioinaction/api/catalog

檢視第一個終端,可以看到訊息是明文的。這表明惡意使用者可以輕易地透過攔截中間網路裝置來利用明文流量取得終端使用者資料。相比之下,webapp 到 catalog 工作負載的流量是相互認證和加密的,無法被竊聽。

驗證工作負載身份與服務帳戶的關聯

我們可以使用 openssl 命令工具檢查 catalog 工作負載的 X.509 證書內容:

kubectl -n istioinaction exec deploy/webapp -c istio-proxy -- openssl s_client -showcerts -connect catalog.istioinaction.svc.cluster.local:80 -CAfile /var/run/secrets/istio/root-cert.pem | openssl x509 -in /dev/stdin -text -noout

證書包含 SPIFFE ID,設定為工作負載的服務帳戶:

X509v3 Subject Alternative Name: critical
URI:spiffe://cluster.local/ns/istioinaction/sa/catalog

使用 openssl verify 工具,我們可以確保 X.509 SVID 的內容有效:

kubectl -n istioinaction exec -it deploy/webapp -c istio-proxy -- /bin/bash
openssl verify -CAfile /var/run/secrets/istio/root-cert.pem <(openssl s_client -connect catalog.istioinaction.svc.cluster.local:80 -showcerts 2>/dev/null)

成功驗證後,將顯示 OK 訊息。這告訴我們 Istio CA 簽署了證書,其中的資料是可信的。

服務間流量授權是定義已驗證主體是否被允許執行操作(如存取、編輯或刪除資源)的過程。策略結合已驗證主體(“誰”)和授權(“什麼”)來定義誰可以做什麼。

Istio 提供 AuthorizationPolicy 資源,這是一個宣告式 API,用於在服務網格中定義網格範圍、名稱空間或工作負載特定的存取策略。

理解 Istio 中的授權

佈署在每個服務旁的服務代理是授權或執行引擎,因為它包含所有用於確定請求是否應該被拒絕或允許的策略。因此,Istio 中的存取控制非常高效,因為決策直接在代理中做出。

AuthorizationPolicy 資源規範提供三個欄位來設定和定義策略:

  1. selector:定義策略適用的工作負載子集
  2. action:指定這是 ALLOW、DENY 還是 CUSTOM 策略
  3. rules:定義識別策略將被啟用的請求的規則列表

授權策略規則指定連線的來源和(可選)操作,當比對時,啟用規則。只有當規則之一比對源和操作時,授權策略執行才會被啟用。在這種情況下,根據 action 屬性允許或拒絕連線。

策略應用到工作負載時的行為變化

在深入細節之前,有一個容易被忽視的陷阱:如果一個或多個 ALLOW 授權策略應用於工作負載,則預設情況下拒絕對該工作負載的所有流量存取。為了接受流量,至少一個 ALLOW 策略必須比對它。

例如,以下 AuthorizationPolicy 資源允許包含 HTTP 路徑 /api/catalog* 的請求到 webapp:

apiVersion: "security.istio.io/v1beta1"
kind: "AuthorizationPolicy"
metadata:
  name: "allow-catalog-requests-in-web-app"
  namespace: istioinaction
spec:
  selector:
    matchLabels:
      app: webapp
  rules:
    - to:
        - operation:
            paths: ["/api/catalog*"]
  action: ALLOW

對於以下兩個請求:

  1. /api/catalog - 允許,因為授權策略比對路徑,與動作允許請求
  2. /hello/world - 拒絕,因為沒有授權策略明確允許請求

第二種情況可能會讓人困惑 - 為什麼請求被拒絕,當沒有策略允許或拒絕它時?這是當 ALLOW 策略應用於工作負載時的預設拒絕行為。

為了簡化思考過程,建議增加一個拒絕全部的策略,當沒有其他策略適用於傳入流量時啟用。這樣,我們只需要考慮想要允許的流量,並為其建立策略。

使用全部捕捉策略預設拒絕所有請求

為了增加安全性並簡化思考過程,我們定義一個網格範圍的策略,拒絕所有沒有明確指定 ALLOW 策略的請求:

apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: deny-all
  namespace: istio-system
spec: {}

應用此策略後,來自 sleep 服務的請求將被拒絕:

kubectl apply -f ch9/policy-deny-all-mesh.yaml
kubectl -n default exec deploy/sleep -c sleep -- curl -sSL webapp.istioinaction/api/catalog

輸出顯示 “RBAC: access denied”,表明拒絕全部授權策略已生效並拒絕了請求。

允許來自非認證遺留工作負載的請求

要允許來自非認證工作負載的請求,我們需要刪除 from 欄位:

apiVersion: "security.istio.io/v1beta1"
kind: "AuthorizationPolicy"
metadata:
  name: "webapp-allow-unauthenticated-view"
  namespace: istioinaction
spec:
  selector:
    matchLabels:
      app: webapp
  rules:
    - to:
        - operation:
            methods: ["GET"]

應用此策略後,從 sleep 服務到 webapp 的請求將回傳應用程式錯誤,而不是 Istio 錯誤:

kubectl apply -f ch9/allow-unauthenticated-view-default-ns.yaml
kubectl -n default exec deploy/sleep -c sleep -- curl -sSL webapp.istioinaction/api/catalog

這是因為 webapp 收到了來自 sleep 服務的請求,但網格範圍的拒絕全部策略拒絕了後續到 catalog 服務的請求。

允許來自單個服務帳戶的請求

一個簡單的方法來驗證流量是否來自 webapp 服務是使用注入到其中的服務帳戶。服務帳戶訊息被編碼到 SVID 中,在相互認證期間,該資料被驗證並儲存在過濾器中繼資料中。以下策略設定 catalog 服務使用過濾器中繼資料,並根據它只允許來自具有 webapp 服務帳戶的工作負載的流量:

apiVersion: "security.istio.io/v1beta1"
kind: "AuthorizationPolicy"
metadata:
  name: "catalog-viewer"
  namespace: istioinaction
spec:
  selector:
    matchLabels:
      app: catalog
  rules:
    - from:
        - source:
            principals: ["cluster.local/ns/istioinaction/sa/webapp"]
      to:
        - operation:
            methods: ["GET"]

應用此策略後,我們的請求成功到達 catalog 工作負載:

kubectl apply -f ch9/catalog-viewer-policy.yaml
kubectl -n default exec deploy/sleep -c sleep -- curl -sSL webapp.istioinaction/api/catalog

更重要的是,我們已經實施了嚴格的授權策略,如果工作負載的身份被竊取,損害將被限制在最小範圍內。

策略的條件比對

通常,策略僅在滿足條件時適用,例如當使用者是管理員時允許所有操作。這可以透過授權策略的 when 屬性實作:

apiVersion: "security.istio.io/v1beta1"
kind: "AuthorizationPolicy"
metadata:
  name: "allow-mesh-all-ops-admin"
  namespace: istio-system
spec:
  rules:
    - from:
        - source:
            requestPrincipals: ["auth@istioinaction.io/*"]
      when:
        - key: request.auth.claims[group]
          values: ["admin"]

此策略僅在滿足兩個條件時允許請求:首先,令牌由請求主體 auth@istioinaction.io/* 發行;其次,JWT 包含值為 admin 的 group 宣告。

授權策略規則的評估

為了理解策略規則,讓我們具體分解一個更複雜的規則:

apiVersion: "security.istio.io/v1beta1"
kind: "AuthorizationPolicy"
metadata:
  name: "allow-mesh-all-ops-admin"
  namespace: istio-system
spec:
  rules:
    - from:
        - source:
            principals: ["cluster.local/ns/istioinaction/sa/webapp"]
        - source:
            namespaces: ["default"]
      to:
        - operation:
            methods: ["GET"]
            paths: ["/users*"]
        - operation:
            methods: ["POST"]
            paths: ["/data"]
      when:
        - key: request.auth.claims[group]
          values: ["beta-tester", "admin", "developer"]
    - to:
        - operation:
            paths: [".html", ".js", "*.png"]

對於此授權策略適用於請求,第一條規則或第二條規則需要比對。對於第一條規則比對請求,我們需要在所有三個屬性中比對:源列表中定義的一個源需要與操作列表中定義的一個操作比對,並且所有條件都需要比對。

授權策略評估的順序

當許多策略應用於工作負載時,複雜性就會出現,很難理解順序。Istio 使用不同的方法評估策略:

  1. 首先評估 CUSTOM 策略
  2. 接下來評估 DENY 策略。如果沒有 DENY 策略比對…
  3. 評估 ALLOW 策略。如果一個比對,請求被允許。否則…
  4. 根據是否存在全部捕捉策略,我們有兩種結果: a. 當存在全部捕捉策略時,它決定請求是否被批准 b. 當不存在全部捕捉策略時,請求是:
    • 如果沒有 ALLOW 策略,則允許,或者
    • 當有 ALLOW 策略但沒有比對時,被拒絕

終端使用者身份驗證與授權

終端使用者身份驗證和授權在 Istio 中使用 JWT 支援。

什麼是 JSON Web Token?

JWT 是一種緊湊的宣告表示,用於驗證客戶端到伺服器。JWT 由以下三部分組成:

  1. Header:由型別和雜湊演算法組成
  2. Payload:包含使用者宣告
  3. Signature:用於驗證 JWT 的真實性

這三部分 - 頭部、負載和簽名 - 由點(.)分隔並進行 Base64 URL 編碼,使 JWT 非常適合在 HTTP 請求中使用。

JWT 由認證伺服器發行,該伺服器包含用於簽署令牌的私鑰和用於驗證令牌的公鑰。公鑰被稱為 JSON Web Key Set(JWKS),並在一個眾所周知的 HTTP 端點上公開。在這個眾所周知的 HTTP 端點,服務可以檢索公鑰來驗證由認證伺服器發行的令牌。

在入口閘道器處進行終端使用者身份驗證和授權

Istio 工作負載可以設定為使用 JWT 對終端使用者請求進行身份驗證和授權。終端使用者是指由身份提供者認證並接收代表其身份和宣告的令牌的使用者。

雖然終端使用者授權可以在任何工作負載級別完成,但此功能通常在 Istio 入口閘道器處執行。這提高了效能,因為無效請求會被提前拒絕。此外,它使 Istio 能夠從請求中刪除 JWT,以便後續服務不會意外洩露它或惡意使用者不會使用它執行重放攻擊。

使用 RequestAuthentication 驗證 JWT

RequestAuthentication 資源的主要目的是驗證 JWT,提取有效令牌的宣告,並將這些宣告儲存在過濾器中繼資料中,授權策略使用這些中繼資料根據資料採取行動。

根據終端使用者請求,可能有三種不同的結果:

  1. 具有效令牌的請求被允許進入叢集,其宣告以過濾器中繼資料的形式提供給策略
  2. 具有無效令牌的請求被拒絕
  3. 沒有令牌的請求被允許進入叢集,但缺少請求身份,意味著沒有宣告儲存在過濾器中繼資料中

以下 RequestAuthentication 資源應用於 Istio 的入口閘道器,設定入口閘道器驗證由 auth@istioinaction.io 發行的令牌:

apiVersion: "security.istio.io/v1beta1"
kind: "RequestAuthentication"
metadata:
  name: "jwt-token-request-authn"
  namespace: istio-system
spec:
  selector:
    matchLabels:
      app: istio-ingressgateway
  jwtRules:
    - issuer: "auth@istioinaction.io"
      jwks: |
        { "keys": [{"e":"AQAB","kid":"##REDACTED##", "kty":"RSA","n":"##REDACTED##"}]}

應用此資源後,我們可以驗證三種型別的請求及其預期結果:

  1. 具有效令牌的請求被接受
  2. 具有無效令牌的請求被拒絕
  3. 沒有令牌的請求被允許進入叢集

要拒絕沒有 JWT 的請求,我們需要建立一個明確拒絕它們的 AuthorizationPolicy 資源:

apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: app-gw-requires-jwt
  namespace: istio-system
spec:
  selector:
    matchLabels:
      app: istio-ingressgateway
  action: DENY
  rules:
    - from:
        - source:
            notRequestPrincipals: ["*"]
      to:
        - operation:
            hosts: ["webapp.istioinaction.io"]

此策略比對所有缺少 requestPrincipals 屬性的源的請求,然後拒絕它們。

根據 JWT 宣告的不同存取級別

在這個例子中,我們允許普通使用者從 API 讀取資料,但禁止寫入任何新資料或更改現有資料。同時,我們允許管理員完全存取。

讓我們設定一個 AuthorizationPolicy 資源,允許普通使用者在針對 webapp 時讀取資料:

apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: allow-all-with-jwt-to-webapp
  namespace: istio-system
spec:
  selector:
    matchLabels:
      app: istio-ingressgateway
  action: ALLOW
  rules:
    - from:
        - source:
            requestPrincipals: ["auth@istioinaction.io/*"]
      to:
        - operation:
            hosts: ["webapp.istioinaction.io"]
            methods: ["GET"]

使用 AuthorizationPolicy 資源,我們允許管理員使用者執行所有操作:

apiVersion: "security.istio.io/v1beta1"
kind: "AuthorizationPolicy"
metadata:
  name: "allow-mesh-all-ops-admin"
  namespace: istio-system
spec:
  rules:
    - from:
        - source:
            requestPrincipals: ["auth@istioinaction.io/*"]
      when:
        - key: request.auth.claims[group]
          values: ["admin"]

應用這些資源後,我們可以驗證普通使用者可以讀取資料但不能寫入,而管理員可以寫入。

與自定義外部授權服務整合

我們已經看到 Istio 的根據 SPIFFE 的身份驗證機制為服務授權提供了基礎。Istio 使用 Envoy 的內建根據角色的存取控制(RBAC)功能來實作授權 - 但如果我們需要更複雜或自定義的授權機制呢?我們可以設定 Istio 的服務代理呼叫不同的授權服務來確定是否允許請求。

在這種情況下,進入服務代理的請求會暫停,而代理呼叫外部授權(ExtAuthz)服務。這個 ExtAuthz 服務可以存在於網格中,作為應用程式的 sidecar,甚至在網格之外。ExtAuthz 需要實作 Envoy 的 CheckRequest API。

使用自定義 AuthorizationPolicy 資源

在前面的部分中,我們建立了具有 DENY 或 ALLOW 動作的 AuthorizationPolicy 資源。在這一部分,我們建立一個具有 CUSTOM 動作的 AuthorizationPolicy,並指定要使用的 ExtAuthz 服務:

apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: ext-authz
  namespace: istioinaction
spec:
  selector:
    matchLabels:
      app: webapp
  action: CUSTOM
  provider:
    name: sample-ext-authz-http
  rules:
    - to:
        - operation:
            paths: ["/"]

此 AuthorizationPolicy 資源應用於 istioinaction 名稱空間中的 webapp 工作負載,委託給名為 sample-ext-authz-http 的 ExtAuthz 服務。

在本文中,我們探討了 Istio 服務網格中的安全通訊機制。我們瞭解到 PeerAuthentication 用於定義對等身份驗證,應用嚴格的身份驗證要求確保流量被加密與不能被竊聽。PERMISSIVE 策略允許 Istio 工作負載接受加密流量和明文流量,可用於在不停機的情況下緩慢遷移。

AuthorizationPolicy 用於根據從工作負載身分證書或終端使用者 JWT 提取的一組可驗證中繼資料授權服務間和終端使用者請求。RequestAuthentication 用於驗證包含 JWT 的終端使用者請求。

我們還看到了如何使用授權策略的 CUSTOM 動作整合外部授權服務,為特定場景提供更靈活的授權機制。

透過這些機制,Istio 提供了一個強大的安全框架,使微服務架構能夠實作細粒度的存取控制和加密通訊,同時保持靈活性和可擴充套件性。這種安全模型特別適合現代雲原生應用,其中服務可能分佈在不同的環境中,傳統的網路邊界安全已不足以提供足夠的保護。

服務網格的控制平面效能調校

在服務網格架構中,控制平面的效能對整體系統的穩定性和可靠性至關重要。本文將探討如何監控、分析和最佳化 Istio 控制平面的效能,確保服務網格能夠高效運作。

控制平面的核心目標

控制平面是服務網格的大腦,負責將最新的設定同步到資料平面。這個同步過程必須及時完成,否則可能導致服務路由到已不存在的工作負載(幽靈工作負載),進而造成請求失敗。

資料平面同步的步驟

資料平面同步是一個多步驟的過程:

  1. 控制平面接收來自 Kubernetes 的事件
  2. DiscoveryServer 元件會先延遲處理事件(去抖動),以便批次處理
  3. 延遲期結束後,合併的事件被增加到推播佇列
  4. 系統限制同時處理的推播請求數量(節流)
  5. 處理中的專案被轉換為 Envoy 設定並推播到工作負載

影響效能的因素

控制平面效能受到以下因素影響:

  • 變更頻率:更高的變更率需要更多處理能力
  • 分配資源:若資源不足,工作必須排隊等待
  • 工作負載數量:更多工作負載需要更多處理能力和網路頻寬
  • 設定大小:較大的 Envoy 設定需要更多處理能力和網路頻寬

監控制平面

四個黃金指標

根據 Google 的網站可靠性工程理念,監控服務效能的四個黃金指標為:延遲、飽和度、錯誤和流量。

延遲:更新資料平面所需的時間

關鍵指標:

  • pilot_proxy_convergence_time:從推播請求進入佇列到分發到工作負載的整個過程持續時間
  • pilot_proxy_queue_time:推播請求在佇列中等待的時間
  • pilot_xds_push_time:將 Envoy 設定推播到工作負載所需的時間

建議閾值:

  • 警告級別:延遲超過 1 秒持續 10 秒以上
  • 嚴重級別:延遲超過 2 秒持續 10 秒以上

飽和度:控制平面資源使用率

關鍵指標:

  • container_cpu_usage_seconds_total:Kubernetes 容器報告的 CPU 使用率
  • process_cpu_seconds_total:istiod 工具報告的 CPU 使用率

當控制平面飽和時,應重新考慮資源分配。

流量:控制平面的負載

入站流量指標:

  • pilot_inbound_updates:每個 istiod 例項接收的設定更新數
  • pilot_push_triggers:觸發推播的事件總數
  • pilot_services:pilot 已知的服務數量

出站流量指標:

  • pilot_xds_pushes:控制平面進行的所有型別推播
  • pilot_xds:每個 pilot 例項處理的工作負載連線總數
  • envoy_cluster_upstream_cx_tx_bytes_total:透過網路傳輸的設定大小

錯誤:控制平面的失敗率

重要的錯誤指標:

  • pilot_total_xds_rejects:被拒絕的設定推播數
  • pilot_xds_write_timeout:啟動推播時的錯誤和超時總和
  • pilot_xds_push_context_errors:生成 Envoy 設定時的錯誤數

效能調校

設定 Sidecar 資源減少設定大小和推播次數

預設情況下,Istio 會設定每個服務代理瞭解網格中的所有其他工作負載,這會導致設定膨脹。使用 Sidecar 資源可以精確定義服務需要存取的其他服務,從而減少設定大小。

例如,定義一個網格範圍的 Sidecar 設定,只允許出口流量到 istio-system 名稱空間:

apiVersion: networking.istio.io/v1beta1
kind: Sidecar
metadata:
  name: default
  namespace: istio-system
spec:
  egress:
    hosts:
      - "istio-system/*"
      - "prometheus/*"
  outboundTrafficPolicy:
    mode: REGISTRY_ONLY

這樣可以將設定大小從 2MB 減少到約 600KB,同時減少推播次數。

使用發現選擇器忽略不相關事件

預設情況下,Istio 控制平面會監視所有名稱空間中的事件。使用名稱空間發現選擇器可以限制控制平面關注的名稱空間:

apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
  namespace: istio-system
spec:
  meshConfig:
    discoverySelectors:
      - matchLabels:
          istio-discovery: enabled

事件批處理和推播節流

可以透過以下環境變數控制事件批處理和推播節流:

  • PILOT_DEBOUNCE_AFTER:指定將事件增加到推播佇列的去抖動時間(預設 100ms)
  • PILOT_DEBOUNCE_MAX:允許事件去抖動的最大時間(預設 10 秒)
  • PILOT_ENABLE_EDS_DEBOUNCE:指定端點更新是否遵循去抖動規則(預設為 true)
  • PILOT_PUSH_THROTTLE:istiod 同時處理的推播請求數(預設 100)

為控制平面分配額外資源

當其他最佳化方法不足時,可以為控制平面分配更多資源:

  • 橫向擴充套件:當出站流量是瓶頸時,增加 istiod 例項數量
  • 縱向擴充套件:當入站流量是瓶頸時,為每個 istiod 例項提供更多資源
istioctl install --set profile=demo \
  --set values.pilot.resources.requests.cpu=2 \
  --set values.pilot.resources.requests.memory=4Gi \
  --set values.pilot.replicaCount=3

效能調校

在調校效能前,請記住 Istio 本身已經非常高效。Istio 團隊測試每個新版本時使用的引數為:1000 個 Kubernetes 服務、2000 個工作負載、每秒 70,000 個請求,這些負載僅消耗一個虛擬核心和 1.5GB 記憶體。

調校:

  1. 確認這是效能問題:檢查資料平面到控制平面的連線性、平台健康狀況等
  2. 識別效能瓶頸:使用延遲、飽和度和流量指標來指導調校決策
  3. 漸進式變更:識別瓶頸後,進行小幅度調整(10-30%)
  4. 安全優先:Istio Pilot 管理整個網格的網路,停機容易造成服務中斷
  5. 使用可突發的虛擬機器:Istio Pilot 不需要持續的 CPU 資源

最佳化 Istio 控制平面效能需要全面瞭解其工作原理和影響因素。透過定義 Sidecar 資源、使用發現選擇器、設定事件批處理和適當分配資源,可以顯著提高控制平面效能。記住,最重要的是始終為工作負載定義 Sidecar 設定,這將提供大部分效能優勢。當控制平面飽和時,才考慮修改事件批處理;當出站流量是瓶頸時進行橫向擴充套件,當入站流量是瓶頸時進行縱向擴充套件。透過這些策略,可以確保服務網格在各種規模下都能高效運作。

在企業環境中擴充套件 Istio

在前幾章中,我們已經瞭解如何設定和應用 Istio 強大的功能來支援服務架構。現在,讓我們探討如何在企業環境中擴充套件 Istio。當服務網格規模擴大時,如何進行擴充套件、故障排除和調優?如何在環境中包含多個叢集、虛擬機器和其他約束條件?最後,我們將探討如何使用 WebAssembly (Wasm) 等技術來自定義和調整服務網格的行為,以滿足特定的使用案例。

多叢集服務網格的優勢

在雲端遷移初期,企業常面臨如何規劃叢集大小的難題。以虛構的 ACME 公司為例,他們最初使用單一大型叢集,但很快就改變了決策,轉向多個較小的叢集,原因在於多叢集架構提供了以下優勢:

  • 改善隔離性 — 確保一個團隊的錯誤不會影響其他團隊
  • 故障邊界 — 限制可能影響整個叢集的設定或操作範圍,減少對架構其他部分的影響
  • 法規與合規 — 限制存取敏感資料的服務與架構其他部分的接觸
  • 提高用性與效能 — 在不同區域執行叢集以提高用性,並將流量路由到最近的叢集以減少延遲
  • 多雲與混合雲 — 能夠在不同環境中執行工作負載,無論是不同的雲提供商還是混合雲

在初步評估中,ACME 公司考慮了跨叢集擴充套件服務網格的能力,以及啟用跨叢集流量管理、可觀察性和安全性作為選擇服務網格的主要驅動因素。為了支援多叢集工作,公司考慮了兩種方法:

  • 多叢集服務網格 — 一個跨越多個叢集的網格,設定工作負載以路由跨叢集流量,並按照應用的 Istio 設定(如 VirtualService、DestinationRule 和 Sidecar 資源)進行操作。
  • 網格聯邦(多網格) — 暴露並啟用兩個獨立服務網格的工作負載之間的通訊。這種選項自動化程度較低,需要在兩個網格上手動設定服務間流量,但當網格由不同團隊操作或有嚴格安全隔離需求時,這是一個好選擇。

本文將專注於多叢集服務網格的實作。

多叢集服務網格概述

多叢集服務網格以對應用完全透明的方式連線跨叢集的服務,同時保持服務網格的所有功能:精細的流量管理、彈性、可觀察性,以及跨叢集通訊的安全性。Istio 透過查詢所有叢集中的服務,然後使用這些訊息來設定服務代理,實作了多叢集服務網格。

要將叢集加入單一網格,需要滿足以下條件:

  1. 跨叢集工作負載發現 — 控制平面必須能夠發現對等叢集中的工作負載(對方叢集的 API 伺服器必須對 Istio 的控制平面可存取)
  2. 跨叢集工作負載連線性 — 工作負載之間必須有連線性,知道工作負載端點的存在沒有用,除非我們可以發起連線
  3. 叢集間的共同信任 — 跨叢集工作負載必須能夠相互認證,以啟用 Istio 的安全功能

滿足這些條件確保了叢集能夠感知到其他叢集中執行的工作負載可以相互連線,並且工作負載可以使用 Istio 策略進行認證和授權。這些都是設定多叢集服務網格的前提條件。

多叢集連線性與安全性

如前所述,為了讓 Istio 建立叢集間的連線,工作負載只能透過存取對等叢集中的 Kubernetes API 來發現。對於某些組織來説,這可能是一種不理想的安全態勢,即每個叢集都可以存取所有其他叢集的 API。在這種情況下,網格聯邦是更好的方法。像 Gloo Mesh 這樣的專案可以幫助實作自動化和安全態勢。

Istio 多叢集佈署模型

在多叢集服務網格中,我們區分兩種型別的叢集:

  • 主叢集 — 安裝 Istio 控制平面的叢集
  • 遠端叢集 — 相對於控制平面安裝的遠端叢集

根據我們想要實作的可用性,有以下佈署模型:

主-遠端佈署模型(分享控制平面)

這種模型使用單一控制平面管理網格,因此通常被稱為單一控制平面或分享控制平面佈署模型。這種模型使用較少的資源,但主叢集的故障會影響整個網格,因此可用性較低。

主-主佈署模型(複製控制平面)

這種模型有多個控制平面,確保了更高的可用性,但需要更多資源。這提高了可用性,因為故障僅限於發生故障的叢集。我們將此模型稱為複製控制平面佈署模型。

外部控制平面

在這種佈署模型中,所有叢集都相對於控制平面是遠端的。這種佈署模型使雲提供商能夠提供 Istio 作為託管服務。

多叢集佈署中的工作負載發現機制

Istio 的控制平面需要與 Kubernetes API 伺服器通訊,以收集設定服務代理所需的相關訊息,如服務和這些服務背後的端點。向 Kubernetes API 伺服器發出請求是一種強大的能力,因為你可以查詢資源詳細訊息、敏感訊息,以及更新或刪除資源,甚至可能使叢集處於不良與不可逆的狀態。

雖然我們將介紹如何使用令牌和根據角色的存取控制(RBAC)來保護對遠端 Kubernetes API 的存取,但讀者應該考慮這種方法的權衡。

跨叢集工作負載發現的實作

跨叢集工作負載發現在技術上是相同的。然而,我們需要為遠端叢集提供服務帳戶令牌(以及啟動與 API 伺服器的安全連線所需的證書)。istiod 使用令牌向遠端叢集進行認證並發現其中執行的工作負載。

這可能聽起來是一個艱巨的過程,但不用擔心。istioctl 自動化了這個過程,我們將在後面的章節中看到。

跨叢集工作負載連線性

另一個前提條件是工作負載具有跨叢集連線性。當叢集在平坦網路中時,例如分享單一網路(如 Amazon VPC)或透過網路對等連線時,工作負載可以使用 IP 地址連線,條件已經滿足!然而,當叢集在不同網路中時,我們必須使用特殊的 Istio 入口閘道器,這些閘道器位於網路邊緣並代理跨叢集流量。在多網路網格中橋接叢集的入口閘道器被稱為東西向閘道器。

叢集間的共同信任

最後一個需要解決的因素是多叢集服務網格中的叢集必須具有共同信任。擁有共同信任確保了對方叢集的工作負載可以相互認證。有兩種方法可以實作叢集間的共同信任:

插入式 CA 證書

使用插入式中間 CA 證書很簡單!不是讓 Istio 生成中間 CA,而是透過在 Istio 安裝名稱空間中提供一個秘密來指定要使用的證書。你可以為兩個叢集都這樣做,並使用由同一個根 CA 簽名的中間 CA。

這種方法因其簡單性而受到青睞;然而,如果中間 CA 暴露,它會帶來安全風險。攻擊者可以使用它們來簽署被信任的證書,直到發現暴露並復原中間 CA 的證書。因此,組織不願意交出中間 CA。可以透過僅將中間 CA 載入到記憶體中而不將其作為 Kubernetes 秘密持久化到 etcd(儲存 Kubernetes 資源如秘密的資料儲存)來降低暴露風險。更安全的替代方案是整合簽署證書的外部 CA。

外部證書頒發機構整合

在這個解決方案中,istiod 充當序號產生器構,驗證和批准儲存為 Kubernetes CSR 的證書籤名請求(CSR)。批准的 Kubernetes CSR 透過以下方式之一提交給外部 CA:

  • 使用 cert-manager — 僅當我們的外部 CA 受 cert-manager 支援時才可行。如果是這種情況,那麼使用 cert-manager 的 istio-csr,我們可以監聽 Kubernetes CSR 並將其提交給外部 CA 進行簽名。
  • 自定義開發 — 建立一個 Kubernetes 控制器,監聽已批准的 Kubernetes CSR 並將其提交給外部 CA 進行簽名。

在本文中,我們將使用插入式 CA 證書在叢集之間建立共同信任,因為它更簡單,並且保持了對多叢集服務網格的關注。

多叢集、多網路、多控制平面服務網格概述

我們將設定一個模擬現實世界企業服務的基礎設施,這些服務在多個叢集中執行,佈署在不同區域,位於不同網路中。基礎設施包括:

  • west-cluster — 位於 us-west 區域的 Kubernetes 叢集,擁有自己的私有網路。這是我們執行 webapp 服務的地方。
  • east-cluster — 位於 us-east 區域的 Kubernetes 叢集,擁有自己的私有網路。這是我們執行 catalog 服務的地方。

在兩個不同區域擁有叢集可以在一個區域發生災難時保護我們免受服務中斷的影響。webapp 和 catalog 工作負載在不同叢集中沒有技術原因 — 這只是為了演示目的。只要可能,“聊天"的工作負載應該靠近以減少延遲。

選擇多叢集佈署模型

多網路基礎設施決定了我們需要使用東西向閘道器來橋接網路以實作跨叢集連線性,但留下了是使用複製控制平面佈署模型還是單一控制平面的決定。這個決定由業務需求驅動。在 ACME 的案例中,其線上商店非常受歡迎:每分鐘的停機都會讓業務損失數百萬!因此,高用性是首要優先事項,我們將使用主-主佈署模型,其中 Istio 控制平面佈署在每個叢集中。

總結一下,我們將設定一個多叢集、多網路、多控制平面服務網格,使用東西向閘道器來橋接網路,並使用主-主佈署模型。

設定雲基礎設施

對於多叢集,本地環境不足;我們必須使用雲提供商。在接下來的範例中,我們使用 Azure。然而,只要你在任何雲提供商中設定了兩個位於不同網路中的 Kubernetes 叢集,就可以跟著做。

在 Azure 中建立叢集

基礎設施由兩個 Kubernetes 叢集組成,每個叢集位於不同的網路中。它們的建立是透過指令碼自動化的。要執行指令碼,你需要安裝 Azure CLI 並登入以存取你的訂閲。

設定插入式 CA 證書

在第 9 章中,當我們介紹工作負載身份引導時,為了簡單起見,我們省略了 Istio 在安裝時生成 CA 來簽署證書的事實。這個生成的 CA 儲存為 Istio 安裝名稱空間中名為 istio-ca-secret 的秘密,並且 istiod 副本分享。可以透過插入我們的 CA 來覆寫預設行為,Istio CA 會使用它而不是生成新的 CA。為此,我們必須將 CA 證書儲存為安裝名稱空間 istio-system 中名為 cacerts 的秘密,包含以下資料:

  • ca-cert.pem — 中間 CA 的證書
  • ca-key.pem — 中間 CA 的私鑰
  • root-cert.pem — 發行中間 CA 的根 CA 證書。根 CA 驗證由其任何中間 CA 發行的證書,這對於跨叢集的相互信任至關重要
  • cert-chain.pem — 中間 CA 證書和根 CA 證書的連線,形成信任鏈

安裝每個叢集中的控制平面

在安裝 Istio 的控制平面之前,讓我們為每個叢集增加網路中繼資料。網路中繼資料使 Istio 能夠利用拓撲訊息並據此設定工作負載。因此,工作負載可以使用位置訊息並優先將流量路由到附近的工作負載。當 Istio 理解網路拓撲時的另一個好處是,它設定工作負載在路由流量到位於不同網路的遠端叢集中的工作負載時使用東西向閘道器。

為跨叢集連線性標記網路拓撲可以在 Istio 安裝中使用 MeshNetwork 設定進行設定。然而,這是一個僅為罕見和高階使用案例保留的遺留設定。更簡單的選項是用網路拓撲訊息標記 Istio 安裝名稱空間。

使用 IstioOperator 資源安裝控制平面

由於我們必須進行許多修改,我們將使用 IstioOperator 資源來定義 west-cluster 的 Istio 安裝:

apiVersion: install.istio.io/v1alpha1
metadata:
  name: istio-controlplane
  namespace: istio-system
kind: IstioOperator
spec:
  profile: demo
  components:
    egressGateways:
      name: istio-egressgateway
      enabled: false
  values:
    global:
      meshID: usmesh
      multiCluster:
        clusterName: west-cluster
      network: west-network

Kubernetes 叢集可以有許多租戶,並且可以跨越許多團隊。Istio 提供了在叢集中安裝多個網格的選項,允許團隊分別管理其網格操作。meshID 屬性使我們能夠識別此安裝所屬的網格。

在兩個叢集中成功安裝控制平面後,我們有兩個獨立的網格,每個網格執行一個 istiod 副本,只發現本地服務,以及用於入口流量的閘道器。

這些網格缺乏跨叢集工作負載發現和連線性,我們將在接下來的部分中設定這些。但在繼續之前,讓我們在每個叢集中執行一些工作負載。這些工作負載將有助於驗證跨叢集發現和連線性是否正確設定。

啟用跨叢集工作負載發現

為了讓 Istio 能夠認證查詢遠端叢集的訊息,它需要一個定義身份和許可權的服務帳戶。因此,Istio 在安裝時建立了一個具有最小許可權集的服務帳戶(名為 istioreader-service-account),可以由另一個控制平面用來認證自己並查詢工作負載相關訊息,如服務和端點。然而,我們需要將服務帳戶令牌與證書一起提供給對方叢集,以啟動與遠端叢集的安全連線。

建立遠端叢集存取的秘密

istioctl 工具有 create-remote-secret 命令,預設使用預設的 istio-reader-serviceaccount 服務帳戶建立遠端叢集存取的秘密。建立秘密時,重要的是指定在 IstioOperator 中 Istio 安裝期間指定的叢集名稱。

一旦建立了秘密,istiod 就會拾取它並查詢新增加的遠端叢集中的工作負載。這在 istiod 的日誌中記錄。

日誌驗證了叢集已初始化,west-cluster 控制平面可以發現 east-cluster 中的工作負載。對於主-主佈署,我們還需要做相反的事情,設定 east-cluster 來查詢 west-cluster。

設定跨叢集連線性

在第 4 章中,我們討論了 Istio 的入口閘道器,並看到它根據 Envoy 代理。它代表了源自公共網路並指向組織內部網路的流量的入口點。這種型別的流量通常被稱為南北向流量。相比之下,不同內部網路之間的流量 — 在我們的例子中,是叢集的網路 — 被稱為東西向流量。

為了簡化東西向流量,大多數雲提供商啟用了虛擬網路的對等,前提是網路地址空間不重疊。對等虛擬網路中的服務可以使用 IPv4 和 IPv6 地址啟動直接連線。然而,網路對等是一個特定於雲的功能。每當我們想要連線不同雲提供商中的叢集或無法進行網路對等的本地叢集時,Istio 提供的選項是東西向閘道器必須透過對方叢集的工作負載可存取的負載平衡器暴露。

Istio 的東西向閘道器

東西向閘道器的目標,除了作為跨叢集東西向流量的入口點外,還在於使這個過程對營運服務的團隊透明。為了實作這一目標,閘道器必須:

  • 啟用跨叢集的精細流量管理
  • 路由加密流量以啟用工作負載之間的相互認證

而與服務網格營運者不應該設定任何額外的資源!換句話説,你不應該設定任何額外的 Istio 資源!這確保了在路由叢集內或跨叢集流量時沒有差異。在這兩種情況下,工作負載都可以精細地定位服務並啟動相互認證的連線。

使用 SNI 叢集設定東西向閘道器

東西向閘道器是具有每個服務的伺服器名稱指示(SNI)叢集附加設定的入口閘道器。但什麼是 SNI 叢集?SNI 叢集就像常規的 Envoy 叢集,由方向、子集、連線埠和 FQDN 組成,這些元素將一組類別似的工作負載分組,可以路由流量。然而,SNI 叢集有一個關鍵區別:它們在 SNI 中編碼所有 Envoy 叢集訊息。這使東西向閘道器能夠將加密流量代理到客戶端在 SNI 中指定的叢集。

使用 SNI 自動透傳路由跨叢集流量

要理解 SNI 自動透傳,讓我們回想一下,手動 SNI 透傳設定入口閘道器根據 SNI 標頭接受流量。這表明要路由接受的流量,服務營運者必須手動定義 VirtualService 資源。SNI 自動透傳,顧名思義,不需要手動建立 VirtualService 來路由接受的流量。這是使用 SNI 叢集完成的,當東西向閘道器的路由器模式設定為 sni-dnat 時,這些叢集會自動設定。

SNI 自動透傳模式使用 Istio Gateway 資源設定。在以下定義中,我們對所有 SNI 標頭比對表示式 *.local 的流量使用 SNI 自動透傳,這適用於所有 Kubernetes 服務。

驗證跨叢集工作負載發現

現在,由於 east-cluster 中的工作負載暴露給 west-cluster,我們期望 webapp 的 Envoy 叢集有一個指向 catalog 工作負載的端點。此端點應指向東西向閘道器的地址,該閘道器將請求代理到其網路中的 catalog 工作負載。

如果 catalog 資源的端點與東西向閘道器的地址比對,則工作負載被發現,跨叢集流量是可能的。考慮到代理設定,一切都設定正確。讓我們手動觸發請求,作為最終驗證。

太好了!我們看到當我們向入口閘道器觸發請求時,它被路由到 west-cluster 中的 webapp。然後它被解析到 east-cluster 中的 catalog 工作負載,最終服務了請求。這樣,我們已經驗證了多叢集、多網路、多控制平面服務網格已設定,工作負載跨叢集被發現;它們可以使用東西向閘道器作為透傳啟動相互認證的連線。

讓我們回顧一下設定多叢集服務網格所需的內容:

  1. 透過提供每個控制平面對等叢集的存取許可權,使用包含服務帳戶令牌和證書的 kubeconfig 進行跨叢集工作負載發現。這個過程由 istioctl 促進,我們只將其應用於對方叢集。
  2. 透過設定東西向閘道器來路由不同叢集(位於不同網路中)的工作負載之間的流量,並用網路訊息標記每個叢集,以便 Istio 知道工作負載所在的網路,實作跨叢集工作負載連線性。
  3. 透過使用發行對方叢集中間證書的共同信任根,在叢集之間設定信任。

這只是幾個步驟,它們大多是自動化的,用於設定多叢集服務網格。

跨叢集負載平衡

在第 6 章中,我們承諾探索跨叢集、位置感知的負載平衡。現在,有了多叢集服務網格,我們準備這樣做。為了演示這一點,我們將佈署兩個範例服務,每個服務都設定為回傳工作負載執行的叢集的名稱。因此,我們可以輕鬆確定服務請求的工作負載的位置。

預設情況下,Istio 使用輪詢演算法在工作負載之間進行負載平衡。因此,流量平均負載平衡。這很好!然而,使用位置感知的負載平衡可以進一步提高效能,使工作負載優先將流量路由到其位置內的工作負載。我們在前幾章中提到,雲提供商將位置訊息增加到節點中作為標籤。Istio 使用從標籤中檢索的這些訊息來設定工作負載的位置。

驗證跨叢集的位置感知路由

由於我們在 Azure 中建立了多叢集服務網格,節點被標記了來自雲提供商的位置訊息。

輸出顯示 west-cluster 中的節點被標記為 westus 區域。檢查 east-cluster 顯示 eastus 區域。這些訊息由 istiod 拾取並在設定端點時傳播到工作負載。

回想一下第 6 章,為了讓 Istio 使用位置訊息,需要被動健康檢查。讓我們應用一個使用離群檢測來被動檢查端點健康狀況的目標規則。

設定傳播後,通常需要幾秒鐘,我們可以驗證請求使用位置訊息並在同一叢集內路由。

正如預期的那樣,所有請求都在 west-cluster 內路由,這是最接近路由流量的入口閘道器的位置。由於所有路由決策都在 Envoy 代理中做出,我們可以得出結論,控制平面必須修改了其設定,這解釋了不同的行為。

驗證跨叢集容錯移轉

為了模擬 simple backend 佈署失敗,我們可以透過設定環境變數 ERROR_RATE 為 1 來設定它使請求失敗。讓我們為 west-cluster 中的工作負載這樣做。

一段時間後,離群檢測檢測到主機不健康,並將流量路由到 east-cluster 中的工作負載,這具有第二高的優先順序。

這顯示了跨叢集容錯移轉的作用:由於具有最高優先順序的工作負載未透過被動健康檢查,流量被路由到 east-cluster。

使用授權策略驗證跨叢集存取控制

我們將驗證的最後一個功能是跨叢集存取控制。回想一下,存取控制要求工作負載之間的流量相互認證,產生可靠的中繼資料,可用於決定是否接受或拒絕流量。為了演示這一點,讓我們提出一個場景。假設我們只想在其源是 Istio 的入口閘道器時接受對服務的流量;否則,流量被拒絕。

正如預期的那樣,請求被拒絕。同時,觸發對入口閘道器的請求並讓請求從閘道器路由會得到成功的回應。

我們可以看到,策略接受了源自入口閘道器的流量。這表明工作負載在跨叢集相互認證,策略可以使用編碼到身分證書中的認證資料進行存取控制。

我們所有關於負載平衡、位置感知路由、跨叢集容錯移轉、相互認證流量和存取控制的範例都表明,多叢集服務網格中的工作負載可以使用 Istio 的所有功能,無論它們在哪個叢集中執行。而與它們這樣做不需要任何額外的設定。

本文探討瞭如何在多叢集環境中擴充套件 Istio 服務網格,並分析了不同佈署模型的優缺點。我們瞭解到 Istio 支援三種多叢集服務網格佈署模型:單一控制平面(主-遠端)、複製控制平面(主-主)和外部控制平面。

透過在 istio-system 名稱空間中安裝中間證書,我們可以在叢集間建立共同信任。在複製控制平面佈署模型中,跨叢集工作負載使用服務帳戶作為遠端叢集中的身份進行發現,並將服務帳戶令牌作為秘密提供給對方叢集。

對於多網路服務網格,我們可以使用東西向閘道器來橋接網路。sni-dnat 路由器模式設定 SNI 叢集,以精細的方式路由跨叢集流量。東西向閘道器可以設定為自動透傳流量,並根據自動設定的 SNI 叢集進行路由。

最重要的是,Istio 的功能在跨叢集工作時與在單一叢集內工作的方式相同,這使得服務網格成為連線分散式系統的強大工具。透過這種方式,企業可以實作更高的可用性、更好的隔離性和更靈活的佈署策略,同時保持一致的服務網格功能。