Istio Routing 實踐掌握virtualservice/gateway/destinationrule/AB版本釋出/金絲雀釋出

PassZhang發表於2020-08-18

原文

在學習像 Istio 這樣的新技術時,看一下示例應用程式總是一個好主意。 Istio repo 有一些示例應用程式,但它們似乎有各種不足。 文件中的 BookInfo 是一個很好的示例。 但是,對於我而言,它太冗長,服務太多,而且文件似乎專注於管理 BookInfo 應用程式,而不是從頭開始構建。 有一個較小的 hellohttp 例子,但它更多的是關於自動伸縮而不是其他。

在這篇文章中,我想介紹一下基礎知識,並向您展示如何從頭開始構建支援 Istio 的“hellohttp”應用程式。 要記住的一點是,Istio 只管理您應用的流量。 在這種情況下,應用程式生命週期由底層平臺 Kubernetes 管理。 因此,您需要了解容器和 Kubernetes 基礎知識,並且需要了解 Istio Routing 原語,例如 Gateway,VirtualService,DestinationRule。 我假設大多數人都知道容器和 Kubernetes 基礎知識。 我將在本文中專注於 Istio Routing。

基礎步驟

以下這些大致就是您需要遵循的,以獲得 Istio 的“hellohttp”應用程式的步驟:

  1. 建立一個 Kubernetes 叢集並安裝帶有 sidecare 自動注入的 Istio。
  2. 使用您選擇的語言建立 Hellohttp 應用程式,建立 Docker 映象並將其推送到公共映象倉庫。
  3. 為你的容器建立 Kubernetes deployment 和 service。
  4. 建立 Gateway 以啟用到群集的 HTTP(S)流量。
  5. 建立 VirtualService,通過 Gateway 公開 Kubernetes 服務。
  6. (可選)如果要建立多個版本應用程式,請建立 DestinationRule 以定義可從 VirtualService 引用的 subsets。
  7. (可選)如果要在服務網格外部呼叫其他外部服務,請建立 ServiceEntry。

我不會在本文中介紹步驟 1 和 2,因為它們不是特定於 Istio 的。 如果您需要有關這些步驟的幫助,可以檢視我在本文末尾提到的文章。 第 3 步也不是 Istio 特定的,但它是其他一切的先決條件,所以讓我們從那開始。

Deployment 和 Service

正如我所提到的,應用程式生命週期由 Kubernetes 管理。 因此,您需要從建立 Kubernetes deployment 和 service 開始。 我的情況如下,我有一個容器化的 ASP.NET 核心應用程式,其映象我已經推送到谷歌映象倉庫。 讓我們從建立一個hellohttp-deploy.yaml.yaml檔案開始:

apiVersion: v1
kind: Service
metadata:
  name: hellohttp-service
  labels:
    app: hellohttp
spec:
  ports:
    - port: 80
      name: http
  selector:
    app: hellohttp
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hellohttp-v1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: hellohttp
      version: v1
  template:
    metadata:
      labels:
        app: hellohttp
        version: v1
    spec:
      containers:
        - name: hellohttp
          image: busybox 
          imagePullPolicy: IfNotPresent
          command: ["/bin/sh", "-c", "echo 'hello httpd v1' > /var/www/index.html; httpd -f -p 80 -h /var/www/"]
          ports:
            - containerPort: 80

建立 Deployment 和 Service:

$ kubectl apply -f hellohttp-deploy.yaml
service "hellohttp-service" created
deployment.extensions "hellohttp-v1" created

到目前為止沒有任何特定的針對 Istio 的內容。

Gateway

我們現在可以開始研究 Istio Routing。 首先,我們需要為服務網格啟用 HTTP/HTTPS 流量。 為此,我們需要建立一個閘道器。 Gateway 描述了在邊緣執行的負載均衡,用於接收傳入或傳出的 HTTP/TCP 連線。

讓我們建立一個hellohttp-gateway.yaml檔案:

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: hellohttp-gateway
spec:
  selector:
    istio: ingressgateway # use istio default controller
  servers:
    - port:
        number: 80
        name: http
        protocol: HTTP
      hosts:
        - "hellohttp.com"

建立 Gateway:

$ kubectl apply -f hellohttp-gateway.yaml
gateway.networking.istio.io "hellohttp-gateway" created

此時,我們為叢集啟用了 HTTP 流量。 我們需要將之前建立的 Kubernetes 服務對映到 Gateway。 我們將使用 VirtualService 執行此操作。

VirtualService

VirtualService 實際上將 Kubernetes 服務連線到 Istio 閘道器。 它還可以執行更多操作,例如定義一組流量路由規則,以便在主機被定址時應用,但我們不會深入瞭解這些細節。

讓我們建立一個hellohttp-virtualservice.yaml檔案:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: hellohttp-virtualservice
spec:
  hosts:
    - "hellohttp.com"
  gateways:
    - hellohttp-gateway
  http:
    - route:
        - destination:
            host: hellohttp-service

請注意,VirtualService 與特定閘道器繫結,並定義引用 Kubernetes 服務的主機。

建立 VirtualService:

$ kubectl apply -f hellohttp-virtualservice.yaml
virtualservice.networking.istio.io "hellohttp-virtualservice" created

測試 V1 版本 APP

我們準備測試我們的應用程式了。 我們需要獲取 Istio Ingress Gateway 的 IP 地址:

$ istio-ingressgateway   NodePort   10.109.111.38   <none>        15020:32105/TCP,80:30824/TCP,443:32478/TCP,31400:32419/TCP,15443:30884/TCP   4d

當我們在瀏覽器中開啟NodePort-IP時,我們應該看到 hellohttp ASP.NET Core 應用程式:

DestinationRule

在某些時候,您希望將應用更新為新版本。 也許你想分割兩個版本之間的流量。 您需要建立一個 DestinationRule 來定義是哪些版本,在 Istio 中稱為 subset。

首先,更新 hellohttp-deploy.yaml 檔案以使用 v2 版本的容器定義 v2 的 deployment:

apiVersion: v1
kind: Service
metadata:
  name: hellohttp-service
  labels:
    app: hellohttp
spec:
  ports:
    - port: 80
      name: http
  selector:
    app: hellohttp
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hellohttp-v1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: hellohttp
      version: v1
  template:
    metadata:
      labels:
        app: hellohttp
        version: v1
    spec:
      containers:
        - name: hellohttp
          image: busybox 
          imagePullPolicy: IfNotPresent
          command: ["/bin/sh", "-c", "echo 'hello httpd v1' > /var/www/index.html; httpd -f -p 80 -h /var/www/"]
          ports:
            - containerPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hellohttp-v2
spec:
  replicas: 1
  selector:
    matchLabels:
      app: hellohttp
      version: v2
  template:
    metadata:
      labels:
        app: hellohttp
        version: v2
    spec:
      containers:
        - name: hellohttp
          image: busybox 
          imagePullPolicy: IfNotPresent
          command: ["/bin/sh", "-c", "echo 'hello httpd v2' > /var/www/index.html; httpd -f -p 80 -h /var/www/"]
          ports:
            - containerPort: 80

建立新的 Deployment:

$ kubectl apply -f hellohttp-deploy.yaml
service "hellohttp-service" unchanged
deployment.extensions "hellohttp-v1" unchanged
deployment.extensions "hellohttp-v2" created

如果使用 EXTERNAL-IP 重新整理瀏覽器,您將看到應用程式的 v1 和 v2 版本交替出現:

這是符合預期的,因為兩個版本都暴露在相同的 Kubernetes 服務之後:hellohttp-service。

如果您想將服務僅指向 v2,該怎麼辦? 這可以通過在 VirtualService 中指定 subset 來完成,但我們需要首先在 DestinationRules 中定義這些 subset。 DestinationRule 本質上是將標籤對映到 Istio 的 subset。

建立一個hellohttp-destinationrule.yaml檔案:

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: hellohttp-destinationrule
spec:
  host: hellohttp-service
  trafficPolicy:
    tls:
      mode: ISTIO_MUTUAL
  subsets:
    - name: v1
      labels:
        version: v1
    - name: v2
      labels:
        version: v2

建立 DestinationRule:

$ kubectl apply -f hellohttp-destinationrule.yaml
destinationrule.networking.istio.io "hellohttp-destinationrule" created

現在你可以從 VirtualService 來引用 v2 subset:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: hellohttp-virtualservice
spec:
  hosts:
    - "hellohttp.com"
  gateways:
  - hellohttp-gateway
  http:
  - route:
    - destination:
        host: hellohttp-service
         subset: v2

更新 VirtualService:

$ kubectl apply -f hellohttp-virtualservice.yaml
virtualservice.networking.istio.io "hellohttp-virtualservice" configured

如果您現在繼續瀏覽 EXTERNAL-IP,您現在應該只能看到應用程式的 v2 版本。

測試AB版本更新

可以從 VirtualService 來引用v2 subset和v1 subset

[root@localhost hello-world]# cat hellohttp-virtualservice.yaml 
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: hellohttp-virtualservice
spec:
  hosts:
    - "hellohttp.com"
  gateways:
    - hellohttp-gateway
  http:
    - route:
        - destination:
            host: hellohttp-service
            subset: v1
          weight: 75
        - destination:
            host: hellohttp-service
            subset: v2
          weight: 25

上面的配置就是通過VirtualService 呼叫DestinationRule 裡面定義的host分類,來進行AB 版本的流量分離,進行AB 版本更新。

對於生產環境裡面的版本更新可以進行header 主機頭匹配,進行版本區分,基礎的金絲雀釋出更新版本就是這樣實現的。

ServiceEntry

我想在 Istio Routing 中提到的最後一件事是 ServiceEntry。 預設情況下,Istio 中的所有外部流量都被阻止。 如果要啟用外部流量,則需要建立 ServiceEntry 以列出為外部流量啟用的協議和主機。 我不會在這篇文章中展示一個例子,但你可以在這裡閱讀更多相關內容。

希望這篇文章對你有用! 如果您想了解更多資訊,可以使用 codelab 系列以下兩部分,其中所有這些概念和更多內容將在逐步的詳細教程中進行說明:

相關文章