Istio 提供了強大的流量管理能力,允許開發者精細地控制服務間的流量走向。透過 DestinationRule,我們可以定義服務的不同版本子集,並利用 VirtualService 設定流量路由規則。這使得實作藍綠佈署和金絲雀發布等策略變得更加簡潔和高效。在實際操作中,可以根據需求逐步調整流量權重,逐步將流量從舊版本服務遷移到新版本,確保服務升級過程的平滑過渡。此外,Istio 還支援根據 HTTP 標頭的流量匹配,可以根據使用者代理、請求來源等資訊進行流量分割,實作更精細的流量控制。
服務佈署與流量管理:使用 Istio 實作無縫升級
在現代微服務架構中,服務的無縫升級和流量管理至關重要。Istio 作為一個領先的服務網格平台,提供了一整套解決方案來實作這一點。本文將探討如何使用 Istio 的 DestinationRule 和 VirtualService 來控制服務流量,實作藍綠佈署和金絲雀發布。
步驟一:定義 DestinationRule
首先,我們需要定義一個 DestinationRule 來區分不同版本的服務。這裡我們以 greeter-service 為例,定義了兩個子集(subset):v1 和 v2。
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: greeter-service
spec:
host: greeter-service.default.svc.cluster.local
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
trafficPolicy:
tls:
mode: ISTIO_MUTUAL
內容解密:
DestinationRule用於定義服務的子集和流量策略。subsets欄位用於區分不同版本的服務,這裡定義了v1和v2。trafficPolicy.tls.mode設定為ISTIO_MUTUAL,表示使用 Istio 的雙向 TLS。
步驟二:組態 VirtualService
接下來,我們需要組態 VirtualService 來控制流量路由。最初,我們將所有流量路由到 v1 版本。
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: greeter-service
spec:
hosts:
- greeter-service.default.svc.cluster.local
http:
- route:
- destination:
host: greeter-service.default.svc.cluster.local
port:
number: 3000
subset: v1
weight: 100
- destination:
host: greeter-service.default.svc.cluster.local
port:
number: 3000
subset: v2
weight: 0
內容解密:
VirtualService用於定義流量的路由規則。http.route欄位定義了流量的路由,初始設定為 100% 流向v1版本。
步驟三:佈署新版本服務
佈署 v2 版本的 greeter-service。
apiVersion: apps/v1
kind: Deployment
metadata:
name: greeter-service-v2
labels:
app: greeter-service
version: v2
spec:
replicas: 3
selector:
matchLabels:
app: greeter-service
version: v2
template:
metadata:
labels:
app: greeter-service
version: v2
spec:
containers:
- name: svc
image: learnistio/greeter-service:2.0.0
imagePullPolicy: Always
ports:
- containerPort: 3000
內容解密:
- 這裡佈署了
v2版本的服務,並設定了三個副本。 - 使用
label和selector確保服務版本的一致性。
步驟四:調整流量比例
將部分流量路由到 v2 版本,例如將 10% 的流量路由到 v2。
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: greeter-service
spec:
hosts:
- greeter-service.default.svc.cluster.local
http:
- route:
- destination:
host: greeter-service.default.svc.cluster.local
port:
number: 3000
subset: v1
weight: 90
- destination:
host: greeter-service.default.svc.cluster.local
port:
number: 3000
subset: v2
weight: 10
內容解密:
- 將
v2版本的流量權重設為 10%,其餘流量仍流向v1版本。 - 可以根據監控結果逐步調整權重。
進階流量分割技術
有時候,僅僅根據權重來分割請求可能不夠幸運的是,Istio 支援更進階的請求匹配方式。您可以根據 URL、標頭(headers)或方法(method)來匹配請求,然後再決定將請求路由到哪裡。
例如,您正在兩個版本之間分割流量,但除此之外,您只希望來自 Firefox 瀏覽器的流量導向到 v2 版本,而來自其他瀏覽器的請求則導向到 v1 版本。或者,您可以將所有 GET 請求路由到一個版本,將其他請求路由到另一個版本。如果將此功能與重寫 URL 或執行 HTTP 重新導向的能力相結合,您就可以涵蓋很多不同的場景。
在本文中,我們將展示如何使用 Istio 匹配請求以及如何重新導向和重寫它們。所有這些都將定義為 VirtualService 資源的一部分。
請求匹配
Istio 允許您使用傳入請求的某些部分並將其與您定義的值進行匹配。如果值匹配,則請求將被路由到虛擬服務中指定的目的地。下表顯示了可以與使用者提供的值匹配的所有請求屬性,以及它們可以被比較的不同方式:
請求匹配屬性
| 屬性 | 描述 |
|---|---|
| uri | 將請求 URI 與指定值匹配 |
| schema | 將請求模式(HTTP、HTTPS 等)與指定值匹配 |
| method | 將請求方法(GET、POST 等)與指定值匹配 |
| authority | 將請求授權標頭與指定值匹配 |
| headers | 將請求標頭與指定值匹配。標頭需要以小寫字母提供,並以連字號分隔(例如 x-my-request-id)。注意:如果使用標頭進行匹配,則其他屬性(uri、schema、method、authority)將被忽略 |
請求匹配型別
| 匹配型別 | 描述 |
|---|---|
| exact | 屬性需要與提供的值完全匹配。例如:exact: Hello 只會在屬性值為 Hello 時匹配,而不是 HELLO 或 hello |
| prefix | 只會匹配屬性的字首。例如:prefix: Hello 將匹配 HelloWorld、Hello |
| regex | 值將根據正規表示式進行匹配。例如:regex: ‘.Firefox.’ 將匹配 Hello Firefox World |
除了匹配屬性外,您還可以定義 sourceLabels 以進一步限制規則僅適用於具有指定標籤的服務(例如,我們可以指定 app: myapp 以僅將匹配應用於來自具有這些標籤的服務的請求)。
實際範例
讓我們看看匹配如何在實踐中運作。我們將使用相同的 Hello Web 和 Greeter 服務的兩個版本。
首先,我們佈署了一個更新的虛擬服務,該服務將來自 Firefox 瀏覽器的所有請求路由到 greeter 服務的 v2 版本。
cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: greeter-service
spec:
hosts:
- greeter-service.default.svc.cluster.local
http:
- match:
- headers:
user-agent:
regex: '.*Firefox.*'
route:
- destination:
host: greeter-service.default.svc.cluster.local
port:
number: 3000
subset: v2
- route:
- destination:
host: greeter-service.default.svc.cluster.local
port:
number: 3000
subset: v1
EOF
程式碼解析:
http:
- match:
- headers:
user-agent:
regex: '.*Firefox.*'
這段程式碼定義了一個 HTTP 匹配規則,該規則檢查 user-agent 標頭是否包含 “Firefox” 字串。如果匹配,則將請求路由到 v2 子集。
route:
- destination:
host: greeter-service.default.svc.cluster.local
port:
number: 3000
subset: v2
這段程式碼將請求路由到 greeter-service 的 v2 子集,該子集對應於 version: v2 標籤。
- route:
- destination:
host: greeter-service.default.svc.cluster.local
port:
number: 3000
subset: v1
如果 user-agent 不包含 “Firefox” 字串,則將請求路由到 v1 子集。
要測試此功能,您可以在兩個不同的瀏覽器中開啟閘道器 URL:Firefox 和另一個瀏覽器。在非 Firefox 瀏覽器中,您將只看到來自 v1 版本的回應,而在 Firefox 瀏覽器中,回應將來自 v2 版本的服務。
重定向和重寫
除了根據標頭進行匹配外,您還可以根據請求 URI 中的值進行匹配。
目前,greeter 服務使用 /hello URI 傳回訊息,但我們希望將其更改為 /greeting。這樣,任何請求 /greeting 端點的服務或使用者都將獲得與請求 /hello 端點相同的回應。
一種方法是建立 greeter 服務原始碼中的第二個端點,然後維護兩個端點。但是,這聽起來不太實際,尤其是因為使用服務網格有更自然的方法來做到這一點。
根據您想要做的事情——HTTP 重寫或 HTTP 重定向,Istio 都能滿足您的需求。以下是如何進行 HTTP 重寫的示例,將所有具有 /greeting URI 的請求重寫為 /hello 端點,並將其他請求路由到 v1 子集:
cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: greeter-service
spec:
hosts:
- greeter-service.default.svc.cluster.local
http:
- match:
- uri:
prefix: /greeting
rewrite:
uri: /hello
route:
- destination:
host: greeter-service.default.svc.cluster.local
port:
number: 3000
subset: v2
- route:
- destination:
host: greeter-service.default.svc.cluster.local
port:
number: 3000
subset: v1
EOF
程式碼解析:
- match:
- uri:
prefix: /greeting
這段程式碼定義了一個 HTTP 匹配規則,該規則檢查 URI 是否以 /greeting 開頭。如果匹配,則執行重寫操作。
rewrite:
uri: /hello
這段程式碼將 URI 重寫為 /hello。
route:
- destination:
host: greeter-service.default.svc.cluster.local
port:
number: 3000
subset: v2
這段程式碼將請求路由到 greeter-service 的 v2 子集。
透過這種方式,您可以根據不同的需求對流量進行分割和路由,從而實作更靈活的流量管理。
Istio 流量管理的進階應用:重寫、重新導向與流量映象
在微服務架構中,流量管理是確保服務穩定性和可靠性的關鍵環節。Istio 作為一個強大的服務網格平台,提供了豐富的流量管理功能,包括流量路由、重寫、重新導向和流量映象等。本文將探討這些進階應用,並透過具體範例和程式碼說明如何在實際環境中實施。
URI 重寫與重新導向
在某些情況下,我們需要修改客戶端的請求 URI 或將請求重新導向到不同的服務。Istio 的 VirtualService 資源提供了 rewrite 和 redirect 功能來實作這一點。
URI 重寫
以下是一個使用 rewrite 功能的範例,將 /greeting 字首的請求重寫為 /hello:
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: greeter-service
spec:
hosts:
- greeter-service.default.svc.cluster.local
http:
- match:
- uri:
prefix: /greeting
rewrite:
uri: /hello
route:
- destination:
host: greeter-service.default.svc.cluster.local
port:
number: 3000
subset: v2
- route:
- destination:
host: greeter-service.default.svc.cluster.local
port:
number: 3000
subset: v1
URI 重新導向
同樣地,我們可以使用 redirect 功能將請求重新導向到不同的 URI 或服務:
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: greeter-service
spec:
hosts:
- greeter-service.default.svc.cluster.local
http:
- match:
- uri:
prefix: /greeting
redirect:
uri: /hello
authority: greeter-service.default.svc.cluster.local:3000
- route:
- destination:
host: greeter-service.default.svc.cluster.local
port:
number: 3000
多重匹配條件
我們可以定義多個匹配條件來滿足不同的路由需求。例如,根據 URI 和特定的 Header 值進行匹配:
http:
- match:
- uri:
prefix: /greeting
headers:
x-my-header:
exact: something
redirect:
uri: /hello
authority: greeter-service.default.svc.cluster.local:3000
或者新增多個匹配條目:
http:
- match:
- uri:
prefix: /greeting
redirect:
uri: /hello
authority: greeter-service.default.svc.cluster.local:3000
- match:
- uri:
prefix: /other-path
redirect:
uri: /other-redirected-path
authority: other-service.default.svc.cluster.local:3000
流量映象(Mirroring)
流量映象是 Istio 中一個非常有用的功能,它允許我們將生產環境中的流量複製一份並傳送到另一個服務上,而不會影響原始請求的處理。這對於測試新版本的服務或收集生產環境下的真實流量資料非常有幫助。
組態流量映象
以下是一個組態流量映象的範例,將所有發往 greeter-service v1 版本的流量映象到 v2 版本:
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: greeter-service
spec:
hosts:
- greeter-service.default.svc.cluster.local
http:
- route:
- destination:
host: greeter-service.default.svc.cluster.local
port:
number: 3000
subset: v1
weight: 100
mirror:
host: greeter-service.default.svc.cluster.local
port:
number: 3000
subset: v2
mirror_percent: 100
在這個範例中,mirror_percent 設定為 100,表示將 100% 的流量映象到 v2 版本。