使用 Istio 實現(xiàn)非侵入流量治理
現(xiàn)在最火的后端架構無疑是微服務了,微服務將之前的單體應用拆分成了許多獨立的服務應用,每個微服務都是獨立的,好處自然很多,但是隨著應用的越來越大,微服務暴露出來的問題也就隨之而來了,微服務越來越多,管理越來越麻煩,特別是要你部署一套新環(huán)境的時候,你就能體會到這種痛苦了,隨之而來的服務發(fā)現(xiàn)、負載均衡、Trace跟蹤、流量管理、安全認證等等問題。如果從頭到尾完成過一套微服務框架的話,你就會知道這里面涉及到的東西真的非常多。當然隨著微服務的不斷發(fā)展,微服務的生態(tài)也不斷完善,最近新一代的微服務開發(fā)就悄然興起了,那就是服務網(wǎng)格/Service Mesh。
什么是Service Mesh?
Service Mesh 是一個非常新的名詞,最早是2016年由開發(fā) Linkerd 的 Buoyant 公司提出的,伴隨著 Linkerd 的傳入, Service Mesh 的概念也慢慢進入國內(nèi)技術社區(qū),現(xiàn)在主流的叫法都叫:服務網(wǎng)格。
- 服務網(wǎng)格是一個用于處理服務間通信的基礎設施層,它負責為構建復雜的云原生應用傳遞可靠的網(wǎng)絡請求。在實踐中,服務網(wǎng)格通常實現(xiàn)為一組和應用程序部署在一起的輕量級的網(wǎng)絡代理,但對應用程序來說是透明的。
要理解網(wǎng)格的概念,就得從服務的部署模型說起:
單個服務調(diào)用,表現(xiàn)為sidecar

單個服務調(diào)用
Service Mesh 的部署模型,先看單個的,對于一個簡單請求,作為請求發(fā)起者的客戶端應用實例,會首先用簡單方式將請求發(fā)送到本地的 Service Mesh 實例。這是兩個獨立進程,他們之間是遠程調(diào)用。
Service Mesh 會完成完整的服務間調(diào)用流程,如服務發(fā)現(xiàn)負載均衡,最后將請求發(fā)送給目標服務,這表現(xiàn)為 Sidecar 方式。
部署多個服務,表現(xiàn)為通訊層

多個服務調(diào)用
多個服務調(diào)用的情況,在這個圖上我們可以看到 Service Mesh 在所有的服務的下面,這一層被稱之為服務間通訊專用基礎設施層。Service Mesh 會接管整個網(wǎng)絡,把所有的請求在服務之間做轉發(fā)。在這種情況下,我們會看到上面的服務不再負責傳遞請求的具體邏輯,只負責完成業(yè)務處理。服務間通訊的環(huán)節(jié)就從應用里面剝離出來,呈現(xiàn)出一個抽象層。
有大量服務,表現(xiàn)為網(wǎng)絡
大量服務調(diào)用
如果有大量的服務,就會表現(xiàn)出來網(wǎng)格,圖中左邊綠色方格是應用,右邊藍色的方框是 Service Mesh,藍色之間的線條是表示服務之間的調(diào)用關系。Sidecar 之間的連接就會形成一個網(wǎng)絡,這個就是服務網(wǎng)格名字的由來,這個時候代理體現(xiàn)出來的就和前面的 Sidecar 不一樣了,形成網(wǎng)狀。
首先第一個,服務網(wǎng)格是抽象的,實際上是抽象出了一個基礎設施層,在應用之外。其次,功能是實現(xiàn)請求的可靠傳遞。部署上體現(xiàn)為輕量級的網(wǎng)絡代理。最后一個關鍵詞是,對應用程序透明。
Service mesh
大家注意看,上面的圖中,網(wǎng)絡在這種情況下,可能不是特別明顯。但是如果把左邊的應用程序去掉,現(xiàn)在只呈現(xiàn)出來 Service Mesh 和他們之間的調(diào)用,這個時候關系就會特別清晰,就是一個完整的網(wǎng)絡。這是 Service Mesh 定義當中一個非常重要的關鍵點,和 Sidecar 不相同的地方:不再將代理視為單獨的組件,而是強調(diào)由這些代理連接而形成的網(wǎng)絡。在 Service Mesh 里面非常強調(diào)代理連接組成的網(wǎng)絡,而不像 Sidecar 那樣看待個體。
現(xiàn)在我們基本上把 Service Mesh 的定義介紹清楚了,大家應該可以大概了解什么是 Service Mesh 了?,F(xiàn)在實現(xiàn) Service Mesh 的開源方案有很多,比如 Linkerd、Istio 等,當然目前最流行最火熱的還是要數(shù) Istio 了,記下來我們就來開始講解 Istio 的使用。
什么是Istio?
Istio 解決了開發(fā)人員和運維在分布式或微服務架構方面面臨的挑戰(zhàn),無論是從頭開始構建還是將現(xiàn)有應用程序遷移到云原生環(huán)境下,Istio 都可以提供幫助。
通過在部署的每個應用程序中添加代理 sidecar,Istio 允許您將應用程序感知流量管理、令人難以置信的可觀察性和強大的安全功能編程到您的網(wǎng)絡中。
Istio
Istio 是一個開源服務網(wǎng)格,它透明地分層到現(xiàn)有的分布式應用程序上。Istio 強大的特性提供了一種統(tǒng)一和更有效的方式來保護、連接和監(jiān)視服務。Istio 是實現(xiàn)負載平衡、服務到服務身份驗證和監(jiān)視的路徑——只需要很少或不需要更改服務代碼。它強大的控制平面帶來了重要的特點,包括:
- 使用 TLS 加密、強身份認證和授權的集群內(nèi)服務到服務的安全通信
- 自動負載均衡的 HTTP, gRPC, WebSocket,和 TCP 流量
- 通過豐富的路由規(guī)則、重試、故障轉移和故障注入對流量行為進行細粒度控制
- 一個可插入的策略層和配置 API,支持訪問控制、速率限制和配額
- 對集群內(nèi)的所有流量(包括集群入口和出口)進行自動度量、日志和跟蹤
Istio 是為可擴展性而設計的,可以處理不同范圍的部署需求。Istio 的控制平面運行在 Kubernetes 上,您可以將部署在該集群中的應用程序添加到您的網(wǎng)格中,將網(wǎng)格擴展到其他集群,甚至連接 VM 或運行在 Kubernetes 之外的其他端點。
架構
Istio 有兩個組成部分:數(shù)據(jù)平面和控制平面。
數(shù)據(jù)平面由一組智能代理(Envoy)組成,被部署為 Sidecar。這些代理負責協(xié)調(diào)和控制微服務之間的所有網(wǎng)絡通信。它們還收集和報告所有網(wǎng)格流量的遙測數(shù)據(jù)。
服務網(wǎng)格使用代理攔截所有的網(wǎng)絡流量,允許根據(jù)您設置的配置提供廣泛的應用程序感知功能。代理與您在集群中啟動的每個服務一起部署,或者與運行在虛擬機上的服務一起運行。
控制平面管理并配置代理來進行流量路由。
在使用 Istio 之前服務與服務之間通信如下圖所示:
使用 Istio 之前
使用 Istio 之后服務與服務之間通信則通過 Envoy 代理進行:
使用 Istio 之后
下圖則是 Istio 每個平面的不同組件的架構:
Istio 架構
Envoy
Istio 默認使用 Envoy 代理的擴展版本,Envoy 是用 C++ 開發(fā)的高性能代理,用于協(xié)調(diào)服務網(wǎng)格中所有服務的入站和出站流量。Envoy 代理是唯一與數(shù)據(jù)平面流量交互的 Istio 組件。
Envoy 代理被部署為服務的 Sidecar,在邏輯上為服務增加了 Envoy 的許多內(nèi)置特性,例如:
- 動態(tài)服務發(fā)現(xiàn)
- 負載均衡
- TLS 校驗
- HTTP/2 與 gRPC 代理
- 熔斷器
- 健康檢查
- 基于百分比流量分割的分階段發(fā)布
- 故障注入
- 豐富的指標
這種 Sidecar 部署允許 Istio 可以執(zhí)行策略決策,并提取豐富的遙測數(shù)據(jù),接著將這些數(shù)據(jù)發(fā)送到監(jiān)視系統(tǒng)以提供有關整個網(wǎng)格行為的信息。
Sidecar 代理模型還允許你向現(xiàn)有的應用添加 Istio 功能,而不需要重新設計架構或重寫代碼。
由 Envoy 代理啟用的一些 Istio 的功能和任務包括:
- 流量控制功能:通過豐富的 HTTP、gRPC、WebSocket 和 TCP 流量路由規(guī)則來執(zhí)行細粒度的流量控制。
- 網(wǎng)絡彈性特性:重試設置、故障轉移、熔斷器和故障注入。
- 安全性和身份認證特性:執(zhí)行安全性策略,并強制實行通過配置 API 定義的訪問控制和速率限制。
- 基于 WebAssembly 的可插拔擴展模型,允許通過自定義策略執(zhí)行和生成網(wǎng)格流量的遙測。
Istiod
組件 Istiod 提供服務發(fā)現(xiàn)、配置和證書管理。
Istiod 將控制流量行為的高級路由規(guī)則轉換為 Envoy 特定的配置,并在運行時將其傳播給 Sidecar。Pilot 提取了特定平臺的服務發(fā)現(xiàn)機制,并將其配置為一種標準格式,任何符合 Envoy API 的 Sidecar 都可以使用。
Istio 可以支持發(fā)現(xiàn)多種環(huán)境,如 Kubernetes 或 VM。
你可以使用 Istio 流量管理 API 讓 Istiod 重新構造 Envoy 的配置,以便對服務網(wǎng)格中的流量進行更精細的控制。
Istiod 通過內(nèi)置的身份和憑證管理進行安全管理,你可以使用 Istio 來升級服務網(wǎng)格中未加密的流量,可以使用 Istio 的授權功能控制誰可以訪問你的服務。
Istiod 充當證書授權(CA),并生成證書以允許在數(shù)據(jù)平面中進行安全的 mTLS 通信。
安裝
接下來我們將介紹如何在 Kubernetes 集群中安裝 Istio,這里我們使用的是最新的 1.10.3 版本。
下面的命令可以下載指定的 1.10.3 版本的 Istio:
- ➜ ~ curl -L https://istio.io/downloadIstio | ISTIO_VERSION=1.10.3 sh -
如果安裝失敗,可以用手動方式進行安裝,在 GitHub Release 頁面獲取對應系統(tǒng)的下載地址:
- # 注意選擇和自己操作系統(tǒng)匹配的文件
- ➜ ~ wget https://github.com/istio/istio/releases/download/1.10.3/istio-1.10.3-linux-amd64.tar.gz
- ➜ ~ tar -xzf istioctl-1.10.3-linux-amd64.tar.gz
- # 進入到 istio 解壓的目錄
- ➜ ~ cd istio-1.10.3 && ls -la
- total 48
- drwxr-x---@ 9 ych staff 288 Jul 15 13:32 .
- drwx---r-x@ 482 ych staff 15424 Jul 20 14:17 ..
- -rw-r--r--@ 1 ych staff 11348 Jul 15 13:32 LICENSE
- -rw-r--r--@ 1 ych staff 5866 Jul 15 13:32 README.md
- drwxr-x---@ 3 ych staff 96 Jul 15 13:32 bin
- -rw-r-----@ 1 ych staff 854 Jul 15 13:32 manifest.yaml
- drwxr-xr-x@ 5 ych staff 160 Jul 15 13:32 manifests
- drwxr-xr-x@ 21 ych staff 672 Jul 15 13:32 samples
- drwxr-xr-x@ 5 ych staff 160 Jul 15 13:32 tools
其中 samples/ 目錄下面是一些示例應用程序,bin/ 目錄下面的 istioctl 是 Istio 的 CLI 工具,可以將該 bin/ 目錄加入到 PATH 路徑之下,也可以直接拷貝到某個 PATH 目錄下去:
- ➜ ~ cp bin/istioctl /usr/local/bin/istioctl
- ➜ ~ istioctl version
- no running Istio pods in "istio-system"
- 1.10.3
安裝 istio 的工具和文件準備好過后,直接執(zhí)行如下所示的安裝命令即可,這里我們采用的是 demo 配置組合的形式,這是因為它包含了一組專為測試準備的功能集合,另外還有用于生產(chǎn)或性能測試的配置組合。
- ➜ ~ istioctl install --set profile=demo -y
- Detected that your cluster does not support third party JWT authentication. Falling back to less secure first party JWT. See https://istio.io/v1.10/docs/ops/best-practices/security/#configure-third-party-service-account-tokens for details.
- ! values.global.jwtPolicy is deprecated; use Values.global.jwtPolicy=third-party-jwt. See http://istio.io/latest/docs/ops/best-practices/security/#configure-third-party-service-account-tokens for more information instead
- ✔ Istio core installed
- ✔ Istiod installed
- ✔ Ingress gateways installed
- ✔ Egress gateways installed
- ✔ Installation complete
安裝完成后我們可以查看 istio-system 命名空間下面的 Pod 運行狀態(tài):
- ➜ ~ kubectl get pods -n istio-system
- NAME READY STATUS RESTARTS AGE
- istio-egressgateway-5c8d96c9b5-6d5g9 1/1 Running 0 4m6s
- istio-ingressgateway-6bcfd457f9-wpj7w 1/1 Running 0 4m6s
- istiod-775bcf58f7-v6jl2 1/1 Running 0 5m4s
如果都是 Running 狀態(tài)證明 istio 就已經(jīng)安裝成功了。然后我們還可以給 namespace 添加一個 isito-injection=enabled 的 label 標簽,指示 Istio 在部署應用的時候,可以自動注入 Envoy Sidecar 代理,比如這里我們給 default 命名空間注入自動標簽:
- ➜ ~ kubectl label namespace default istio-injection=enabled
- namespace/default labeled
部署示例應用
然后我們可以來安裝官方提供的一個非常經(jīng)典的 Bookinfo 應用示例,這個示例部署了一個用于演示多種 Istio 特性的應用,該應用由四個單獨的微服務構成,這個應用模仿在線書店的一個分類,顯示一本書的信息。頁面上會顯示一本書的描述、書籍的 ISBN、頁數(shù)等信息,以及關于這本書的一些評論。
Bookinfo 應用分為四個單獨的微服務:
- productpage:這個微服務會調(diào)用 details 和 reviews 兩個微服務,用來生成頁面。
- details:這個微服務中包含了書籍的信息。
- reviews:這個微服務中包含了書籍相關的評論,它還會調(diào)用 ratings 微服務。
- ratings:這個微服務中包含了由書籍評價組成的評級信息。
reviews 微服務有 3 個版本:
- v1 版本不會調(diào)用 ratings 服務。
- v2 版本會調(diào)用 ratings 服務,并使用 1 到 5 個黑色星形圖標來顯示評分信息。
- v3 版本會調(diào)用 ratings 服務,并使用 1 到 5 個紅色星形圖標來顯示評分信息。
下圖可以用來說明我們這個示例應用的整體架構:
沒有使用 Istio 之前的架構
Bookinfo 應用中的幾個微服務是由不同的語言編寫而成的,這些服務對 Istio 并無依賴,但是構成了一個有代表性的服務網(wǎng)格的例子:它由多個服務、多個語言構成,并且 reviews 服務具有多個版本。
我們要在 Istio 中運行這個應用,不需要對應用本身做任何改變,只要簡單的在 Istio 環(huán)境中對服務進行配置和運行,也就是把 Envoy sidecar 注入到每個服務之中。最終的部署結果將如下圖所示:
istio bookinfo
所有的微服務都和 Envoy sidecar 集成在一起,被集成服務所有的出入流量都被 sidecar 所劫持,這樣就為外部控制準備了所需的 Hook,然后就可以利用 Istio 控制平面為應用提供服務路由、遙測數(shù)據(jù)收集以及策略實施等功能。
進入上面的 Istio 安裝目錄,執(zhí)行如下命令:
- ➜ ~ kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml
- service/details created
- serviceaccount/bookinfo-details created
- deployment.apps/details-v1 created
- service/ratings created
- serviceaccount/bookinfo-ratings created
- deployment.apps/ratings-v1 created
- service/reviews created
- serviceaccount/bookinfo-reviews created
- deployment.apps/reviews-v1 created
- deployment.apps/reviews-v2 created
- deployment.apps/reviews-v3 created
- service/productpage created
- serviceaccount/bookinfo-productpage created
- deployment.apps/productpage-v1 created
如果在安裝過程中禁用了 Sidecar 自動注入功能而選擇手動注入 Sidecar,請在部署應用之前可以使用 istioctl kube-inject 命令來注入 sidecar 容器。
- ➜ ~ kubectl apply -f <(istioctl kube-inject -f samples/bookinfo/platform/kube/bookinfo.yaml)
這里我們部署的 bookinfo.yaml 資源清單文件就是普通的 Kubernetes 的 Deployment 和 Service 的 yaml 文件,使用 istioctl kube-inject 或者配置自動注入后會在這個文件的基礎上向其中的 Deployment 追加一個鏡像為 docker.io/istio/proxyv2:1.10.3 的 sidecar 容器,上面的命令會啟動全部的四個服務,其中也包括了 reviews 服務的三個版本(v1、v2 以及 v3)。
過一會兒就可以看到如下 service 和 pod 啟動:
- ➜ ~ kubectl get pods
- NAME READY STATUS RESTARTS AGE
- details-v1-79f774bdb9-mqpzm 2/2 Running 0 25m
- productpage-v1-6b746f74dc-xjzgh 2/2 Running 0 25m
- ratings-v1-b6994bb9-9j9dq 2/2 Running 0 25m
- reviews-v1-545db77b95-wwhkq 2/2 Running 0 25m
- reviews-v2-7bf8c9648f-rh6b9 2/2 Running 0 25m
- reviews-v3-84779c7bbc-bsld9 2/2 Running 0 25m
- ➜ ~ kubectl get svc
- NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
- details ClusterIP 10.99.137.40 <none> 9080/TCP 26m
- kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 57d
- productpage ClusterIP 10.111.10.219 <none> 9080/TCP 26m
- ratings ClusterIP 10.105.20.158 <none> 9080/TCP 26m
- reviews ClusterIP 10.105.81.29 <none> 9080/TCP 26m
現(xiàn)在應用的服務都部署成功并啟動了,如果我們需要在集群外部訪問,就需要添加一個 istio gateway,gateway 相當于 k8s 的 ingress controller 和 ingress,它為 HTTP/TCP 流量配置負載均衡,通常在服務網(wǎng)格邊緣作為應用的 ingress 流量管理。
創(chuàng)建一個 Ingress gateway:
- ➜ ~ kubectl apply -f samples/bookinfo/networking/bookinfo-gateway.yaml
- gateway.networking.istio.io/bookinfo-gateway created
- virtualservice.networking.istio.io/bookinfo created
驗證 gateway 是否創(chuàng)建成功:
- ➜ ~ kubectl get gateway
- NAME AGE
- bookinfo-gateway 15s
要想訪問這個應用,這里我們需要更改下 istio 提供的 istio-ingressgateway 這個 Service 對象,默認是 LoadBalancer 類型的服務:
- ➜ ~ kubectl get svc -n istio-system
- NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
- istio-egressgateway ClusterIP 10.103.199.67 <none> 80/TCP,443/TCP 47m
- istio-ingressgateway LoadBalancer 10.100.241.242 <pending> 15021:31548/TCP,80:31529/TCP,443:30433/TCP,31400:31175/TCP,15443:32533/TCP 47m
- istiod ClusterIP 10.107.77.147 <none> 15010/TCP,15012/TCP,443/TCP,15014/TCP
LoadBalancer 類型的服務,實際上是用來對接云服務廠商的,如果我們沒有對接云服務廠商的話,可以將這里類型改成 NodePort,但是這樣當訪問我們的服務的時候就需要加上 nodePort 端口了:
- ➜ ~ kubectl edit svc istio-ingressgateway -n istio-system
- ......
- type: NodePort # 修改成 NodePort 類型
- status:
- loadBalancer: {}
這樣我們就可以通過 http://
bookinfo demo
刷新頁面可以看到 Book Reviews 發(fā)生了改變(紅色、黑色的星形或者沒有顯示),因為每次請求會被路由到到了不同的 Reviews 服務版本去。
儀表盤
上面我們是安裝的 Istio 的核心組件,此外 Istio 還和一些遙測應用做了集成,遙測能幫助我們了解服務網(wǎng)格的結構、展示網(wǎng)絡的拓撲結構、分析網(wǎng)格的健康狀態(tài)等,對于分布式微服務應用也是非常重要的。
我們可以使用下面的命令來部署 Kiali、Prometheus、Grafana 以及 Jaeger:
- ➜ ~ kubectl apply -f samples/addons
- # 如果出現(xiàn)了錯誤,則可以重新執(zhí)行上面的命令
- ➜ ~ kubectl get pods -n istio-system
- NAME READY STATUS RESTARTS AGE
- grafana-f766d6c97-4888w 1/1 Running 0 3m57s
- istio-egressgateway-5c8d96c9b5-6d5g9 1/1 Running 0 60m
- istio-ingressgateway-6bcfd457f9-wpj7w 1/1 Running 0 60m
- istiod-775bcf58f7-v6jl2 1/1 Running 0 61m
- jaeger-7f78b6fb65-bj8pm 1/1 Running 0 3m56s
- kiali-85c8cdd5b5-b5zv4 1/1 Running 0 3m55s
- prometheus-54b5dcf6bf-wjkpl 3/3 Running 0 3m48s
上面幾個組件部署完成后我們就可以查看前面 Bookinfo 示例應用的遙測信息了,比如可以使用下面的命令訪問 Kiali:
- ➜ ~ istioctl dashboard kiali
在左側的導航菜單,選擇 Graph ,然后在 Namespace 下拉列表中,選擇 default 。Kiali 儀表板展示了網(wǎng)格的概覽、以及 Bookinfo 示例應用的各個服務之間的關系。它還提供過濾器來可視化流量的流動。
kiali dashboard
至此,整個 Istio 和 Bookinfo 示例應用就安裝并驗證成功了,現(xiàn)在就可以使用這一應用來體驗 Istio 的特性了,其中包括了流量的路由、錯誤注入、速率限制等特性。
目前搭建 Bookinfo 應用我們只用到了下面兩個資源文件:
- samples/bookinfo/platform/kube/bookinfo.yaml
- samples/bookinfo/networking/bookinfo-gateway.yaml
前者就是通常的 Kubernetes 定義的 Deployment 和 Service 的資源清單文件,只是在部署時使用 istioctl kube-inject(或者通過對命名空間打上自動注入的標簽)對這個文件定義的 Pod 注入了 sidecar 代理,后者定義了這個應用的外部訪問入口 gateway,以及應用內(nèi)部 productpage 服務的 VirtualService 規(guī)則,而其他內(nèi)部服務的訪問規(guī)則還沒有被定義。
現(xiàn)在訪問應用界面并刷新,會看到 Reviews 有時不會顯示評分,有時候會顯示不同樣式的評分,這是因為后面有3個不同的 Reviews 服務版本,而沒有配置該服務的路由規(guī)則的情況下,該服務的幾個實例會被隨機訪問到,有的版本服務會進一步調(diào)用 Ratings 服務,有的不會。
這里我們會了解 Istio 中兩個非常重要的流量管理的資源對象:
- VirtualService(虛擬服務):用來在 Istio 中定義路由規(guī)則,控制流量路由到服務上的各種行為。
- DestinationRule(目標規(guī)則):虛擬服務視定義將流量如何路由到指定目標地址,然后使用目標規(guī)則來配置該目標的流量,在評估虛擬服務路由規(guī)則之后,目標規(guī)則將應用于流量的真實目標地址。
VirtualService
虛擬服務(VirtualService)和目標規(guī)則(Destination Rule)是 Istio 流量路由功能的關鍵對象,虛擬服務配置如何在 Istio 內(nèi)將請求路由到服務,每個虛擬服務包含一組路由規(guī)則,Istio 會按定義的順序來評估它們,Istio 將每個指定的請求匹配到虛擬服務指定的實際目標地址。在網(wǎng)格中可以有多個虛擬服務,也可以沒有。
使用虛擬服務,你可以為一個或多個主機名指定流量行為,在虛擬服務中使用路由規(guī)則,告訴 Envoy 如何發(fā)送虛擬服務的流量到合適的目標,路由目標地址可以是同一服務的不同版本,也可以是完全不同的服務。
一個典型的使用場景是將流量發(fā)送到指定服務的不同版本??蛻舳藭⑻摂M服務視為一個單一實體,將請求發(fā)送至虛擬服務主機,然后 Envoy 根據(jù)虛擬服務規(guī)則把流量路由到不同的版本。比如把 20% 的流量路由到新版本 或 將這些用戶的請求路由到版本 2,可以創(chuàng)建一個金絲雀發(fā)布,然后逐步增加發(fā)送到新版本服務的流量百分比。流量路由完全獨立于實例部署,所以實現(xiàn)新版本服務的實例可以根據(jù)流量的負載來伸縮,完全不影響流量路由。相比之下,Kubernetes 則只支持基于實例縮放的流量分發(fā),這會更復雜。
如下所示我們定義一個虛擬服務,根據(jù)請求是否來自某個特定用戶,把它們路由到服務的不同版本去。
- apiVersion: networking.istio.io/v1alpha3
- kind: VirtualService
- metadata:
- name: reviews
- spec:
- hosts: # 列出VirtualService的hosts,可以是IP、DNS名稱、FQDN或*
- - reviews
- http: # 在下面配置VirtualService的路由規(guī)則,指定符合哪些規(guī)則的流量打到哪些Destination,支持HTTP/1.1,HTTP2,及gRPC等協(xié)議
- - match: # 指定具體的匹配規(guī)則
- - headers:
- end-user:
- exact: jason
- route:
- - destination: # 指定滿足規(guī)則后將流量打到哪個具體的Destination
- host: reviews
- subset: v2
- - route: # 流量規(guī)則按從上到下的優(yōu)先級去匹配,若不滿足上述規(guī)則時,進入該默認規(guī)則
- - destination:
- host: reviews
- subset: v3
我們使用 hosts 字段列舉虛擬服務的主機——即用戶指定的目標或是路由規(guī)則設定的目標,這是客戶端向服務發(fā)送請求時使用的一個或多個地址。
- hosts:
- - reviews
虛擬服務主機名可以是 IP 地址、DNS 名稱,比如 Kubernetes Service 的短名稱,隱式或顯式地指向一個完全限定域名(FQDN)。也可以使用通配符(“*”)前綴,創(chuàng)建一組匹配所有服務的路由規(guī)則。虛擬服務的 hosts 字段實際上不必是 Istio 服務注冊的一部分,它只是虛擬的目標地址,這樣可以為沒有路由到網(wǎng)格內(nèi)部的虛擬主機建模。
然后接著就是路由規(guī)則的定義,這里通過 http 字段來定義虛擬服務的路由規(guī)則,用來描述匹配條件和路由行為,它們把 HTTP/1.1、HTTP2 和 gRPC 等流量發(fā)送到 hosts 字段指定的目標,一條路由規(guī)則包含了指定的請求要流向哪個目標地址,可以有0個或多個匹配條件。
比如上面示例中的第一個路由規(guī)則有一個條件,所以使用 match 字段開始定義,我們希望該路由應用于來自 jason 用戶的所有請求,所以使用 headers、end-user 和 exact 字段來匹配合適的請求。
- - match:
- - headers:
- end-user:
- exact: jason
然后后面的 route 部分的 destination 字段指定了符合該條件的流量的實際目標地址,與虛擬服務的 hosts 不同,destination 的 host 必須是存在于 Istio 服務注冊中心的實際目標地址,否則 Envoy 不知道該將請求發(fā)送到哪里??梢允且粋€有代理的服務網(wǎng)格,或者是一個通過服務入口被添加進來的非網(wǎng)格服務。本示例運行在 Kubernetes 環(huán)境中,host 名為一個 Kubernetes 服務名:
- route:
- - destination:
- host: reviews # Kubernetes Service 短名稱
- subset: v2
此外 destination 下面還指定了 Kubernetes 服務的子集,將符合此規(guī)則條件的請求轉入其中,比如這里我們使用的子集名稱是 v2,我們會在目標規(guī)則中看到如何定義服務子集。
路由規(guī)則是按從上到下的順序選擇的,虛擬服務中定義的第一條規(guī)則有最高優(yōu)先級。比如上面我們定義的虛擬服務中,不滿足第一個路由規(guī)則的流量均會流向一個默認的目標,第二條規(guī)則沒有配置 match 條件,直接將流量導向 v3 子集。
- - route:
- - destination:
- host: reviews
- subset: v3
一般建議提供一個默認的無條件或基于權重的規(guī)則作為每一個虛擬服務的最后一條規(guī)則,從而確保流經(jīng)虛擬服務的流量至少能夠匹配到一條路由規(guī)則。
DestinationRule
與虛擬服務一樣,DestinationRule(目標規(guī)則)也是 Istio 流量路由功能的關鍵部分,我們可以將虛擬服務看成將流量如何路由到指定目標地址,然后使用目標規(guī)則來配置該目標的流量。在評估虛擬服務路由規(guī)則之后,目標規(guī)則將應用于流量的“真實”目標地址。
可以使用目標規(guī)則來指定命名的服務子集,例如按版本為所有指定服務的實例分組,然后可以在虛擬服務的路由規(guī)則中使用這些服務子集來控制到服務不同實例的流量。目標規(guī)則還允許你在調(diào)用整個目的服務或特定服務子集時定制 Envoy 的流量策略,比如負載均衡模型、TLS 安全模式或熔斷器設置。
默認情況下,Istio 使用輪詢的負載均衡策略,實例池中的每個實例依次獲取請求。Istio 同時支持如下的負載均衡模型,可以在 DestinationRule 中為流向某個特定服務或服務子集的流量指定這些模型。
- 隨機:請求以隨機的方式轉到池中的實例。
- 權重:請求根據(jù)指定的百分比轉到實例。
- 最少請求:請求被轉到最少被訪問的實例。
比如在下面的示例中,目標規(guī)則為 my-svc 目標服務配置了 3 個具有不同負載均衡策略的子集:
- apiVersion: networking.istio.io/v1alpha3
- kind: DestinationRule
- metadata:
- name: my-destination-rule
- spec:
- host: my-svc
- trafficPolicy:
- loadBalancer:
- simple: RANDOM # 隨機的策略
- subsets:
- - name: v1
- labels:
- version: v1
- - name: v2
- labels:
- version: v2
- trafficPolicy:
- loadBalancer:
- simple: ROUND_ROBIN # 輪詢
- - name: v3
- labels:
- version: v3
每個子集都是基于一個或多個 labels 定義的,在 Kubernetes 中它是附加到 Pod 這種對象上的鍵/值對。除了定義子集之外,目標規(guī)則對于所有子集都有默認的流量策略,而對于具體的子集,則可以使用特定于子集的策略來覆蓋它。上面的示例定義在 subsets 上的默認策略,為 v1 和 v3 子集設置了一個簡單的隨機負載均衡器,在 v2 策略中,指定了一個輪詢負載均衡器。
在對虛擬服務和目標規(guī)則有了初步了解后,接下來我們就來對 Bookinfo 服務的訪問規(guī)則進行修改。
不同服務版本訪問規(guī)則
對 Reviews 服務添加一條路由規(guī)則,啟用 samples/bookinfo/networking/virtual-service-reviews-v3.yaml 定義的 VirtualService 規(guī)則,內(nèi)容如下:
- apiVersion: networking.istio.io/v1alpha3
- kind: VirtualService
- metadata:
- name: reviews
- spec:
- hosts:
- - reviews
- http:
- - route:
- - destination:
- host: reviews
- subset: v3
這樣,所有訪問 reviews 服務的流量就會被引導到 reviews 服務對應的 subset 為 v3 的 Pod 中。啟用這條規(guī)則:
- ➜ ~ kubectl apply -f samples/bookinfo/networking/virtual-service-reviews-v3.yaml
- virtualservice.networking.istio.io/reviews created
然后查看所有的路由規(guī)則:
- ➜ ~ kubectl get virtualservices
- NAME GATEWAYS HOSTS AGE
- bookinfo [bookinfo-gateway] [*] 158d
- reviews [reviews] 20s
我們可以看到 reviews 的 VirtualService 已經(jīng)創(chuàng)建成功了,此時我們?nèi)ニ⑿聭玫捻撁妫l(fā)現(xiàn)訪問 Reviews 失敗了:
bookinfo reviews v3 failed
這是因為我們還沒有創(chuàng)建 DestinationRule 對象,DestinationRule 對象是 VirtualService 路由生效后,配置應用與請求的策略集,用來將 VirtualService 中指定的 subset 與對應的 Pod 關聯(lián)起來。
在 samples/bookinfo/networking/destination-rule-all.yaml 文件中有定義所有該應用中要用到的所有 DestinationRule 資源對象,其中有一段就是對 Reviews 相關的 DestinationRule 的定義:
- ---
- 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 # 匹配version=v3標簽的Pod
我們可以看到 DestinationRule 中定義了 subsets 集合,其中 labels 就和我們之前 Service 的 labelselector 一樣是去匹配 Pod 的 labels 標簽的,比如我們這里 subsets 中就包含一個名為 v3 的 subset,而這個 subset 匹配的就是具有 version=v3 這個 label 標簽的 Pod 集合,前面我們創(chuàng)建的 Bookinfo 中也有這個標簽的 Pod:
- ➜ ~ kubectl get pods -l version=v3
- NAME READY STATUS RESTARTS AGE
- reviews-v3-84779c7bbc-bsld9 2/2 Running 2 47h
這樣我們就通過 DestinationRule 將 VirtualService 與 Service 不同的版本關聯(lián)起來了?,F(xiàn)在我們直接創(chuàng)建 DestinationRule 資源:
- ➜ ~ kubectl apply -f samples/bookinfo/networking/destination-rule-all.yaml
- destinationrule.networking.istio.io/productpage created
- destinationrule.networking.istio.io/reviews created
- destinationrule.networking.istio.io/ratings created
- destinationrule.networking.istio.io/details created
創(chuàng)建完成后,我們就可以查看目前我們網(wǎng)格中的 DestinationRules:
- ➜ ~ kubectl get destinationrule
- NAME HOST AGE
- details details 30s
- productpage productpage 30s
- ratings ratings 30s
- reviews reviews 30s
此時再訪問應用就成功了,多次刷新頁面發(fā)現(xiàn) Reviews 始終都展示的是 v3 版本(帶紅色星的)的 Ratings 了,說明我們VirtualService 的配置成功了。
reviews v3
基于權重的服務訪問規(guī)則
剛剛我們演示的基于不同服務版本的服務網(wǎng)格的控制,接下來我們來演示下基于權重的服務訪問規(guī)則的使用。
首先移除剛剛創(chuàng)建的 VirtualService 對象,排除對環(huán)境的影響:
- ➜ ~ kubectl delete virtualservice reviews
- virtualservice.networking.istio.io "reviews" deleted
- ➜ ~ kubectl get virtualservice
- NAME GATEWAYS HOSTS AGE
- bookinfo ["bookinfo-gateway"] ["*"] 2d
現(xiàn)在我們再去訪問 Bookinfo 應用又回到最初隨機訪問 Reviews 的情況了。現(xiàn)在我們查看文件 samples/bookinfo/networking/virtual-service-reviews-80-20.yaml 的定義:
- apiVersion: networking.istio.io/v1alpha3
- kind: VirtualService
- metadata:
- name: reviews
- spec:
- hosts:
- - reviews
- http:
- - route:
- - destination:
- host: reviews
- subset: v1
- weight: 80
- - destination:
- host: reviews
- subset: v2
- weight: 20
這個規(guī)則定義了 80% 的對 Reviews 的流量會落入到 v1(沒有 Ratings)這個 subset,20% 會落入 v2(帶黑色 Ratings)子集,然后我們創(chuàng)建這個資源對象:
- ➜ ~ kubectl apply -f samples/bookinfo/networking/virtual-service-reviews-80-20.yaml
- virtualservice.networking.istio.io/reviews created
- ➜ ~ kubectl get virtualservice
- NAME GATEWAYS HOSTS AGE
- bookinfo ["bookinfo-gateway"] ["*"] 2d
- reviews ["reviews"] 8s
我們查看當前網(wǎng)格中的 VirtualService 對象,可以看到已經(jīng)有 reviews 了,證明已經(jīng)創(chuàng)建成功了,由于上面我們已經(jīng)將應用中所有的 DestinationRules 都已經(jīng)創(chuàng)建過了,所以現(xiàn)在我們直接訪問應用就可以了,我們多次刷新,可以發(fā)現(xiàn)沒有出現(xiàn) Ratings 的次數(shù)與出現(xiàn)黑色星 Ratings 的比例大概在4:1左右,并且沒有紅色星的 Ratings 的情況出現(xiàn),說明我們配置的基于權重的 VirtualService 訪問規(guī)則配置生效了。
基于請求內(nèi)容的服務訪問規(guī)則
除了上面基于服務版本和服務權重的方式控制服務訪問之外,我們還可以基于請求內(nèi)容來進行訪問控制。
同樣,將上面創(chuàng)建的 VirtualService 對象刪除:
- ➜ ~ kubectl delete virtualservice reviews
- virtualservice.networking.istio.io "reviews" deleted
- ➜ ~ kubectl get virtualservice
- NAME GATEWAYS HOSTS AGE
- bookinfo ["bookinfo-gateway"] ["*"] 2d
查看文件 samples/bookinfo/networking/virtual-service-reviews-jason-v2-v3.yaml 的定義:
- apiVersion: networking.istio.io/v1alpha3
- kind: VirtualService
- metadata:
- name: reviews
- spec:
- hosts:
- - reviews
- http:
- - match:
- - headers:
- end-user:
- exact: jason
- route:
- - destination:
- host: reviews
- subset: v2
- - route:
- - destination:
- host: reviews
- subset: v3
這個 VirtualService 對象定義了對 reviews 服務訪問的 match 規(guī)則,意思是如果當前請求的 header 中包含 jason 這個用戶信息,則只會訪問到 v2 的 reviews 這個服務版本,即都帶黑星的樣式,如果不包含該用戶信息,則都直接將流量轉發(fā)給 v3 這個 reviews 的服務。
我們先不啟用這個 VirtualService,先去訪問下 Bookinfo 這個應用。
bookinfo login
右上角有登錄按鈕,在沒有登錄的情況下刷新頁面,reviews 服務是被隨機訪問的,可以看到有帶星不帶星的樣式,點擊登錄,在彈窗中 User Name 輸入 jason,Password 為空,登錄:
bookinfo logined
再刷新頁面,可以看到跟未登錄前的訪問規(guī)則一樣,也是隨機的。現(xiàn)在我們來創(chuàng)建上面的 VirtualService 這個對象:
- ➜ ~ kubectl apply -f samples/bookinfo/networking/virtual-service-reviews-jason-v2-v3.yaml
- virtualservice.networking.istio.io/reviews created
- ➜ ~ kubectl get virtualservice
- NAME GATEWAYS HOSTS AGE
- bookinfo ["bookinfo-gateway"] ["*"] 2d
- reviews ["reviews"] 8s
此時再回去刷新頁面,發(fā)現(xiàn)一直都是黑星的 Reviews 版本(v2)被訪問到了,注銷退出后再訪問,此時又一直是紅星的版本(v3)被訪問了。
說明我們基于 headers->end-user->exact:jason 的控制規(guī)則生效了。在 productpage 服務調(diào)用 reviews 服務時,登錄的情況下會在 header 中帶上用戶信息,通過 exact 規(guī)則匹配到相關信息后,流量被引向了上面配置的 v2 版本中。
這里要說明一下 match 的匹配規(guī)則:
- All conditions inside a single match block have AND semantics, while the list of match blocks have OR semantics. The rule is matched if any one of the match blocks succeed.
意思是一個 match 塊里的條件是需要同時滿足才算匹配成功的,如下面是 url 前綴和端口都必須都滿足才算成功:
- - match:
- - uri:
- prefix: "/wpcatalog"
- port: 443
多個 match 塊之間是只要有一個 match 匹配成功了,就會被路由到它指定的服務版本去,而忽略其他的。我們的示例中在登錄的條件下,滿足第一個 match,所以服務一直會訪問到 v2 版本。退出登錄后,沒有 match 規(guī)則滿足匹配,所以就走最后一個 route 規(guī)則,即 v3 版本。
到這里,我們就和大家一起學習了基于不同服務版本、權重以及請求內(nèi)容來控制服務流量的配置。