【譯文連載】 理解Istio服務網格(第三章 流控)
第3章 流控............................................................................................................... 1
3.1 更加智慧的金絲雀........................................................................................ 1
3.2流量路由...................................................................................................... 2
3.2.1路由到特定版本的部署........................................................................ 3
3.2.2 Recommendation服務v2版本的金絲雀部署........................................... 4
3.2.3 繼續加碼recommendation服務的v2版本.............................................. 5
3.2.4 基於HTTP頭的路由.......................................................................... 6
3.3 暗部署......................................................................................................... 8
3.4對外請求(Egress)..................................................................................... 10
第3章 流控
前面講過Istio包括一個控制平面和一個數據平面。資料平面由代理組成,代理以邊車容器形式和業務容器共存。前面還介紹了“邊車”這種代理部署方式,其中每個應用例項都有自己專屬的代理,網路流量在到達應用例項之前都會經過該代理。這些邊車代理可以獨立地配置路由、過濾和擴張網路流量。本章中,我們會介紹利用Istio的各種流控模式。你將發現這些模式其實跟一些大的網際網路公司比如Netfilix、Amzaon和Fracebook用的東西差不多。
3.1 更加智慧的金絲雀
過去幾年中金絲雀部署(canary deployment)概念變得越來越流行。這個名字來自於“煤礦中的金絲雀”概念。過去礦工們常把一隻鳥籠中的金絲雀放進礦井中去檢測是否有危險氣體存在,因為金絲雀比人類對這類氣體更加敏感。金絲雀不僅能為礦工們放聲歌唱,而且一旦發現有危險氣體洩露,礦工們就能迅速逃離礦井。
金絲雀部署也有類似作用。在這種部署模式下,你部署一個新版本程式碼到生產環境中,但只允許一部分流量訪問到它。也許只是測試客戶,也許只是組織內部員工,也許只是iOS使用者等等。然後你可以監控這個版本的故障、出錯、SLA的變化等等。如果這個版本沒有問題,那就可以逐步引導更多的流量給到它;如果有問題,那就很容易將它從生產環境中移除。金絲雀部署讓你部署更快,而且將可能的有問題程式碼帶來的影響控制到最小。
預設地,Kubernetes提供Service提供輪詢負債功能能力。如果你只想為最新程式碼pod匯入10%的網路流量,那麼你不得不將新程式碼pod的數目設定為老程式碼pod的十分之一。利用Istio,你可以做到更精細的控制。你可以設定只將20%的網路流量導給三個最新程式碼的pod。Istio還能讓你逐漸增加匯入給新程式碼pod的流量,直到所有流量都被匯入給它,然後老程式碼版本就能從生產環境中移除了。
3.2流量路由
使用Istio,你可以設定路由規則(routing rule)來控制流量匯入指定pod中。特別地,Istio使用DestionationRule和VirtualServer資源來描述這些規則。下面是一個DestionationRule示例:
apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: recommendation namespace: tutorial spec: host: recommendation subsets: - labels: version: v1 name: version-v1 - labels: version: v2 name: version-v2
下面是一個VirtualServer示例,它通過一個集合(subset)和權重(weighting factor)來指定流量匯入:
apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: recommendation namespace: tutorial spec: hosts: - recommendation http: - route: - destination: host: recommendation subset: version-v1 weight: 100
使用此VirtualService定義,你可以配置特定百分比的流量定向到recommendation服務的特定版本上。在上面的例子中,recommendation服務的100%流量將始終流向與標籤version:v1匹配的pod。此處Pod的選擇方式與Kubernetes基於標籤的選擇器模型非常相似。因此,服務網格中與recommendation服務進行通訊的任何流量都將始終被路由到recommendation服務的v1版本。
上述路由行為不僅僅針對進入流量(ingress trafic)。實際上,可針對所有進入了網格的流量。這適用於網格內的所有服務間通訊。如本例所示,這些路由規則適用於可能在服務呼叫圖中的深層服務。如果你將不屬於服務網格的服務部署到Kubernetes,它將不會看到這些規則,而會遵循預設的Kubernetes負載平衡規則。
3.2.1路由到特定版本的部署
為了在類似金絲雀部署的場景中展示Istio更復雜的路由能力,我們來部署recommendation服務的v2版本。首先,你要修改recommendation服務的一些原始碼。修改
com.redhat.developer.demos.recommendation.RecommendationVerticle中的RESPONSE_STRING_FORMAT所定義的字串為“v2”: private static final String RESPONSE_STRING_FORMAT = "recommendation v2 from '%s': %d\n";
然後再編譯和打包新程式碼為v2版本:
cd recommendation/java/vertx mvn clean package docker build -t example/recommendation:v2 .
你可以執行jar檔案以快速測試程式碼修改:
java -jar target/recommendation.jar
然後你在另一箇中斷中執行下面的curl命令:
curl localhost:8080 recommendation v2 from 'unknown': 1
最後,注入Istio邊車代理並部署到Kubernetes中:
oc apply -f <(istioctl kube-inject -f ../../kubernetes/Deployment-v2.yml) -n tutorial
現在,你可以執行oc get pods命令去檢視pod。如果所有pod都成功執行的話,會是下面這樣子:
NAME READY STATUS RESTARTS AGE customer-3600192384-fpljb 2/2 Running 0 17m preference-243057078-8c5hz 2/2 Running 0 15m recommendation-v1-60483540 2/2 Running 0 12m recommendation-v2-99634814 2/2 Running 0 15s
現在,執行curl訪問customer服務端點,就會看到流量在兩個recommendation版本中做負載均衡。你會看到下面這樣子:
#!/bin/bash while true do curl customer-tutorial.$(minishift ip).nip.io sleep .1 done customer => preference => recommendation v1 from '60483540': 29 customer => preference => recommendation v2 from '99634814': 1 customer => preference => recommendation v1 from '60483540': 30 customer => preference => recommendation v2 from '99634814': 2 customer => preference => recommendation v1 from '60483540': 31 customer => preference => recommendation v2 from '99634814': 3
現在,建立DestionalRule和VirtualService例項來將所有流量匯入recommendation服務的v1版本。你要轉到原始碼的根目錄,進入istio-tutorial目錄,執行下面的命令:
oc -n tutorial create -f istiofiles/destination-rule-recommendation-v1-v2.yml oc -n tutorial create -f istiofiles/virtual-service-recommendation-v1.yml
現在,執行curl命令去訪問customer服務的話,你會看到流量全部被導到了v1版本:
customer => preference => recommendation v1 from '60483540': 32 customer => preference => recommendation v1 from '60483540': 33 customer => preference => recommendation v1 from '60483540': 34
VirtualService已經建立了到由DesetionationRule指定的目標子集的路由,子集中只有recommendation服務的v1版本。
3.2.2 Recommendation服務v2版本的金絲雀部署
現在,所有訪問流量都被導向recommendation服務的v1版本。你可以使用建立一個VirtualService例項來進行金絲雀部署:
apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: recommendation namespace: tutorial spec: hosts: - recommendation http: - route: - destination: host: recommendation subset: version-v1 weight: 90 - destination: host: recommendation subset: version-v2 weight: 10
這個VirtualService例項指定90%的流量被導向v1版本,10%的流量被導向v2版本。現在,使用這個VirtualService例項來覆蓋之前的例項,使用下面的命令:
oc -n tutorial replace -f istiofiles/virtual-service-recommendation-v1_and_v2.yml
現在,使用curl命令訪問customer服務的話,只有10%的流量被導向recommendation服務的v2版本。V2就是一個金絲雀版本。監控其日誌、指標和跟蹤系統來檢視這個版本有沒有引入任何錯誤或非期望的行為到你的環境中。
3.2.3 繼續加碼recommendation服務的v2版本
現在,如果沒有任何不期望的事情發生,你對recommendation服務的v2版本的信心會大一些了。你可能會想往v2版本匯入更多的流量。此時,你只需要將如下VirtualService定義替換已有的:
apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: recommendation namespace: tutorial spec: hosts: - recommendation http: - route: - destination: host: recommendation subset: version-v1 weight: 50 - destination: host: recommendation subset: version-v2 weight: 50
使用這個VirtualService例項後,各有50%的流量會被導向v1和v2版本。要應用該配置,只需要執行下面的命令:
oc -n tutorial replace –f istiofiles/virtual-service-recommendation-v1_and_v2_50_50.yml
執行成功後,你會看到流量行為的變化,一半流量被導向了v2版本,另一半到v2版本。輸出類似下面這樣子:
customer => ... => recommendation v1 from '60483540': 192 customer => ... => recommendation v2 from '99634814': 37 customer => ... => recommendation v2 from '99634814': 38 customer => ... => recommendation v1 from '60483540': 193 customer => ... => recommendation v2 from '99634814': 39 customer => ... => recommendation v2 from '99634814': 40
最後,如果一切正常的話,你可將所有流量導向v2版本,只需應用下面的VirtualService例項:
apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: recommendation namespace: tutorial spec: hosts: - recommendation http: - route: - destination: host: recommendation subset: version-v2 weight: 100
現在,你會看到所有流量被導向v2版本:
customer => preference => recommendation v2 from '99634814': 43 customer => preference => recommendation v2 from '99634814': 44 customer => preference => recommendation v2 from '99634814': 45 customer => preference => recommendation v2 from '99634814': 46 customer => preference => recommendation v2 from '99634814': 47 customer => preference => recommendation v2 from '99634814': 48
在繼續下面的步驟前,要恢復到預設流量路由行為,執行下面的命令刪除recommendation服務的VirtualService例項即可,然後你會看到Kubernetes自己的輪詢負載均衡效果:
oc delete virtualservice/recommendation -n tutorial
3.2.4 基於HTTP頭的路由
前面介紹了Istio基於服務的元資料所做的細粒度路由控制。你還可以用Istio基於請求的元資料進行控制。比如,你可以使用匹配原語去建立基於請求的特定路由規則。比如,你想基於請求的地域、移動裝置或者瀏覽器去將部分流量導向特定服務。我們來看下如何利用Istio做到這些。使用Istio,你可以在VirtualService中設定匹配規則。例如下面的VirtualService定義:
apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: creationTimestamp: null name: recommendation namespace: tutorial spec: hosts: - recommendation http: - match: - headers: baggage-user-agent: regex: .*Safari.* route: - destination: host: recommendation subset: version-v2 - route: - destination: host: recommendation subset: version-v1
這條規則匹配HTTP請求頭,只將由“Safari”瀏覽器發起的請求匯入recommendation服務的v2版本。執行下面的命令以安裝這條規則:
oc -n tutorial create -f istiofiles/virtual-service-safari-recommendation-v2.yml
然後試一下:
curl customer-tutorial.$(minishift ip).nip.io customer => preference => recommendation v1 from '60483540': 465
如果在curl命令中新增“user-agent:Safari”頭,那麼訪問會被導向v2版本:
curl -H 'User-Agent: Safari' customer-tutorial.$(minishift ip).nip.io customer => preference => recommendation v2 from '99634814': 318
如果用Firefox瀏覽器,那該訪問會被導向v1版本:
curl -A Firefox customer-tutorial.$(minishift ip).nip.io customer => preference => recommendation v1 from '60483540': 465
注意:如果你使用真實的瀏覽器進行測試,請注意在macOS系統上Chome瀏覽器會被識別為Safari。
Istio的DestiontionRule和VirtualService物件是使用CRD定義的,你可以象使用Kubernetes原生物件型別(比如Deployment、Pod、Service等)一樣去使用它們。執行下面的命令去獲取跟recommendation有關的Istio物件:
oc get crd | grep virtualservice kubectl describe destinationrule recommendation -n tutorial oc get virtualservice recommendation -o yaml -n tutorial
注意:基本上oc和kubectl兩個命令可以交替使用,因為oc是kubectl的一個超級,它多了一些跟login、project和new-app相關的命令,這些命令彌補了原生Kubernetes的一些不足。
繼續下面的步驟前,請使用下面的命令去清理所安裝的Istio物件:
oc delete virtualservice recommendation -n tutorial oc delete destinationrule recommendation -n tutorial
3.3 暗部署
暗部署(Dark Launch)對不同的人有不同的含義。本質上,暗部署是對客戶不可見的一種生產系統的部署。此時,Istio允許你複製或映象流量到應用的新版本中,然後將其行為與線上應用的行為進行對比。這種方式允許你將生產品質的請求導向新服務,同時不影響生產環境。
例如,recommendation的v1版本是生產環境,而v2版本是新部署環境。可使用Istio將到v1的瀏覽映象到v2。但Istio做瀏覽映象時,它採用“觸發並忘記”(fire-and-forget)模式。其含義是,Istio會做生產瀏覽的非同步映象,將被映象的請求發往測試pod,因此不用擔心它會返回什麼。我們來嘗試下。
首先,請確保環境中沒有DeistionationRule和VirtualService例項存在:
oc get destinationrules -n tutorial No resources found. oc get virtualservices -n tutorial No resources found.
我們來看下配置瀏覽映象的VirtualService定義:
apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: recommendation namespace: tutorial spec: hosts: - recommendation http: - route: - destination: host: recommendation subset: version-v1 mirror: host: recommendation subset: version-v2
該定義中,所有流量被導向recommendation服務的v1版本,在mirror部分,指定了接受映象流量的host和subset。
然後,轉到從Istio Tutorial克隆下來的程式碼的根目錄,執行下面的命令:
oc -n tutorial create -f istiofiles/destination-rule-recommendation-v1-v2.yml oc -n tutorial create -f istiofiles/virtual-service-recommendation-v1-mirror-v2.yml
在終端中,輸出recommendation服務v2版本pod的日誌:
oc -n tutorial logs -f `oc get pods|grep recommendation-v2|awk '{ print $1 }'` -c recommendation
你也可以使用stern去檢視recommendation服務的v1和v2版本pod的日誌:
stern recommendation
在另一個視窗中,用curl訪問customer服務:
curl customer-tutorial.$(minishift ip).nip.io customer => preference => recommendation v1 from '60483540': 466
從輸出中,你能看到recommendation服務的v1版本被訪問到了。再檢視v2版本pod的日誌,你會看到該服務處理進行流量所產生的日誌條目。
流量映象對於釋出前的測試會有很大幫助,但是還是有一些挑戰。比如,一個服務的新版本可能會訪問資料庫或其它關聯服務。要進行微服務中的資料處理,建議你閱讀Edson Yanaga所寫的Migrating to Microservice Databases(O’Reilly)一書。要了解流量映象更多的細節,可閱讀Christian的博文“Advanced Traffic-Shadowing Patterns for Microservices with Istio Service Mesh” (https://blog.christianposta.com/microservices/advanced-traffic-shadowing-patterns-for-microservices-with-istio-service-mesh/)。
請記得在繼續後面的步驟前刪除DestionationRule和VirtualService例項:
oc delete virtualservice recommendation -n tutorial oc delete destinationrule recommendation -n tutorial
3.4對外請求(Egress)
預設地,服務的所有流量都會通過Istio代理,代理和應用服務部署在一起。這個代理負責校驗路由規則,決定如何轉發請求。Istio很好的一點是它預設阻止所有發到叢集外的請求流量,除非顯式建立規則去允許這種訪問。因此,你可以在不信任網路和傳統私有云環境中使用Istio。這些場景中,Istio會幫助阻止惡意代理訪問應用服務進而獲得網路的完全訪問許可權。通過預設阻止對外訪問請求,並允許利用路由規則去控制內外部流量,你可以更從容地應對外面攻擊,不管它們是從哪裡發起的。
要進行測試驗證,可以進入一個pod,然後用curl發起請求:
oc get pods -n tutorial NAME READY STATUS RESTARTS AGE customer-6564ff969f-jqkkr 2/2 Running 0 19m preference-v1-5485dc6f49-hrlxm 2/2 Running 0 19m recommendation-v1-60483540 2/2 Running 0 20m recommendation-v2-99634814 2/2 Running 0 7m oc exec -it recommendation-v2-99634814 /bin/bash [jboss@recommendation-v2-99634814 ~]$ curl -v now.httpbin.org * About to connect() to now.httpbin.org port 80 (#0) * Trying 54.174.228.92... * Connected to now.httpbin.org (54.174.228.92) port 80 (#0) > GET / HTTP/1.1 > User-Agent: curl/7.29.0 > Host: now.httpbin.org > Accept: */* > < HTTP/1.1 404 Not Found < date: Sun, 02 Dec 2018 20:01:43 GMT < server: envoy < content-length: 0 < * Connection #0 to host now.httpbin.org left intact [jboss@recommendation-v2-99634814 ~]$ exit
結果你會從now.httpbin.org收到404 NOT Found錯誤資訊。要能正常訪問,你需要定義一個ServiceEntry例項,下面這個正是我們將要應用的:
apiVersion: networking.istio.io/v1alpha3 kind: ServiceEntry metadata: name: httpbin-egress-rule namespace: tutorial spec: hosts: - now.httpbin.org ports: - name: http-80 number: 80 protocol: http
首先你要建立DesionationRule例項,然後應用ServiceEntry例項:
oc -n tutorial create -f istiofiles/destination-rule-recommendation-v1-v2.yml oc -n tutorial create -f istiofiles/service-entry-egress-httpbin.yml -n tutorial
現在,你再進入pod,執行curl後你會得到200返回:
oc exec -it recommendation-v2-99634814 /bin/bash [jboss@recommendation-v2-99634814 ~]$ curl now.httpbin.org {"now": {"epoch": 1543782418.7876487...
你可以列表所有出口規則:
oc get serviceentry -n tutorial SERVICE-ENTRY NAME HOSTS PORTS NAMESPACE AGE httpbin-egress-rule now.httpbin.org http/80 tutorial 5m
最後,清理所有Istio例項,回到預設的Kubernetes行為:
oc delete serviceentry httpbin-egress-rule -n tutorial oc delete virtualservice recommendation -n tutorial oc delete destinationrule recommendation -n tutorial
本章中的這些示例都很簡單,但都是你探索Istio流控功能的一個良好的起點。
書籍英文版下載連結為 https://developers.redhat.com/books/introducing-istio-service-mesh-microservices/,作者 Burr Sutter 和 Christian Posta。
本中文譯稿版權由本人所有。水平有限,錯誤肯定是有的,還請海涵。
感謝您的閱讀,歡迎關注我的微信公眾號: