使用 Istio 多叢集功能在叢集之間「突發」工作負載

使用 Istio 多叢集功能在叢集之間「突發」工作負載

程式碼研究室簡介

subject上次更新時間:3月 30, 2021
account_circle作者:Google 員工

1. 歡迎使用

感謝您參與 Google 推出的 Istio Multi Cloud Burst 程式碼研究室。這個程式碼研究室需要初學者級別的 Kubernetes、Node 和 Go 實作經驗。

事前準備

  • Google Cloud Platform 帳戶 (使用現有帳戶,我們會提供免費帳戶)
  • 筆記型電腦 (安裝「kubectl」、「gcloud」等) 或 Google Cloud Shell。

學習目標

  • 如何在 GKE 上建立 Kubernetes 叢集
  • 如何使用 Helm 在 Kubernetes 叢集上安裝 Istio
  • 如何使用 Helm 安裝 Istio 多叢集
  • 從來源部署網路應用程式至 Kubernetes
  • 為 Istio 編寫及套用流量轉送規則
  • Prometheus 指標
  • 在 Kubernetes 叢集中建構及推送容器映像檔

2. 開始設定

您可以透過下列任一方式,按照本程式碼研究室的操作說明進行操作:

  • Google Cloud Shell (建議):瀏覽器內的殼層,內建安裝工具
  • 筆記型電腦 (請按照下方說明操作)

開始使用 Google Cloud Platform

  1. 如果您沒有 GCP 帳戶,請向講師索取免費使用者帳戶卡。
  2. 前往 Google Cloud 控制台,然後按一下「Select a project」(選取專案):5c2d9bf74c78f7e4.png
  3. 記下專案的「ID」,然後點選專案進行選擇:ecc5e8e97bfa6559.png

Cloud Shell 會在瀏覽器中提供指令列殼層,並自動安裝所需工具,並驗證 Google Cloud Platform 帳戶。(如果您不想在 Cloud Shell 中執行這項練習,請跳至下一節)。

前往 Cloud 控制台,然後按一下右上方工具列中的「啟用 Cloud Shell」:

68a17b036ce24ccb.png

在 Cloud Shell 中新增工具

  1. 安裝 kubectx****:這裡下載 bash 指令碼,並將其放在 $PATH 中的某個位置。
  2. 安裝 helm****:請按照這些操作說明操作。

您也可以執行下列指令,將兩者都安裝至 ~/.bin,並將其新增至 $PATH:

mkdir -p ~/.bin && \
cd ~/.bin && \
curl -LO https://raw.githubusercontent.com/ahmetb/kubectx/master/kubectx && \
chmod +x kubectx && \
curl -LO https://raw.githubusercontent.com/ahmetb/kubectx/master/kubens && \
chmod +x kubens && \
curl -LO  https://storage.googleapis.com/kubernetes-helm/helm-v2.12.0-linux-amd64.tar.gz && \
tar xzf helm-v2.12.0-linux-amd64.tar.gz && \
rm helm-v2.12.0-linux-amd64.tar.gz && \
mv linux-amd64/helm ./helm && \
rm -r linux-amd64 && \
export PATH=${HOME}/.bin:${PATH}

以下是一些快速提示,可讓您更輕鬆地使用 Cloud Shell:

1. 將殼層分離至新視窗:

2. 使用檔案編輯器: 按一下右上方的鉛筆圖示,啟動瀏覽器內的檔案編輯器。這會很實用,因為我們會將程式碼片段複製到檔案中。

3. 啟動新的分頁:如果您需要多個終端機提示。

4. 放大文字: Cloud Shell 的預設字型大小可能太小,無法清楚辨識。

Linux 上的 Ctrl-+ 或 Windows 上的 ⌘-+。

如果您比較習慣使用自己的工作站環境,而非 Cloud Shell,請設定下列工具:

  1. 安裝gcloud: (Cloud Shell 已預先安裝)。請按照操作說明在您的平台上安裝 gcloud。我們會使用這個值建立 Kubernetes 叢集。
  2. 安裝kubectl:(Cloud Shell 已預先安裝)。執行下列指令安裝:
gcloud components install kubectl

執行下列指令,即可驗證 gcloud。系統會要求您使用 Google 帳戶登入。接著,請選擇預先建立的專案 (如上圖所示) 做為預設專案。(您可以略過設定運算區域的步驟):

gcloud init
  1. 安裝curl: 已預先安裝在大多數 Linux/macOS 系統中。你可能已經有這個功能。否則,請在網路上搜尋如何安裝該應用程式。
  2. 安裝 kubectx****:這裡下載 bash 指令碼,並將其放在 $PATH 中的某個位置
  3. 安裝 helm****:請按照這些操作說明操作。

3. 設定 GCP 專案

在專案中啟用 GKE (Google Kubernetes Engine)、GCR (Google Container Registry) 和 GCB (Google Cloud Build) API:

gcloud services enable \
  cloudapis.googleapis.com \
  container.googleapis.com \
  containerregistry.googleapis.com \
  cloudbuild.googleapis.com

設定環境變數

我們會在設定期間大量使用 Google Cloud 專案,因此請設定環境變數,方便快速參照

export GCLOUD_PROJECT=$(gcloud config get-value project)

我們會在工作坊中建立一些程式碼和設定檔,因此請建立專案目錄並切換至該目錄

mkdir -p src/istio-burst && \
cd src/istio-burst && \
export proj=$(pwd)

4. 建立「primary」Kubernetes 叢集

您可以使用 Google Kubernetes Engine (GKE) 輕鬆建立代管 Kubernetes 叢集。

下列指令會建立 Kubernetes 叢集:

  • 命名為「primary」
  • 在 us-west1-a 區域中,
  • 最新的 Kubernetes 版本
  • 包含 4 個初始節點
export cluster=primary
export zone=us-west1-a
gcloud container clusters create $cluster --zone $zone --username "admin" \
--cluster-version latest --machine-type "n1-standard-2" \
--image-type "COS" --disk-size "100" \
--scopes "https://www.googleapis.com/auth/compute",\
"https://www.googleapis.com/auth/devstorage.read_only",\
"https://www.googleapis.com/auth/logging.write",\
"https://www.googleapis.com/auth/monitoring",\
"https://www.googleapis.com/auth/servicecontrol",\
"https://www.googleapis.com/auth/service.management.readonly",\
"https://www.googleapis.com/auth/trace.append" \
--num-nodes "4" --network "default" \
--enable-cloud-logging --enable-cloud-monitoring --enable-ip-alias

(這項作業可能需要 5 分鐘的時間。您可以在 Cloud 控制台中查看叢集建立情形。

建立 Kubernetes 叢集後,gcloud 會使用指向叢集的憑證設定 kubectl

gcloud container clusters get-credentials $cluster --zone=$zone

您現在應該可以使用 kubectl 與新叢集。

執行下列指令,即可列出叢集的 Kubernetes 節點 (這些節點應顯示「Ready」狀態):

kubectl get nodes

修改 Kubeconfig 名稱以利使用

我們會經常切換情境,因此為叢集命名簡短的別名會很方便。

這個指令會將您剛建立的 kubeconfig 項目重新命名為 primary

kubectx ${cluster}=gke_${GCLOUD_PROJECT}_${zone}_${cluster}

設定權限:

您必須是叢集管理員,才能部署 Istio。這個指令會將與 Google Cloud 帳戶相關聯的電子郵件地址設為叢集管理員

kubectl create clusterrolebinding cluster-admin-binding \
    --clusterrole=cluster-admin \
    --user=$(gcloud config get-value core/account)

5. 建立「爆發」叢集

下列指令會建立 Kubernetes 叢集:

  • 命名為「burst」
  • 在 us-west1-a 區域中,
  • 最新的 Kubernetes 版本
  • 包含 1 個初始節點
  • 最多可啟用 5 個節點的自動調度資源
export cluster=burst
export zone=us-west1-a
gcloud container clusters create $cluster --zone $zone --username "admin" \
--cluster-version latest --machine-type "n1-standard-2" \
--image-type "COS" --disk-size "100" \
--scopes "https://www.googleapis.com/auth/compute",\
"https://www.googleapis.com/auth/devstorage.read_only",\
"https://www.googleapis.com/auth/logging.write",\
"https://www.googleapis.com/auth/monitoring",\
"https://www.googleapis.com/auth/servicecontrol",\
"https://www.googleapis.com/auth/service.management.readonly",\
"https://www.googleapis.com/auth/trace.append" \
--num-nodes "1" --enable-autoscaling --min-nodes=1 --max-nodes=5 \
--network "default" \
--enable-cloud-logging --enable-cloud-monitoring --enable-ip-alias

(這項作業可能需要 5 分鐘的時間。您可以在 Cloud 控制台中查看叢集建立情形。

建立 Kubernetes 叢集後,gcloud 會使用指向叢集的憑證設定 kubectl

gcloud container clusters get-credentials $cluster --zone=$zone

您現在應該可以使用 kubectl 與新叢集。

執行下列指令,即可列出叢集的 Kubernetes 節點 (這些節點應顯示「Ready」狀態):

kubectl get nodes

修改 Kubeconfig 名稱以利使用

這個指令會將您剛剛建立的 kubeconfig 項目修改為 burst

kubectx ${cluster}=gke_${GCLOUD_PROJECT}_${zone}_${cluster}

設定權限:

您必須是叢集管理員,才能部署 Istio Remote。這個指令會將與 Google Cloud 帳戶相關聯的電子郵件地址設為叢集管理員

kubectl create clusterrolebinding cluster-admin-binding \
   
--clusterrole=cluster-admin \
   
--user=$(gcloud config get-value core/account)

6. 套用防火牆規則

為了讓兩個叢集彼此通訊,我們需要建立防火牆規則。

執行下列指令,在 Google Cloud Platform 中建立防火牆規則,讓叢集能夠通訊

function join_by { local IFS="$1"; shift; echo "$*"; }
ALL_CLUSTER_CIDRS=$(gcloud container clusters list \
--filter="(name=burst OR name=primary) AND zone=$zone" \
--format='value(clusterIpv4Cidr)' | sort | uniq)
ALL_CLUSTER_CIDRS=$(join_by , $(echo "${ALL_CLUSTER_CIDRS}"))
ALL_CLUSTER_NETTAGS=$(gcloud compute instances list \
--filter="(metadata.cluster-name=burst OR metadata.cluster-name=primary) AND metadata.cluster-location=us-west1-a" \
--format='value(tags.items.[0])' | sort | uniq)
ALL_CLUSTER_NETTAGS=$(join_by , $(echo "${ALL_CLUSTER_NETTAGS}"))
gcloud compute firewall-rules create istio-multicluster-test-pods \
  --allow=tcp,udp,icmp,esp,ah,sctp \
  --direction=INGRESS \
  --priority=900 \
  --source-ranges="${ALL_CLUSTER_CIDRS}" \
  --target-tags="${ALL_CLUSTER_NETTAGS}" --quiet

我們已設定兩個叢集,並準備在其中部署應用程式和 Istio!

7. Istio 簡介

什麼是 Istio?

Istio 是服務網格控制層,旨在「連結、保護、控制及觀察服務」。它會以各種方式執行這項操作,但主要會將 Proxy 容器 ( Envoy) 附加到每個已部署的 Kubernetes Pod。Proxy 容器會搭配通用政策和追蹤中心 ( Mixer) 控制微服務之間的所有網路通訊。

a25613cd581825da.png

這些政策可獨立於 Kubernetes 部署和服務套用,這表示網路操作員可以觀察網路活動、限制、重新導向或重寫網路政策,而無須重新部署相關應用程式。

Istio 支援的部分流量管理功能包括:

  • 斷路器
  • 以百分比為準的流量分配
  • 網址重寫
  • 傳輸層安全標準 (TLS) 終止
  • 健康狀態檢查
  • 負載平衡

本講習課程將著重於百分比流量分配。

我們將使用的 Istio 術語

VirtualService

VirtualService 會定義一組流量轉送規則,用於在主機位址時套用。

閘道

Gateway 是負載平衡器,會在邊緣網格中運作,用於接收或傳送 HTTP/TCP 連線。閘道可指定通訊埠、SNI 設定等。

DestinationRule

DestinationRule 會定義適用於轉送後,要傳送至服務的流量的政策。這些設定會指定負載平衡、Sidecar 的連線集區大小,以及離群值偵測設定。

Istio 多叢集

您可能注意到,我們建立兩個叢集時,primary 叢集有 4 個節點,但沒有自動調度資源功能,而 burst 叢集有 1 個節點,並可自動調度至最多 5 個節點。

這項設定有兩個原因。

首先,我們要模擬「內部」到「雲端」的情況。在內部部署環境中,您無法存取自動調度資源叢集,因為您有固定的基礎架構。

其次,4 個節點設定 (如上方所述) 是執行 Istio 的最低要求。這就引出一個問題:如果 Istio 至少需要 4 個節點,burst 叢集如何在 1 個節點上執行 Istio?答案是,Istio 多叢集會安裝較少的 Istio 服務,並與主要叢集中的 Istio 安裝作業通訊,以便擷取政策規則並發布遙測資訊。

8. 應用程式架構總覽

元件總覽

我們將使用 NodeJSRedis 部署三層應用程式。

Worker

這個 worker 應用程式是以 NodeJS 編寫,會監聽傳入的 POST HTTP 要求,並對這些要求執行雜湊運算,如果已定義名為 PREFIX 的環境變數,就會在雜湊前方加上該值。計算出雜湊後,應用程式會在指定 Redis 伺服器的「calculation」管道上傳送結果。

我們稍後會使用 PREFIX 環境變數來示範多叢集功能。

供您參考:這些是應用程式使用的套件。

  • body-parser: 可讓我們剖析 HTTP 要求
  • cors: 允許使用跨源資源共享
  • dotenv: 輕鬆剖析環境變數
  • express: 輕鬆託管網站
  • ioredis: 用於與 Redis 資料庫通訊的用戶端程式庫
  • morgan: 提供精美的結構化記錄

前端

我們的前端也是一個 NodeJS 應用程式,可使用 express 代管網頁。它會根據使用者輸入的頻率,以該頻率向我們的 worker 應用程式傳送要求。這個應用程式也會訂閱名為「calculation」的 Redis 管道訊息,並在網頁中顯示結果。

應用程式會使用下列依附元件。

  • body-parser: 可讓我們剖析 HTTP 要求
  • dotenv: 輕鬆剖析環境變數
  • express: 輕鬆託管網站
  • ioredis: 用於與 Redis 資料庫通訊的用戶端程式庫
  • morgan: 提供精美的結構化記錄
  • request::允許提出 HTTP 要求
  • socket.io::允許網頁與伺服器進行雙向通訊

這個網頁使用 Bootstrap 設定樣式,執行時會如下所示:

e5e3b9cbede4cac4.png

架構圖

7ae4bc22a58f80a6.png

部署圖表

我們將在建立的兩個叢集中部署最終應用程式。primary 叢集會部署所有元件 (frontendworker 和 Redis),但 burst 叢集只會部署 worker 應用程式。

下圖說明這兩個叢集。紅框代表 Kubernetes 服務,藍框則代表 Kubernetes 部署作業。黃色方塊代表 Istio 安裝作業。

561db37c510944bd.png

請注意,即使叢集中沒有 Redis 部署項目,burst 叢集仍會部署 Redis 服務。我們需要在叢集中提供這項服務,讓 Kubernetes DNS 能夠解析要求,但在實際提出要求時,Istio Proxy 會將要求重新導向至 primary 叢集中的 Redis 部署。

最終應用程式會在名為 istiowatcher.primary 叢集中執行額外的部署作業,這樣一來,當流量超過特定門檻時,我們就能自動將流量動態重新導向至 burst

8f6183bdfc3f813c.png

9. 建立應用程式部署檔案

我們需要建立一組 Kubernetes 資訊清單,才能部署應用程式

切換至專案的根目錄,然後建立名為 kubernetes 的新資料夾

mkdir ${proj}/kubernetes && cd ${proj}/kubernetes

編寫 frontend.yaml

這麼做會建立 Kubernetes 部署和服務,以便存取前端映像檔。

將以下內容插入 frontend.yaml

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: frontend-deployment
  labels:
    app: frontend
spec:
  replicas: 1
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      containers:
      - name: frontend
        image: gcr.io/istio-burst-workshop/frontend
        ports:
        - containerPort: 8080
        readinessProbe:
            initialDelaySeconds: 10
            httpGet:
              path: "/_healthz"
              port: 8080
              httpHeaders:
              - name: "Cookie"
                value: "istio_session-id=x-readiness-probe"
        livenessProbe:
          initialDelaySeconds: 10
          httpGet:
            path: "/"
            port: 8080
            httpHeaders:
            - name: "Cookie"
              value: "istio_session-id=x-liveness-probe"
        env:
        - name: PORT
          value: "8080"
        - name: PROCESSOR_URL
          value: "http://worker-service"
        - name: REDIS_URL
          value: "redis-cache-service:6379"
---
apiVersion: v1
kind: Service
metadata:
  name: frontend-service
spec:
  type: ClusterIP
  selector:
    app: frontend
  ports:
  - name: http
    port: 80
    targetPort: 8080

Deployment 中的重要注意事項

  • 我們已將應用程式執行的通訊埠指定為 8080
  • 我們已將 worker 的地址設為「http://worker-service」,並會使用 Kubernetes 內建的 DNS 功能解析產生的服務
  • 我們已將 REDIS_URL 的地址設為「redis-cache-service:6379」,並會使用 Kubernetes 內建的 DNS 功能來解析產生的 IP 位址。
  • 我們也為容器設定 livenessreadiness 探針,協助 Kubernetes 瞭解容器何時啟動及運作。

編寫 worker-service.yaml

我們會在單獨的檔案中編寫 Kubernetes 服務定義,而非在部署定義中編寫,因為我們會在多個叢集中重複使用這項服務,但會為每個叢集編寫不同的部署作業。

worker-service.yaml 中插入以下內容

apiVersion: v1
kind: Service
metadata:
 name: worker-service
spec:
 type: ClusterIP
 selector:
   app: worker
 ports:
 - name: http
   port: 80
   targetPort: 8081

編寫 worker-primary.yaml

這是我們要推送至主要叢集的 worker 部署作業。

將以下內容插入 worker-primary.yaml

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
 name: worker-deployment
 labels:
   app: worker
spec:
 replicas: 1
 selector:
   matchLabels:
     app: worker
 template:
   metadata:
     labels:
       app: worker
       cluster-type: primary-cluster
   spec:
     containers:
     - name: worker
       image: gcr.io/istio-burst-workshop/worker
       imagePullPolicy: Always
       ports:
       - containerPort: 8081
       readinessProbe:
           initialDelaySeconds: 10
           httpGet:
             path: "/_healthz"
             port: 8081
             httpHeaders:
             - name: "Cookie"
               value: "istio_session-id=x-readiness-probe"
       livenessProbe:
         initialDelaySeconds: 10
         httpGet:
           path: "/"
           port: 8081
           httpHeaders:
           - name: "Cookie"
             value: "istio_session-id=x-liveness-probe"
       env:
       - name: PORT
         value: "8081"
       - name: REDIS_URL
         value: "redis-cache-service:6379"

請注意,我們在這裡採用相同的模式,提供 livenessreadiness 探針,並指定應用程式要使用的 PORTREDIS_URL 環境變數。

這次部署作業還有另一個值得注意的地方,就是缺少 PREFIX 環境變數。也就是說,我們的計算結果將是原始雜湊值 (前面沒有任何前置字串)。

這項部署作業的最後重點是 cluster-type: primary-cluster 標籤。稍後我們會在 Istio 多叢集上進行流量路由時使用這個值

編寫 redis.yaml

工作站與前端的通訊是透過 Redis 管道進行,因此我們需要在叢集中部署 Redis 應用程式。

將下列內容插入 redis.yaml

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
 name: redis-cache
spec:
 template:
   metadata:
     labels:
       app: redis-cache
   spec:
     containers:
     - name: redis
       image: redis:alpine
       ports:
       - containerPort: 6379
       readinessProbe:
         periodSeconds: 5
         tcpSocket:
           port: 6379
       livenessProbe:
         periodSeconds: 5
         tcpSocket:
           port: 6379
       volumeMounts:
       - mountPath: /data
         name: redis-data
       resources:
         limits:
           memory: 256Mi
           cpu: 125m
         requests:
           cpu: 70m
           memory: 200Mi
     volumes:
     - name: redis-data
       emptyDir: {}

這是 Redis 應用程式的半標準部署作業。它會根據 redis:alpine 映像檔建立容器、公開適當的連接埠,並設定合理的資源限制。

編寫 redis-service.yaml

我們需要 Kubernetes 服務才能與 Redis 應用程式通訊

將下列內容插入 redis-service.yaml

apiVersion: v1
kind: Service
metadata:
 name: redis-cache-service
spec:
 type: ClusterIP
 selector:
   app: redis-cache
 ports:
 - port: 6379
   targetPort: 6379

這會提供名為 redis-cache-service 的服務,用於存取 Redis 部署。

10. 部署應用程式

將映像檔推送至 GCR 並編寫 Kubernetes 資訊清單後,現在是部署應用程式並查看其運作方式的好時機!

執行下列指令來部署應用程式

  1. 確認我們位於正確的叢集
kubectx primary
  1. 部署 Redis Cache
kubectl apply -f redis.yaml
  1. 部署 Redis 服務
kubectl apply -f redis-service.yaml
  1. 部署前端
kubectl apply -f frontend.yaml
  1. 部署 worker
kubectl apply -f worker-primary.yaml
  1. 部署 Worker 服務
kubectl apply -f worker-service.yaml

我們已將應用程式部署至 GKE。恭喜!

測試

等待 Pod 上線

kubectl get pods -w

所有 Pod 都處於「Running」狀態後,請按下 Ctrl + C

NAME                                   READY     STATUS    RESTARTS   AGE
frontend-deployment-695d95fbf7-76sd8   1/1       Running   0          2m
redis-cache-7475999bf5-nxj8x           1/1       Running   0          2m
worker-deployment-5b9cf9956d-g975p     1/1       Running   0          2m

您會發現我們並未透過 LoadBalancer 公開前端。這是因為稍後我們會透過 Istio 存取應用程式。為了測試所有服務是否都已啟用及運作,我們會使用 kubectl port-forward. 執行下列指令,將本機 (或 Cloud Shell) 電腦上的通訊埠 8080 轉送至執行 frontend 部署作業的通訊埠 8080。

kubectl port-forward \
$
(kubectl get pods -l app=frontend -o jsonpath='{.items[0].metadata.name}') \
8080:8080

如果您是在本機上執行:請開啟網路瀏覽器,然後前往 http://localhost:8080

如果您在 Cloud Shell 中執行:請按一下「網頁預覽」按鈕,然後選取「透過以下通訊埠預覽:8080」。

bdb5dc75f415be11.png

您應該會看到前端!如果您在「頻率」方塊中輸入數字,系統就會開始顯示井字號

1caafaffab26897a.png

恭喜!一切都已完成!

按下 Ctrl+C 即可停止轉送通訊埠。

11. 清理已部署的應用程式

我們將在叢集中套用 Istio,然後重新部署應用程式,因此請先清除目前的應用程式。

執行下列指令,即可刪除所有剛建立的部署作業和服務

  1. 刪除「redis-cache-service
kubectl delete -f redis-service.yaml
  1. 刪除「redis
kubectl delete -f redis.yaml
  1. 刪除「frontend
kubectl delete -f frontend.yaml
  1. 刪除「worker
kubectl delete -f worker-primary.yaml
  1. 刪除「worker-service
kubectl delete -f worker-service.yaml

12. 在主要叢集上安裝 Istio

取得 Istio

Istio 的發布版本由 GitHub 託管。下列指令會下載 1.0.0 版的 istio 並解壓縮。

  1. 變更至專案根目錄
cd ${proj}
  1. 下載封存檔案
curl -LO https://github.com/istio/istio/releases/download/1.0.0/istio-1.0.0-linux.tar.gz
  1. 解壓縮及移除封存檔
tar xzf istio-1.0.0-linux.tar.gz && rm istio-1.0.0-linux.tar.gz

建立 Istio 範本

執行下列 Helm 指令可建立範本,以便在叢集中安裝 Istio。

helm template istio-1.0.0/install/kubernetes/helm/istio \
--name istio --namespace istio-system \
--set prometheus.enabled=true \
--set servicegraph.enabled=true  > istio-primary.yaml

這樣會在目前目錄中建立名為 istio-primary.yaml 的檔案,其中包含部署及執行 Istio 所需的所有定義和規格。

請注意兩個 --set 參數。這些功能可為 Istio 系統新增 PrometheusServiceGraph 支援功能。我們稍後會在本研究室中使用 Prometheus 服務。

部署 Istio

如要部署 Istio,我們必須先建立名為 istio-system 的命名空間,讓 Istio 部署和服務能夠在其中執行。

kubectl create namespace istio-system

最後,套用我們使用 Helm 建立的 istio-primary.yaml 檔案

kubectl apply -f istio-primary.yaml

標籤預設命名空間

Istio 會將補充 Proxy 服務插入每個部署作業。這項功能是採用選擇加入的方式,因此我們需要使用 istio-injection=enableddefault 命名空間加上標籤,讓 Istio 自動為我們插入補充資訊。

kubectl label namespace default istio-injection=enabled

恭喜!我們已建立叢集並啟用 Istio,準備部署應用程式!

13. 使用 Istio 流量管理功能部署應用程式

建立 Istio 流量管理設定檔

Istio 的運作方式與 Kubernetes 相似,因為它會使用 yaml 檔案進行設定。因此,我們需要建立一組檔案,告訴 Istio 如何公開及轉送流量。

建立名為 istio-manifests 的目錄,然後切換至該目錄

mkdir ${proj}/istio-manifests && cd ${proj}/istio-manifests

編寫 frontend-gateway.yaml

這個檔案會以類似 GKE LoadBalancer 的方式公開 Kubernetes 叢集,並將所有傳入流量轉送至前端服務。

建立名為 frontend-gateway.yaml 的檔案,並插入以下內容。

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
 name: frontend-gateway
spec:
 selector:
   istio: ingressgateway # use Istio default gateway implementation
 servers:
 - port:
     number: 80
     name: http
     protocol: HTTP
   hosts:
   - "*"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: frontend-ingress-virtual-service
spec:
 hosts:
 - "*"
 gateways:
 - frontend-gateway
 http:
 - route:
   - destination:
       host: frontend-service
       port:
         number: 80

編寫 redis-virtualservice.yaml

建立名為 redis-virtualservice.yaml 的檔案,並插入以下內容

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: redis-virtual-service
spec:
 hosts:
 - redis-cache-service
 gateways:
 - mesh
 tcp:
 - route:
   - destination:
       host: redis-cache-service.default.svc.cluster.local

編寫 worker-virtualservice.yaml

建立名為 worker-virtualservice.yaml 的檔案,並插入以下內容

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: worker-virtual-service
spec:
 hosts:
 - worker-service
 gateways:
 - mesh
 http:
 - route:
   - destination:
       host: worker-service.default.svc.cluster.local  
       port:
         number: 80

部署 Istio 流量管理政策

部署 Istio 政策的方式與其他 Kubernetes 資源相同,使用 kubectl apply

  1. 套用閘道
kubectl apply -f frontend-gateway.yaml
  1. 套用 Redis VirtualService
kubectl apply -f redis-virtualservice.yaml
  1. 套用我們的 Worker VirtualService
kubectl apply -f worker-virtualservice.yaml

部署應用程式

  1. 變更回 kubernetes 目錄
cd ${proj}/kubernetes
  1. 部署 Redis Cache
kubectl apply -f redis.yaml
  1. 部署 Redis 服務
kubectl apply -f redis-service.yaml
  1. 部署前端
kubectl apply -f frontend.yaml
  1. 部署 worker
kubectl apply -f worker-primary.yaml
  1. 部署 Worker 服務
kubectl apply -f worker-service.yaml

驗證

目前,我們已在叢集中重新部署應用程式,並套用 Istio 和流量管理政策。

請等候所有工作負載上線

所有節點都上線後,請取得我們在 frontend-ingressgateway.yaml 中設定的 IngressGateway

$ kubectl -n istio-system get svc istio-ingressgateway
NAME                   TYPE           CLUSTER-IP    EXTERNAL-IP     PORT(S)                                                                                                     AGE
istio-ingressgateway   LoadBalancer   10.36.3.112   35.199.158.10   80:31380/TCP,

請瀏覽 <EXTERNAL-IP> 位址,或使用 curl 命令,您應該會看到前端!

$ curl 35.199.158.10
<!doctype html>
<html>

<head>
    <title>String Hashr</title>
    <!-- Bootstrap -->
...

14. 在「burst」叢集中安裝 Istio

我們花了許多時間在 primary 叢集中進行設定及部署作業,但我們還有另一個叢集要部署!

在本節中,我們需要從兩個叢集中擷取設定變數,因此請密切留意每個指令指向哪個叢集。

建立 Istio 遠端資訊清單

就像我們將 Istio 部署至 primary 叢集一樣,我們會使用 Helm 將 Istio 遠端部署作業模板化,以便部署至 burst 叢集。不過,我們必須先取得 primary 叢集的相關資訊

收集主要叢集資訊

變更為 primary 叢集

kubectx primary

下列指令會擷取主要叢集中各個 Pod 的 IP 位址。Istio Remote 會使用這些資訊,與主要叢集進行通訊。

export PILOT_POD_IP=$(kubectl -n istio-system get pod -l istio=pilot -o jsonpath='{.items[0].status.podIP}')
export POLICY_POD_IP=$(kubectl -n istio-system get pod -l istio-mixer-type=policy -o jsonpath='{.items[0].status.podIP}')
export STATSD_POD_IP=$(kubectl -n istio-system get pod -l istio=statsd-prom-bridge -o jsonpath='{.items[0].status.podIP}')
export TELEMETRY_POD_IP=$(kubectl -n istio-system get pod -l istio-mixer-type=telemetry -o jsonpath='{.items[0].status.podIP}')
export ZIPKIN_POD_IP=$(kubectl -n istio-system get pod -l app=jaeger -o jsonpath='{range .items[*]}{.status.podIP}{end}')

建立遙控器範本

我們現在將使用 helm 建立名為 istio-remote-burst.yaml 的檔案,然後部署至 burst 叢集。

變更為專案根目錄

cd $proj
helm template istio-1.0.0/install/kubernetes/helm/istio-remote --namespace istio-system \
--name istio-remote \
--set global.remotePilotAddress=${PILOT_POD_IP} \
--set global.remotePolicyAddress=${POLICY_POD_IP} \
--set global.remoteTelemetryAddress=${TELEMETRY_POD_IP} \
--set global.proxy.envoyStatsd.enabled=true \
--set global.proxy.envoyStatsd.host=${STATSD_POD_IP} \
--set global.remoteZipkinAddress=${ZIPKIN_POD_IP} > istio-remote-burst.yaml

在爆發叢集上安裝 Istio Remote

如要在 burst 叢集中安裝 Istio,請按照在 primary 叢集中安裝時的步驟操作,但請改用 istio-remote-burst.yaml 檔案。

將 kubecontext 變更為 burst

kubectx burst

建立 istio-system 命名空間

kubectl create ns istio-system

套用 istio-burst.yaml

kubectl apply -f istio-remote-burst.yaml

標籤預設命名空間

同樣地,我們需要標記 default 命名空間,讓 Proxy 能夠自動插入。

kubectl label namespace default istio-injection=enabled

恭喜!目前,我們已在 burst 叢集上設定 Istio Remote。不過,叢集目前仍無法進行通訊。我們需要為 burst 叢集產生 kubeconfig 檔案,以便部署至 primary 叢集,並將兩者連結在一起。

為「burst」叢集建立 kubeconfig

變更為突發叢集

kubectx burst

設定環境

我們需要收集叢集的相關資訊,才能為叢集建立 kubeconfig 檔案。

  1. 取得叢集名稱
CLUSTER_NAME=$(kubectl config view --minify=true -o "jsonpath={.clusters[].name}")
  1. 取得叢集伺服器名稱
SERVER=$(kubectl config view --minify=true -o "jsonpath={.clusters[].cluster.server}")
  1. 取得 istio-multi 服務帳戶憑證授權單位的密鑰名稱
SECRET_NAME=$(kubectl get sa istio-multi -n istio-system -o jsonpath='{.secrets[].name}')
  1. 取得先前機密金鑰中儲存的憑證授權單位資料
CA_DATA=$(kubectl get secret ${SECRET_NAME} -n istio-system -o "jsonpath={.data['ca\.crt']}")
  1. 取得先前機密中儲存的權杖
TOKEN=$(kubectl get secret ${SECRET_NAME} -n istio-system -o "jsonpath={.data['token']}" | base64 --decode)

建立 kubeconfig 檔案

設定所有環境變數後,我們需要建立 kubeconfig 檔案

cat <<EOF > burst-kubeconfig
apiVersion: v1
clusters:
   - cluster:
       certificate-authority-data: ${CA_DATA}
       server: ${SERVER}
     name: ${CLUSTER_NAME}
contexts:
   - context:
       cluster: ${CLUSTER_NAME}
       user: ${CLUSTER_NAME}
     name: ${CLUSTER_NAME}
current-context: ${CLUSTER_NAME}
kind: Config
preferences: {}
users:
   - name: ${CLUSTER_NAME}
     user:
       token: ${TOKEN}
EOF

這會在目前目錄中建立名為 burst-kubeconfig 的新檔案,primary 叢集可使用這個檔案驗證及管理 burst 叢集。

改回主要叢集

kubectx primary

建立密鑰並加上標籤,套用「burst」的 kubeconfig

kubectl create secret generic burst-kubeconfig --from-file burst-kubeconfig -n istio-system

為密鑰加上標籤,讓 Istio 知道要將其用於多叢集驗證

kubectl label secret burst-kubeconfig istio/multiCluster=true -n istio-system

恭喜!我們已驗證兩個叢集,並透過 Istio 多叢集功能相互通訊。讓我們部署跨叢集應用程式

15. 部署跨叢集應用程式

建立部署作業

切換至 kubernetes 目錄

cd ${proj}/kubernetes

為「burst」叢集建立 worker 部署作業:worker-burst.yaml

建立名為 worker-burst.yaml 的檔案,並插入以下內容:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: worker-deployment
  labels:
    app: worker
spec:
  replicas: 1
  selector:
    matchLabels:
      app: worker
  template:
    metadata:
      labels:
        app: worker
        cluster-type: burst-cluster
    spec:
      containers:
      - name: worker
        image: gcr.io/istio-burst-workshop/worker
        imagePullPolicy: Always
        ports:
        - containerPort: 8081
        readinessProbe:
            initialDelaySeconds: 10
            httpGet:
              path: "/_healthz"
              port: 8081
              httpHeaders:
              - name: "Cookie"
                value: "istio_session-id=x-readiness-probe"
        livenessProbe:
          initialDelaySeconds: 10
          httpGet:
            path: "/"
            port: 8081
            httpHeaders:
            - name: "Cookie"
              value: "istio_session-id=x-liveness-probe"
        env:
        - name: PORT
          value: "8081"
        - name: REDIS_URL
          value: "redis-cache-service:6379"
        - name: PREFIX
          value: "bursty-"

請注意,這幾乎與先前建立的 worker-primary.yaml 完全相同。兩者之間有兩個主要差異。

第一個主要差異是我們新增了 PREFIX 環境變數,其值為「bursty-

env:
- name: PORT
  value
: "8081"
- name: REDIS_URL
  value
: "redis-cache-service:6379"
- name: PREFIX
  value
: "bursty-"

這表示 burst 叢集中的工作者會在傳送的所有雜湊前方加上「bursty-」,我們可以利用這項資訊判斷應用程式是否確實跨叢集。

第二個主要差異是,我們將此部署作業的 cluster-type 標籤從 primary-cluster 變更為 burst-cluster

labels:
  app: worker
  cluster-type: burst-cluster

我們會在稍後更新 VirtualService 時使用這個標籤。

修改 Istio 服務

目前,我們的 Istio 服務並未充分利用這兩個部署項目。100% 的流量都會轉送至「primary」叢集。我們來調整一下吧!

切換至 istio-manifests 目錄

cd ${proj}/istio-manifests

編輯 worker-virtualservice.yaml 以加入 DestinationRules

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: worker-virtual-service
spec:
  hosts:
  - worker-service
  gateways:
  - mesh
  http:
  - route:
    - destination:
        host: worker-service.default.svc.cluster.local    
        subset: primary
        port:
          number: 80        
      weight: 50
    - destination:
        host: worker-service.default.svc.cluster.local    
        subset: burst  
        port:
          number: 80        
      weight: 50
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: worker-destination-rule
spec:
  host: worker-service
  trafficPolicy:
    loadBalancer:
      simple: RANDOM
  subsets:
  - name: primary
    labels:
      cluster-type: primary-cluster
  - name: burst
    labels:
     cluster-type: burst-cluster

您可以看到我們已在 VirtualService 中新增第二個目的地。它仍會參照相同的主機 (worker-service.default.svc.cluster.local)),但 50% 的流量會路由至 primary 子集,而其他 50% 會路由至 burst 子集。

我們將 primary 子集定義為標籤為 cluster-type: primary-cluster 的部署,而 burst 子集則是標籤為 cluster-type: burst-cluster 的部署。

這樣一來,兩個叢集的流量就會以 50/50 的比例分配。

部署至叢集

將 redis-service.yaml 部署至爆發叢集

變更為 burst kubeconfig

kubectx burst

變更至專案根目錄

cd ${proj}

然後部署

將 redis-service.yaml 部署至爆發叢集

kubectl apply -f kubernetes/redis-service.yaml

將 worker-burst.yaml 部署至 burst 叢集

kubectl apply -f kubernetes/worker-burst.yaml

將 worker-service.yaml 部署至爆發叢集

kubectl apply -f kubernetes/worker-service.yaml

套用 Istio VirtualServices

變更為 primary kubeconfig

kubectx primary

然後部署

kubectl apply -f istio-manifests/worker-virtualservice.yaml

驗證是否運作正常

如要確認是否正常運作,請瀏覽 Istio Ingress 點,並注意約 50% 的雜湊字串前置字串為「burst-」。

78fb6e235e9f4a07.png

這表示我們已成功跨叢集通訊!請嘗試變更不同服務的權重,並套用 worker-virtualservice.yaml 檔案。這是平衡叢集間流量的好方法,但如果能自動執行這項操作,那就更棒了。

16. 善用 Prometheus 指標

Prometheus 簡介

Prometheus 是開放原始碼系統監控與快訊工具包,最初是在 SoundCloud 上打造。這個資料集會維護多維度資料模型,其中時間序列資料會以指標名稱和鍵/值組合來識別。

以下是 Prometheus 架構圖,供您參考:

601e1155a825e0c2.png

當 Istio 與 Prometheus 一併部署時,會自動將各種指標回報至 Prometheus 伺服器。我們可以使用這些指標即時管理叢集。

探索 Prometheus 指標

首先,我們需要公開 Prometheus 部署作業。

前往 GKE 的「Workloads」(工作負載) 分頁,然後深入查看「prometheus」工作負載。

b4a7a3cd67db05b3.png

查看部署作業詳細資料後,請依序前往「Actions」>「Expose」。

c04a482e55bdfd41.png

選擇轉送至 9090 通訊埠,然後輸入「負載平衡器」

d5af3ba22a7a6ebb.png

並選擇「曝光」。

這會在可公開存取的 IP 位址上建立服務,我們可以用來探索 Prometheus 指標

等待端點開始運作,然後按一下「外部端點」旁的 IP 位址 b1e40ad90851da29.png

您現在應該會看到 Prometheus UI。

ed273552270337ec.png

Prometheus 提供足夠的指標,可做為單獨的工作坊。不過,我們先來探討 istio_requests_total 指標。

執行這項查詢會傳回大量資料。這是所有透過 Istio 服務網格傳送的要求的指標,而且數量龐大!我們會修改運算式,篩選出我們真正感興趣的內容:

要求的目的服務為 worker-service.default.svc.cluster.local,且來源為 frontend-deployment,且限制在過去 15 秒內

查詢如下所示:

istio_requests_total{reporter="destination",
destination_service="worker-service.default.svc.cluster.local",
source_workload="frontend-deployment"}[15s]

並提供更容易管理的資料

19d551fd5eac3785.png

但還是有點密集。我們想知道每秒的請求次數,而不是所有要求。

為此,我們可以使用內建的 rate 函式

rate(istio_requests_total{reporter="destination",
destination_service="worker-service.default.svc.cluster.local",
source_workload="frontend-deployment"}[15s])

dbb9dc063a18da9b.png

這有助於我們更接近目標,但我們需要進一步將這些指標縮減為邏輯群組。

為此,我們可以使用 sumby 關鍵字來分組及加總結果

sum(rate(istio_requests_total{reporter="destination",
destination_service="worker-service.default.svc.cluster.local",
source_workload="frontend-deployment"}[15s])) by (source_workload,
source_app, destination_service)

898519966930ec56.png

太棒了,我們可以從 Prometheus 取得所需的確切指標。

最終 Prometheus 查詢

根據我們所學到的一切,我們需要向 Prometheus 提出的最終查詢為

sum(rate(istio_requests_total{reporter="destination",
destination_service="worker-service.default.svc.cluster.local",
source_workload="frontend-deployment"}[15s])) by (source_workload,
source_app, destination_service)

我們現在可以使用他們的 HTTP API 取得指標。

我們可以透過發出 HTTP GET 要求,以自己的查詢來查詢 Prometheus 的 API,請按照以下方式進行。請將 <prometheus-ip-here> 替換為 Prometheus 的 IP 位址。

curl http://<prometheus-ip-here>/api/v1/query?query=sum\(rate\(istio_requests_total%7Breporter%3D%22destination%22%2C%0Adestination_service%3D%22worker-service.default.svc.cluster.local%22%2C%0Asource_workload%3D%22frontend-deployment%22%7D%5B15s%5D\)\)%20by%20\(source_workload%2C%0Asource_app%2C%20destination_service\)

回覆範例如下:

{
    "status": "success",
    "data": {
        "resultType": "vector",
        "result": [
            {
                "metric": {
                    "destination_service": "worker-service.default.svc.cluster.local",
                    "source_app": "frontend",
                    "source_workload": "frontend-deployment"
                },
                "value": [
                    1544404907.503,
                    "18.892886390062788"
                ]
            }
        ]
    }
}

我們現在可以從 JSON 中擷取指標值

清除

我們需要刪除剛剛用來公開 Prometheus 的服務。在 Google Cloud 控制台中,前往我們剛建立的服務頂端,然後點選「刪除」

d58cb51b4c922751.png

後續步驟:

我們已經找到方法,瞭解流量如何在叢集中移動,以及移動速度,接下來要做的是編寫一個小型二進位檔,定期查詢 Prometheus。如果 worker 的每秒要求數超過特定門檻,就會在 worker 虛擬服務上套用不同的目的地權重,將所有流量傳送至 burst 叢集。當每秒要求次數低於較低門檻時,請將所有流量傳回 primary

17. 建立跨叢集爆發

設定方式

將 worker-service 的所有流量設為主要叢集

我們會將所有目的地為 worker-service 的流量,且已路由至 primary 叢集的流量視為應用程式的「預設」狀態

請將 $proj/istio-manifests/worker-virtualservice.yaml 編輯成如下所示

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: worker-virtual-service
spec:
  hosts:
  - worker-service
  gateways:
  - mesh
  http:
  - route:
    - destination:
        host: worker-service.default.svc.cluster.local    
        subset: primary
        port:
          number: 80        
      weight: 100
    - destination:
        host: worker-service.default.svc.cluster.local    
        subset: burst  
        port:
          number: 80        
      weight: 0
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: worker-destination-rule
spec:
  host: worker-service
  trafficPolicy:
    loadBalancer:
      simple: RANDOM
  subsets:
  - name: primary
    labels:
      cluster-type: primary-cluster
  - name: burst
    labels:
     cluster-type: burst-cluster

確認您已連線至 primary 叢集

kubectx primary

套用 istio-manifests/worker-virtualservice.yaml

kubectl apply -f istio-manifests/worker-virtualservice.yaml

編寫 istiowatcher 守護程式

我們將使用 Go 編寫這項服務,以便享有速度和可攜性。應用程式的整體流程會啟動,並每秒查詢 Prometheus。

在 src 中建立名為 istiowatcher 的新目錄

mkdir -p ${proj}/src/istiowatcher && cd ${proj}/src/istiowatcher

我們會從容器內呼叫 istioctl,以便在叢集中操控 Istio 控制平面。

編寫 istiowatcher.go

在該目錄中建立名為 istiowatcher.go 的檔案,並在其中插入下列內容

package main

import (
       
"github.com/tidwall/gjson"
       
"io/ioutil"
       
"log"
       
"net/http"
       
"os/exec"
       
"time"
)

func main() {
       
//These are in requests per second
       
var targetLow float64 = 10
       
var targetHigh float64 = 15
       
// This is for the ticker in milliseconds
       
ticker := time.NewTicker(1000 * time.Millisecond)

       
isBurst := false

       
// Our prometheus query
       
reqQuery := `/api/v1/query?query=sum(rate(istio_requests_total{reporter="destination",destination_service="worker-service.default.svc.cluster.local",source_workload="frontend-deployment"}[15s]))by(source_workload,source_app,destination_service)`

       
for t := range ticker.C {
               
log.Printf("Checking Prometheus at %v", t)

               
// Check prometheus
               
// Note that b/c we are querying over the past 5 minutes, we are getting a very SLOW ramp of our reqs/second
               
// If we wanted this to be a little "snappier" we can scale it down to say 30s
               
resp, err := http.Get("http://prometheus.istio-system.svc.cluster.local:9090" + reqQuery)
               
if err != nil {
                       
log.Printf("Error: %v", err)
                       
continue
               
}
               
defer resp.Body.Close()
               
body, _ := ioutil.ReadAll(resp.Body)

               
val := gjson.Get(string(body), "data.result.0.value.1")
               
log.Printf("Value: %v", val)

               
currentReqPerSecond := val.Float()
               
log.Printf("Reqs per second %f", currentReqPerSecond)

               
if currentReqPerSecond > targetHigh && !isBurst {
                       
applyIstio("burst.yaml")
                       
log.Println("Entering burst mode")
                       
isBurst = true
               
} else if currentReqPerSecond < targetLow && isBurst {
                       
applyIstio("natural.yaml")
                       
log.Println("Returning to natural state.")
                       
isBurst = false
               
}
       
}
}

func applyIstio(filename string) {
       
cmd := exec.Command("istioctl", "replace", "-f", filename)
       
if err := cmd.Run(); err != nil {
               
log.Printf("Error hit applying istio manifests: %v", err)
       
}
}

編寫 Dockerfile

建立名為 Dockerfile 的新檔案,並在其中插入下列內容。

FROM golang:1.11.2-stretch as base

FROM base as builder

WORKDIR /workdir
RUN curl -LO https://github.com/istio/istio/releases/download/1.0.0/istio-1.0.0-linux.tar.gz
RUN tar xzf istio-1.0.0-linux.tar.gz
RUN cp istio-1.0.0/bin/istioctl ./istioctl

FROM base

WORKDIR /go/src/istiowatcher
COPY . .
COPY --from=builder /workdir/istioctl /usr/local/bin/istioctl

RUN go get -d -v ./...
RUN go install -v ./...

CMD ["istiowatcher"]

這個多階段 Dockerfile 會在第一個階段下載並解壓縮 Istio 1.0.0 版本。第二個階段會將目錄中的所有內容複製到映像檔,然後將 istioctl 從建構階段複製到 /usr/local/bin (以便讓應用程式呼叫),取得依附元件、編譯程式碼,並將 CMD 設為「istiowatcher」。

編寫 burst.yaml

frontendworker 的每秒要求次數超過 15 時,istiowatcher 就會套用這個檔案。

建立名為 burst.yaml 的新檔案,並在其中插入下列內容。

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: worker-virtual-service
spec:
 hosts:
 - worker-service
 gateways:
 - mesh
 http:
 - route:
   - destination:
       host: worker-service.default.svc.cluster.local  
       subset: primary
       port:
         number: 80      
     weight: 0
   - destination:
       host: worker-service.default.svc.cluster.local    
       subset: burst
       port:
         number: 80      
     weight:  100

編寫 natural.yaml

frontendworker 的每秒要求次數低於 10 時,我們會將此視為「自然」狀態。在這個狀態下,100% 的流量都會轉送至 primary 叢集。

建立名為 natural.yaml 的新檔案,並在其中插入下列內容

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: worker-virtual-service
spec:
 hosts:
 - worker-service
 gateways:
 - mesh
 http:
 - route:
   - destination:
       host: worker-service.default.svc.cluster.local  
       subset: primary
       port:
         number: 80      
     weight: 100
   - destination:
       host: worker-service.default.svc.cluster.local    
       subset: burst
       port:
         number: 80      
     weight: 0

建構及推送 istiowatcher

執行下列指令,將目前目錄傳送至 Google Cloud Build (GCB),GCB 會在 GCR 中建構並標記映像檔。

gcloud builds submit -t gcr.io/${GCLOUD_PROJECT}/istiowatcher

部署 istiowatcher

切換至 kubernetes 目錄

cd ${proj}/kubernetes/

編寫部署檔案:istiowatcher.yaml

建立名為 istiowatcher.yaml 的檔案,然後插入以下內容 (請替換 <your-project-id>)。

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: istiowatcher-deployment
  labels:
    app: istiowatcher
spec:
  replicas: 1
  selector:
    matchLabels:
      app: istiowatcher
  template:
    metadata:
      labels:
        app: istiowatcher
    spec:
      serviceAccountName: istio-pilot-service-account
      automountServiceAccountToken: true
      containers:
      - name: istiowatcher
        image: gcr.io/<your-project-id>/istiowatcher
        imagePullPolicy: Always

部署

確認我們是在主要叢集中執行

kubectx primary

istio-system 命名空間中部署 istiowatcher.yaml

kubectl apply -n istio-system -f istiowatcher.yaml

請注意 yaml 中的 serviceAccountNameautomountServiceAccountToken 指令。這樣一來,我們就能取得在叢集內執行 istioctl 所需的憑證。

我們也需要在 istio-system 命名空間中部署這項作業,確保我們擁有 istio-pilot-service-account 的憑證。(它不存在於 default 命名空間中)。

觀看流量自動切換!

接下來是神奇時刻!讓我們前往前端,將每秒要求次數調高至 20

請注意,這項作業需要幾秒鐘的時間,但我們會加快速度,所有雜湊都會加上「bursty-」前置字串!

這是因為我們會在 15s 範圍內取樣 Prometheus,因此回應時間會稍微延遲。如果想縮小頻寬,可以將 Prometheus 查詢變更為 5s.

18. 後續步驟

清除

如果您使用的是本工作坊提供的臨時帳戶,則不需要清理。

您可以刪除 Kubernetes 叢集、防火牆規則和 GCR 中的映像檔

gcloud container clusters delete primary --zone=us-west1-a
gcloud container clusters delete burst --zone=us-west1-a
gcloud compute firewall-rules delete istio-multicluster-test-pods 
gcloud container images delete gcr.io/$GCLOUD_PROJECT/istiowatcher

未來展望