漸進式 Istio 第一部分:流量管理

如何在不部署 Sidecar Proxy 的情況下使用 Istio 進行流量管理。

2018 年 11 月 21 日 | 作者:Sandeep Parikh

流量管理是 Istio 提供的關鍵優勢之一。Istio 流量管理的核心是能夠將流量流和基礎架構擴展解耦。這讓您可以以前所未有的方式控制您的流量,而無需像 Istio 這樣的服務網格。

例如,假設您想要執行金絲雀部署。使用 Istio,您可以指定服務的 v1 版本接收 90% 的傳入流量,而該服務的 v2 版本僅接收 10%。使用標準 Kubernetes 部署,實現這一目標的唯一方法是手動控制每個版本可用的 Pod 數量,例如 9 個 Pod 運行 v1 和 1 個 Pod 運行 v2。這種手動控制很難實施,並且隨著時間的推移可能會難以擴展。有關更多資訊,請查看使用 Istio 的金絲雀部署

在部署對現有服務的更新時,也存在同樣的問題。雖然您可以使用 Kubernetes 更新部署,但需要將 v1 Pod 替換為 v2 Pod。使用 Istio,您可以部署服務的 v2 版本,並使用內建的流量管理機制在網路層級將流量轉移到您更新的服務,然後移除 v1 Pod。

除了金絲雀部署和一般流量轉移之外,Istio 還讓您可以實作動態請求路由(基於 HTTP 標頭)、故障恢復、重試、斷路器和故障注入。有關更多資訊,請查看流量管理文件

這篇文章介紹了一種技術,該技術重點介紹了您可以漸進式實作 Istio 的一種特別有用的方式,在這種情況下,僅使用流量管理功能,而無需單獨更新每個 Pod。

設定:為什麼要實作 Istio 流量管理功能?

當然,第一個問題是:您為什麼要這樣做?

如果您是眾多擁有大型叢集且有多個團隊部署的組織之一,答案就很清楚了。假設 A 團隊開始使用 Istio,並希望在服務 A 上開始一些金絲雀部署,但 B 團隊尚未開始使用 Istio,因此他們沒有部署 sidecar。

使用 Istio,A 團隊仍然可以透過讓服務 B 透過 Istio 的入口閘道呼叫服務 A 來實作他們的金絲雀部署。

背景:Istio 網格中的流量路由

但是,如何在不更新每個應用程式的 Pod 以包含 Istio sidecar 的情況下使用 Istio 的流量管理功能?在回答這個問題之前,讓我們先快速高層次地了解流量如何進入 Istio 網格以及如何路由。

屬於 Istio 網格一部分的 Pod 包含一個 sidecar Proxy,負責調解 Pod 的所有傳入和傳出流量。在 Istio 網格中,Pilot 負責將高階路由規則轉換為配置,並將其傳播到 sidecar Proxy。這意味著當服務彼此通信時,它們的路由決策是從客戶端確定的。

假設您有兩個屬於 Istio 網格一部分的服務,服務 A 和服務 B。當 A 想要與 B 通信時,Pod A 的 sidecar Proxy 負責將流量導向服務 B。例如,如果您想要在服務 B 的 v1 和 v2 版本之間平均分配流量 50/50,則流量將如下所示:

50/50 Traffic Split
50/50 流量分配

如果服務 A 和 B 不屬於 Istio 網格的一部分,則沒有 sidecar Proxy 知道如何將流量路由到服務 B 的不同版本。在這種情況下,您需要使用另一種方法將流量從服務 A 傳輸到服務 B,遵循您設定的 50/50 規則。

幸運的是,標準的 Istio 部署已經包含一個閘道,專門處理 Istio 網格外部的入口流量。此閘道用於允許從叢集外部透過外部負載平衡器進入的入口流量,或允許從 Kubernetes 叢集內部但服務網格外部進入的入口流量。它可以配置為將傳入的入口流量代理到適當的 Pod,即使它們沒有 sidecar Proxy。雖然這種方法允許您利用 Istio 的流量管理功能,但這確實意味著通過入口閘道的流量將產生額外的跳轉。

50/50 Traffic Split using Ingress Gateway
使用入口閘道的 50/50 流量分配

實際操作:使用 Istio 的流量路由

在實際操作中查看此類型方法的一種簡單方法是,首先使用平台設定說明設定您的 Kubernetes 環境,然後使用Helm安裝最簡 Istio 設定檔,僅包含流量管理元件(入口閘道、出口閘道、Pilot)。以下範例使用Google Kubernetes Engine

首先,設定並配置GKE

$ gcloud container clusters create istio-inc --zone us-central1-f
$ gcloud container clusters get-credentials istio-inc
$ kubectl create clusterrolebinding cluster-admin-binding \
   --clusterrole=cluster-admin \
   --user=$(gcloud config get-value core/account)

接下來,安裝 Helm產生最小的 Istio 安裝 - 僅限流量管理元件

$ helm template install/kubernetes/helm/istio \
  --name istio \
  --namespace istio-system \
  --set security.enabled=false \
  --set galley.enabled=false \
  --set sidecarInjectorWebhook.enabled=false \
  --set mixer.enabled=false \
  --set prometheus.enabled=false \
  --set pilot.sidecar=false > istio-minimal.yaml

然後建立 istio-system 名稱空間並部署 Istio

$ kubectl create namespace istio-system
$ kubectl apply -f istio-minimal.yaml

接下來,部署 Bookinfo 範例,但不包括 Istio sidecar 容器

壓縮
$ kubectl apply -f @samples/bookinfo/platform/kube/bookinfo.yaml@

現在,設定一個新的閘道,允許從 Istio 網格外部存取 reviews 服務,一個新的 VirtualService,在 reviews 服務的 v1 和 v2 版本之間均分流量,以及一組新的 DestinationRule 資源,將目標子集與服務版本相匹配

$ cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: reviews-gateway
spec:
  selector:
    istio: ingressgateway # use istio default controller
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "*"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
  - "*"
  gateways:
  - reviews-gateway
  http:
  - match:
    - uri:
        prefix: /reviews
    route:
    - destination:
        host: reviews
        subset: v1
      weight: 50
    - destination:
        host: reviews
        subset: v2
      weight: 50
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: reviews
spec:
  host: reviews
  subsets:
  - name: v1
    labels:
      version: v1
  - name: v2
    labels:
      version: v2
  - name: v3
    labels:
      version: v3
EOF

最後,部署一個可用於使用 curl 進行測試的 Pod(不含 Istio sidecar 容器)

壓縮
$ kubectl apply -f @samples/sleep/sleep.yaml@

測試您的部署

現在,您可以使用透過 sleep Pod 的 curl 命令測試不同的行為。

第一個範例是使用標準 Kubernetes 服務 DNS 行為向 reviews 服務發出請求(請注意jq 用於以下範例中過濾 curl 的輸出)

$ export SLEEP_POD=$(kubectl get pod -l app=sleep \
  -o jsonpath={.items..metadata.name})
$ for i in `seq 3`; do \
  kubectl exec -it $SLEEP_POD curl http://reviews:9080/reviews/0 | \
  jq '.reviews|.[]|.rating?'; \
  done
{
  "stars": 5,
  "color": "black"
}
{
  "stars": 4,
  "color": "black"
}
null
null
{
  "stars": 5,
  "color": "red"
}
{
  "stars": 4,
  "color": "red"
}

請注意,我們如何從 reviews 服務的所有三個版本獲得回應(null 來自沒有評分的 reviews v1),而不是在 v1 和 v2 之間獲得均等分配。這是預期的行為,因為 curl 命令正在所有三個版本的 reviews 服務中使用 Kubernetes 服務負載平衡。為了存取 reviews 50/50 分配,我們需要透過入口閘道存取服務

$ for i in `seq 4`; do \
  kubectl exec -it $SLEEP_POD curl http://istio-ingressgateway.istio-system/reviews/0 | \
  jq '.reviews|.[]|.rating?'; \
  done
{
  "stars": 5,
  "color": "black"
}
{
  "stars": 4,
  "color": "black"
}
null
null
{
  "stars": 5,
  "color": "black"
}
{
  "stars": 4,
  "color": "black"
}
null
null

任務完成!這篇文章展示了如何部署僅包含流量管理元件(Pilot、入口閘道)的最小 Istio 安裝,然後使用這些元件將流量導向 reviews 服務的特定版本。並且沒有必要部署 Istio sidecar Proxy 來獲得這些功能,因此現有工作負載或應用程式幾乎沒有任何中斷。

這篇文章展示了如何使用內建的入口閘道(以及一些 VirtualServiceDestinationRule 資源),您可以輕鬆地利用 Istio 的流量管理來處理叢集外部的入口流量和叢集內部的服務對服務流量。這種技術是採用 Istio 的漸進式方法的一個很好的範例,並且在 Pod 由不同團隊擁有或部署到不同命名空間的實際情況中尤其有用。

分享這篇文章