istio流量管理


1 准備工作

1.1 在k8s部署istio

Istio在k8s集群內的部署很簡單,非生產要求的部署,可以直接在https://github.com/istio/istio/releases 下載最新的發布包,壓縮包里有供簡單部署的yaml文件:

$ for i in install/kubernetes/helm/istio-init/files/crd*yaml; do kubectl apply -f $i; done
$ kubectl apply -f install/kubernetes/istio-demo.yaml

注意,上面兩條指令會把主要的組件都給裝上,鏡像下載比較費力,內存消耗也比較大。沒個十幾G內存的同學請慎重。

部署完成后會新增一個命名空間istio-system:

$ kubectl get pods -n istio-system
NAME                                                           READY   STATUS      RESTARTS   AGE
grafana-f8467cc6-rbjlg                                         1/1     Running     0          1m
istio-citadel-78df5b548f-g5cpw                                 1/1     Running     0          1m
istio-cleanup-secrets-release-1.1-20190308-09-16-8s2mp         0/1     Completed   0          2m
istio-egressgateway-78569df5c4-zwtb5                           1/1     Running     0          1m
istio-galley-74d5f764fc-q7nrk                                  1/1     Running     0          1m
istio-grafana-post-install-release-1.1-20190308-09-16-2p7m5    0/1     Completed   0          2m
istio-ingressgateway-7ddcfd665c-dmtqz                          1/1     Running     0          1m
istio-pilot-f479bbf5c-qwr28                                    2/2     Running     0          1m
istio-policy-6fccc5c868-xhblv                                  2/2     Running     2          1m
istio-security-post-install-release-1.1-20190308-09-16-bmfs4   0/1     Completed   0          2m
istio-sidecar-injector-78499d85b8-x44m6                        1/1     Running     0          1m
istio-telemetry-78b96c6cb6-ldm9q                               2/2     Running     2          1m
istio-tracing-69b5f778b7-s2zvw                                 1/1     Running     0          1m
kiali-99f7467dc-6rvwp                                          1/1     Running     0          1m
prometheus-67cdb66cbb-9w2hm                                    1/1     Running     0          1m

1.2 istio自動注入

在介紹istio原理時有提到,istio會在每一個被管理的pod里注入一個sidecar容器envoy。那么istio是如何完成注入的呢,主要有2種方式:

  1. 手動注入:如下命令,在kubectl前先執行istioctl手動注入
$ istioctl kube-inject -f <your-app-spec>.yaml | kubectl apply -f -
  1. 自動注入:運用k8s的admission-control完成自動注入
    • kube-apiserver配置文件的admission-control參數,增加MutatingAdmissionWebhook 以及 ValidatingAdmissionWebhook 兩項
    • 給命名空間打上自動注入的標簽(下方我們允許istio在default空間自動注入,同時禁用了istio-system空間的自動注入):
$ kubectl label namespace default istio-injection=enabled
$ kubectl get namespace -L istio-injection
NAME              STATUS   AGE     ISTIO-INJECTION
default           Active   4d21h   enabled
ingress-nginx     Active   4d17h   
istio-system      Active   4d17h   disabled
kube-node-lease   Active   4d21h   
kube-public       Active   4d21h   
kube-system       Active   4d21h

后面的測試,我們以第2種方式自動注入envoy。
注:通過在deployment文件的annotations參數里加上sidecar.istio.io/inject: "false",可以覆蓋命名空間的標簽,禁止istio對本pod的自動注入

1.3 應用部署要求

Istio原生與k8s能夠無縫對接, istio的存在與否對應用程序本身來說,也是透明的。所以應用的部署,主要涉及的是yaml文件的修改完善:

  1. Deployment文件的要求:
    • 應帶有app和version標簽,這個主要是用來識別不同版本的pod。
    • Deployment應明確列出端口列表,istio會忽略未列出的端口。
    • 下方為data-product的示例,注釋部分為需要注意的地方:
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  # 建議pod名稱 = 應用名 + 版本號
  name: data-product-v4.0
  namespace: default
spec:
  template:
    metadata:
      annotations:
        sidecar.istio.io/inject: "true"
      namespace: default
      labels:
        # 標簽1:應用名稱
        app: data-product
        # 標簽2:版本號
        version: v4.0
    spec:
      containers:
      - name: data-product
        ports:
        # 需要Istio管理的端口號列表
        - containerPort: 50051
……
  1. Service文件的要求:
    • 需要指定端口的協議類型,否則默認按TCP協議處理:
---
apiVersion: v1
kind: Service
metadata:
  name: data-product
  namespace: default
  annotations:
    prometheus.io/scrape: 'true'
    prometheus.io/path:   /metrics
    prometheus.io/port:   '8081'
  labels:
    kubernetes.io/cluster-service: "true"
    kubernetes.io/name: "data-product"
spec:
  ports:
    - name: data-product
      port: 50051
      # 指定端口的協議類型為grpc
      name: grpc
      targetPort: 50051
  selector:
    app: data-product

2 負載均衡

$ kubectl get po -o wide
NAME                                     READY   STATUS  
business-product-6b954db744-cgn8k        2/2     Running 
business-product-6b954db744-dgm4x        2/2     Running 
business-product-6b954db744-n5c8p        2/2     Running 
business-product-v4.0-747ccffbb4-2p2z8   2/2     Running 
business-product-v4.0-747ccffbb4-4bh6z   2/2     Running 
business-product-v4.0-747ccffbb4-8kgqs   2/2     Running 
business-product-v4.0-747ccffbb4-hz6lb   2/2     Running 
business-product-v4.0-747ccffbb4-kgs6p   2/2     Running 
business-product-v4.0-747ccffbb4-rmmwg   2/2     Running 
business-product-v4.0-747ccffbb4-sspgr   2/2     Running 
business-product-v4.0-747ccffbb4-sz7ll   2/2     Running 
business-product-v4.0-747ccffbb4-vnd58   2/2     Running 
data-product-5bc9f7bb9c-dfchd            2/2     Running 
data-product-5bc9f7bb9c-tv8cj            2/2     Running 
data-product-5bc9f7bb9c-xzz7s            2/2     Running 

這里我們啟用了9個business-product的pod,和3個data-product的pod。上表有一個細節,每個pod里有2個容器,除了我們的應用程序容器外,還有一個就是enovy。

當pod被注入了enovy,並且是按1.3節的要求將port暴露給了istio,則Istio會自動接管pod的流量,實現負載均衡。
Business-product作為client與data-product server之間通過grpc通訊,這里我簡單修改了下business-product和data-product的邏輯,當收到請求時data-product會將自身和client的ip作為響應,而business-product則會在這個響應的基礎上再加上自己的ip。所以我們對business-product發起連續的請求,就能得到下方的一組ip對(其中每一行的第1個ip是client的ip,第2個是server檢測到的client ip,第3個是server的ip):

"ip": "172.30.27.8/24 - 127.0.0.1:43094 - 172.30.86.3/24",
"ip": "172.30.95.17/24 - 127.0.0.1:43120 - 172.30.86.3/24",
"ip": "172.30.27.9/24 - 127.0.0.1:43094 - 172.30.86.3/24",
"ip": "172.30.86.9/24 - 127.0.0.1:41662 - 172.30.27.3/24",
"ip": "172.30.95.15/24 - 127.0.0.1:43094 - 172.30.86.3/24",
"ip": "172.30.86.7/24 - 127.0.0.1:41712 - 172.30.27.3/24",
"ip": "172.30.27.11/24 - 127.0.0.1:43872 - 172.30.95.7/24",
"ip": "172.30.86.8/24 - 127.0.0.1:41712 - 172.30.27.3/24",
"ip": "172.30.95.14/24 - 127.0.0.1:43944 - 172.30.95.7/24",

"ip": "172.30.27.8/24 - 127.0.0.1:43944 - 172.30.95.7/24",
"ip": "172.30.95.17/24 - 127.0.0.1:43872 - 172.30.95.7/24",
"ip": "172.30.27.9/24 - 127.0.0.1:43944 - 172.30.95.7/24",
"ip": "172.30.86.9/24 - 127.0.0.1:43094 - 172.30.86.3/24",
"ip": "172.30.95.15/24 - 127.0.0.1:43872 - 172.30.95.7/24",
"ip": "172.30.86.7/24 - 127.0.0.1:43120 - 172.30.86.3/24",
"ip": "172.30.27.11/24 - 127.0.0.1:41662 - 172.30.27.3/24",
"ip": "172.30.86.8/24 - 127.0.0.1:43094 - 172.30.86.3/24",
"ip": "172.30.95.14/24 - 127.0.0.1:41712 - 172.30.27.3/24",

"ip": "172.30.27.8/24 - 127.0.0.1:41712 - 172.30.27.3/24",
"ip": "172.30.95.17/24 - 127.0.0.1:41712 - 172.30.27.3/24",
"ip": "172.30.27.9/24 - 127.0.0.1:41712 - 172.30.27.3/24",
"ip": "172.30.86.9/24 - 127.0.0.1:43872 - 172.30.95.7/24",
"ip": "172.30.95.15/24 - 127.0.0.1:41662 - 172.30.27.3/24",
"ip": "172.30.86.7/24 - 127.0.0.1:43944 - 172.30.95.7/24",
"ip": "172.30.27.11/24 - 127.0.0.1:43094 - 172.30.86.3/24",
"ip": "172.30.86.8/24 - 127.0.0.1:43872 - 172.30.95.7/24",
"ip": "172.30.95.14/24 - 127.0.0.1:43120 - 172.30.86.3/24",

我們連續發起了27次請求,不出所料,client的9個Pod分別被分配到了3次請求,接下來我們看看server是否負載上了:
• 27次請求中,3個server的pod均被分配了9次,負載是均衡的。
• 進一步分析,同一個client,每次被分配到的server是不同的,這是因為envoy代理在中間攔了一手,這正是負載的實現原理。
• 再進一步分析,server檢測到的客戶端ip,是127.0.0.1的本地ip,這個連接其實是面向本pod的envoy的。

所以我們可以認為envoy為我們在網格內建立起了一個服務之間的連接池,從而實現了負載均衡。

最后,我們再回過頭來對比針對grpc長連接的情況,k8s的負載和istio的負載的差異:
• K8s的負載是在連接發起時實現的,比如我們的例子里有9個客戶端,則調用了9次負載算法,次數太少,負載未必是均衡的。
• 基於連接的負載,一旦pod異常退出,連接斷開,重新建立起的連接有更大的概率被集中到個別沒斷開的pod上,這會加劇負載的不均衡性。
• Istio維護的負載是在每次通訊的時候完成的動態調度,不受連接數的限制,在大量通訊的情況下,istio能夠保證負載的均衡。

3 流量遷移:金絲雀發布

上一章envoy已經接管了我們的流量,接下來就可以讓envoy為我們做更細致的流量管理的動作了。本章我們會通過簡單的配置來實現金絲雀發布。

假設我們已經發布了data-product的v3.0版本,現在我們有一個v4.0的版本需要更新上去。版本發布過程中我們期望先將10%的流量導入到v4.0版本,剩余90%的流量還是由v3.0版本來處理。待v4.0版本運行穩定無異常后,我們再將100%的流量遷移到v4.0版本。

3.1 發布應用

回顧1.3章節,我們在發布data-product時,需要deployment的名稱區分版本(想想為啥?)。為實現兩個版本的平滑過渡、金絲雀發布,我們需要同時發布兩個版本的deployment:

$ kubectl get deployment 
NAME                READY   UP-TO-DATE   AVAILABLE   AGE
business-product    3/3     3            3           5h13m
data-product        3/3     3            3           4h30m
data-product-v4.0   3/3     3            3           4h16m

上表data-product是v3.0版本的(這里我命名不規范),data-product-v4.0是v4.0版本的,各有3個pod在運行。

3.2 創建目標規則:DestinationRule

目標規則(DestinationRule)是用來定義流量應該由哪些pod來接收的。下方的yaml文件我們創建了一個DestinationRule,標識發往data-product的流量,由data-product的v3.0和v4.0兩個版本來接收。這個yaml文件的關鍵是subsets字段的兩個labels,這對應着1.3和3.1節的應用版本。

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: data-product
spec:
  host: data-product
  subsets:
  - name: v3
    labels:
      version: v3.0
  - name: v4
    labels:
      version: v4.0

3.3 創建虛擬服務:VirtualService

Service和virtualservice的目標是一致的,都是將流量導向后端的pod。區別是由istio引入的virtualservice可以完成更細致的流量划分。
下方的yaml文件,我們將10%的流量導向了v4版本,90%的流量導向v3版本。這個文件的關鍵是subset字段,這對應於DestinationRule的subsets;以及weight權重,按百分比划分。

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: data-product
spec:
  hosts:
  - data-product
  http:
  - route:
    - destination:
        host: data-product
        subset: v3
      weight: 90
    - destination:
        host: data-product
        subset: v4
      weight: 10

3.4 驗證和結語

限於篇幅,驗證數據就不貼了。平均每10次請求,會有1次導向v4,另外9次還在v3。
不難理解,運行一段時間覺得v4版本ok了,我們可以將權重配比調整到50%,75%,直到100%的流量都導向v4。之后v3版本的deployment就可以刪了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM