返回文章列表

Istio流量管理實戰

本文探討 Istio 流量管理機制,涵蓋 Gateway、VirtualService 和 ServiceEntry 的組態與應用,並以電影網站範例示範如何管理外部服務存取與多版本服務佈署。文章解析 Istio 如何透過權重分配實作流量分割,逐步釋放新版本服務至生產環境,降低風險並確保系統穩定性。

Web 開發 系統設計

Istio 提供強大的流量管理能力,讓開發者得以精細控制服務間的通訊。本文從設定 Gateway 開始,逐步引導讀者理解 Istio 的核心概念。透過建立 Gateway 資源,我們可以將外部流量匯入 Kubernetes 叢集。然而,單純的 Gateway 並不足以處理複雜的路由需求,因此需要搭配 VirtualService 進行更精確的流量導向。VirtualService 允許開發者定義服務的存取方式和路由規則,例如根據主機名稱或路徑將流量導向不同的服務版本。此外,ServiceEntry 則能將外部服務納入 Istio 的管理範圍,讓開發者得以應用流量管理、故障注入等功能於外部服務。

使用 Istio Gateway 管理流量

在設定好 GATEWAY 變數後,您可以嘗試存取它,但連線將被拒絕:

$ curl -v $GATEWAY
* Trying 10.99.132.130...
* TCP_NODELAY set
* Connection failed
* connect to 10.99.132.130 port 80 failed: Connection refused
* Failed to connect to 10.99.132.130 port 80: Connection refused
* Closing connection 0
curl: (7) Failed to connect to 10.99.132.130 port 80: Connection refused

這是因為 ingress gateway 需要一個 Gateway 資源來知道如何路由請求。Ingress 和 Gateway 資源在服務網格的邊緣運作,用於啟用進入叢集的流量。

建立 Istio Gateway 資源

以下是一個最小的 Istio Gateway 資源範例:

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

這個範例建立了一個 Istio Gateway 資源,選擇器為 istio: ingressgateway。在 Istio 安裝過程中,會建立一個帶有 istio=ingressgateway 標籤的 istio-ingressgateway pod,而具有匹配選擇器 istio=ingressgateway 的 Gateway 資源會將路由組態新增到 istio-ingressgateway pod 中。

程式碼解密:

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: gateway
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - '*'
  • apiVersionkind 定義了資源的 API 版本和型別。
  • metadata 部分定義了資源的名稱。
  • spec 部分定義了資源的規格,包括選擇器和伺服器組態。
  • selector 指定了 Gateway 資源要關聯的 ingress gateway pod。
  • servers 部分定義了要代理的請求的主機和埠。

使用真實網域名稱

在真實世界中,hosts 欄位會被設定為實際的網域名稱(例如 www.example.com),叢集服務將透過該網域名稱被存取。* 只應在測試和本地場景中使用,而不應在生產環境中使用。

佈署 Gateway 資源

讓我們將自己的 Gateway 資源佈署到預設名稱空間:

cat <<EOF | kubectl create -f -
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: gateway
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - '*'
EOF

處理請求

現在,讓我們嘗試向 $GATEWAY 端點傳送請求:

$ curl -v $GATEWAY
* Trying 10.99.132.130...
* TCP_NODELAY set
* Connected to 10.99.132.130 (10.99.132.130) port 80 (#0)
> GET / HTTP/1.1
> Host: 10.99.132.130
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 404 Not Found
< date: Fri, 09 Oct 2020 21:50:08 GMT
< server: istio-envoy
< content-length: 0
<
* Connection #0 to host 10.99.132.130 left intact
* Closing connection 0

這次,我們收到了 HTTP 404 回應。請求到達了 ingress gateway,但 gateway 不知道如何路由請求。

檢視 Istio Ingress Gateway Pod 日誌

讓我們檢視 Istio ingress gateway pod 的日誌,以檢視傳入的請求:

$ kubectl get pod --selector="istio=ingressgateway" --all-namespaces
NAMESPACE   NAME                                    READY   STATUS    RESTARTS   AGE
istio-system   istio-ingressgateway-7bd5586b79-nzbm8   1/1     Running   0          11m

$ kubectl logs istio-ingressgateway-7bd5586b79-nzbm8 -n istio-system
...
[2020-10-09T21:50:08.971Z] "GET / HTTP/1.1" 404 - "-" "-" 0 0 0 - "192.168.99.1" "curl/7.64.1" "697452d4-08c9-9370-a3c2-2cbca13b34c5" "10.99.132.130" "-" - - 172.17.0.5:8080 192.168.99.1:58015 - default
....

這告訴我們,傳入的流量確實到達了 ingress gateway。

Virtual Services

由於我們將在書中大量討論服務,讓我們先對術語做一個快速說明。單詞“service”有多個不同的含義,取決於上下文。在本文中,當提到 Kubernetes Service 資源時,我將使用“Kubernetes service”,而當提到 Istio Virtual Service 資源時,我將使用“Virtual service”。

Istio 的 Virtual Service 資源用於組態請求在網格內的路由方式。Virtual Service 是本文中將大量使用的資源之一。

Virtual Service 範例

以下是一個 Istio Virtual Service 資源範例:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: helloweb
spec:
  hosts:
  - 'helloweb.dev'
  gateways:
  - gateway
  http:
  - route:
    - destination:
        host: helloweb.default.svc.cluster.local
        port:
          number: 3000

在 Virtual Service 中,hosts 是客戶端嘗試連線到服務時使用的地址。在我們的例子中,我們將使用 helloweb.dev 作為主機。使用實際的主機名而不是星號(*)將允許我們同時佈署和存取多個服務透過 gateway。

程式碼解密:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: helloweb
spec:
  hosts:
  - 'helloweb.dev'
  gateways:
  - gateway
  http:
  - route:
    - destination:
        host: helloweb.default.svc.cluster.local
        port:
          number: 3000
  • hosts 定義了客戶端存取服務時使用的主機名。
  • gateways 指定了允許流量透過的 Gateway 資源名稱。
  • http 部分定義了 HTTP 請求的路由規則。
  • route 部分指定了請求的目的地,包括主機和埠。

Istio 流量管理深度解析

在 Kubernetes 環境中,Istio 提供了一套完整的流量管理機制,讓開發者能夠精確控制服務間的通訊。流量管理的核心在於如何有效地引導和管理服務間的流量,以確保系統的穩定性和可擴充套件性。

虛擬服務(VirtualService)組態詳解

虛擬服務是 Istio 流量管理的關鍵資源之一,它允許開發者定義服務的存取方式以及流量的路由規則。虛擬服務的主要功能包括:

  • 定義服務的主機名稱和閘道器
  • 組態 HTTP 流量的路由規則
  • 支援多個目的地(destination)的設定

虛擬服務範例

以下是一個基本的虛擬服務組態範例,它將流量路由到 helloweb.default.svc.cluster.local 服務的 3000 連線埠:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: helloweb
spec:
  hosts:
  - 'helloweb.dev'
  gateways:
  - gateway
  http:
  - route:
    - destination:
        host: helloweb.default.svc.cluster.local
        port:
          number: 3000

#### 內容解密:

  • hosts 欄位指定了虛擬服務的主機名稱,在此例中為 helloweb.dev
  • gateways 欄位指定了虛擬服務所使用的閘道器,在此例中為 gateway
  • http.route.destination 欄位定義了流量的路由規則,將流量導向 helloweb.default.svc.cluster.local 服務的 3000 連線埠。

服務條目(ServiceEntry)組態詳解

服務條目允許開發者將外部服務納入 Istio 的服務網格中,從而能夠對外部服務應用流量管理、故障注入等功能。

服務條目範例

以下是一個服務條目的組態範例,它將 api.themoviedb.org 納入服務網格:

apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
  name: themoviedb-api
spec:
  hosts:
  - api.themoviedb.org
  ports:
  - number: 443
    name: https
    protocol: HTTPS
  resolution: DNS
  location: MESH_EXTERNAL

#### 內容解密:

  • hosts 欄位指定了外部服務的主機名稱,在此例中為 api.themoviedb.org
  • ports 欄位定義了外部服務的連線埠和協定,在此例中使用 HTTPS 協定連線至 443 連線埠。
  • resolution 欄位指定了解析外部服務 IP 的方式,在此例中使用 DNS 解析。
  • location 欄位指定了外部服務的位置,在此例中為 MESH_EXTERNAL,表示該服務位於服務網格之外。

電影網站範例佈署與驗證

本文將透過佈署一個電影網站範例來說明如何使用 Istio 管理外部服務的存取。首先,需要在 themoviedb.org 取得 API 金鑰。然後,使用以下 YAML 檔案佈署電影網站:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: movieweb
spec:
  replicas: 3
  selector:
    matchLabels:
      app: movieweb
  template:
    metadata:
      labels:
        app: movieweb
    spec:
      containers:
      - name: movieweb
        image: learnistio/movie-web:1.0.0
        env:
        - name: THEMOVIEDB_API_KEY
          value: '<API_KEY_HERE>'
        ports:
        - containerPort: 3000
---
apiVersion: v1
kind: Service
metadata:
  name: movieweb
spec:
  selector:
    app: movieweb
  ports:
  - port: 3000
    name: http

#### 內容解密:

  • 此 YAML 檔案定義了一個名為 movieweb 的 Deployment 和 Service。
  • THEMOVIEDB_API_KEY 環境變數需替換為實際的 API 金鑰。

接著,佈署虛擬服務和服務條目以組態流量路由和外部服務存取:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: movieweb
spec:
  hosts:
  - '*'
  gateways:
  - gateway
  http:
  - route:
    - destination:
        host: movieweb.default.svc.cluster.local
        port:
          number: 3000
---
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
  name: themoviedb-api
spec:
  hosts:
  - api.themoviedb.org
  ports:
  - number: 443
    name: https
    protocol: HTTPS
  resolution: DNS
  location: MESH_EXTERNAL

#### 內容解密:

  • 虛擬服務組態將所有到達 gateway 的流量路由到 movieweb Service。
  • 服務條目組態允許 movieweb Service 存取外部的 api.themoviedb.org

完成佈署後,可以透過瀏覽器或 curl 命令存取電影網站,驗證 Istio 的流量管理和外部服務存取功能。

服務流量管理:基礎流量分割技術解析

在前面的章節中,我們主要關注單一版本的服務佈署,並介紹瞭如何允許外部請求進入叢集,以及如何使用服務入口資源讓服務網格能夠存取外部服務和API。然而,在大多數實際案例中,叢集內通常會有多個服務同時執行並相互通訊。這些服務可能會以不同的速度進行演進,導致出現多個不同版本的服務。在這種情況下,如何安全地發布新版本的服務,並確保其與現有版本具有相同的穩定性和功能性,成為了一個重要的課題。

流量分割技術的應用場景

流量分割技術可以幫助我們解決上述問題。本文將示範如何佈署新版本的服務,並透過逐步釋放新版本到生產環境的過程,同時將風險降至最低。

為了示範流量分割,我們將使用前面介紹的 Hello Web 和 Greeter 服務的兩個不同版本。

透過權重進行流量分割

Istio允許我們透過為每個服務版本分配權重來路由請求。這些權重代表了應該被路由到特定服務版本的輸入請求的百分比。

目前,服務之間的流量流動如圖3.4所示。流量從網際網路進入負載平衡器和閘道器,然後到達helloweb Kubernetes服務。helloweb Kubernetes服務再將流量負載平衡到所有標有app: helloweb標籤的Pod上。在Hello web Pod內,我們透過DNS名稱(http://greeter-service.default.svc.cluster.local:3000)呼叫Greeter服務。這個DNS名稱是在Kubernetes服務佈署時自動建立的。然後,Kubernetes服務會將流量負載平衡到所有標有app: greeter-service標籤的Pod上。

佈署多版本服務

要佈署第二個版本的Greeter服務,我們需要:

  1. 建立一個全新的Kubernetes佈署,並為其設定version: v2標籤,以及app: greeter-service標籤。這個佈署使用了一個不同的Docker映像,其中包含了v2版本的服務。

  2. 由於我們已經佈署了Kubernetes的greeter-service,因此不需要再佈署另一個Kubernetes服務。我們也不應該重新命名服務或佈署不同版本的服務,因為Hello Web依賴於DNS名稱(例如http://greeter-service:3000)來呼叫它。

greeter-service Kubernetes服務的選擇器定義如下:

selector:
  app: greeter-service

這意味著它並不知道greeter服務的版本資訊。如果我們佈署了v2版本的greeter服務並重新載入hello web,我們將會隨機收到來自v1或v2 Pod的回應。

使用Istio進行流量控制

為瞭解決上述問題,我們需要定義多個目的地,以便將請求路由到不同的服務版本。這正是Istio的VirtualService資源可以做到的。

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: greeter-service
spec:
  hosts:
  - greeter-service
  http:
  - route:
    - destination:
        host: greeter-service.default.svc.cluster.local
        port:
          number: 3000
    - destination:
        host: greeter-service.default.svc.cluster.local
        port:
          number: 3000

然而,這樣做還不夠,因為我們沒有區分不同的服務版本。為此,我們需要使用Istio的DestinationRule資源。透過這個資源和子集的概念,我們可以根據Kubernetes Pod上的標籤來區分不同的服務版本。

以下是greeter服務的DestinationRule範例:

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

內容解密:

  1. apiVersion和kind:指定了Istio的API版本和資源型別(在本例中為DestinationRule)。
  2. metadata:提供了資源的後設資料,如名稱。
  3. spec:定義了資源的具體組態。
    • host:指定了目標服務的主機名。
    • subsets:定義了服務的不同子集,每個子集根據特定的標籤進行區分。
      • name:子集的名稱。
      • labels:用於區分不同子集的標籤。

透過結合VirtualService和DestinationRule,我們可以實作對不同版本服務的精細流量控制,從而安全地發布新版本的服務。