Istio從1.6版本開始在流量管理中引入了新的資源型別Workload Entry,用以將虛擬機器(VM)或者裸金屬(bare metal)進行抽象,使其在網格化後作為與Kubernetes中的POD同等重要的負載,具備流量管理、安全管理、視覺化等能力。通過WorkloadEntry可以簡化虛擬機器/裸金屬的網格化配置過程。
1 VM與POD同等負載首先,我們通過一個示例來展示如何使用Workload Entry實現VM與POD同等負載。示例的拓撲如下圖所示,核心目標是將ack中的hello2 pod和ecs中的hello2 app視為同一個服務(hello2 service)下的兩種負載。簡單起見,本例沒有配置流量轉移,因此實驗預期的結果是:從hello1發出的請求,交替發向hello2 pod和hello2 app。本例的流量始於hello1 POD內部,因此無需為hello1配置service,同理沒有gateway等外部流量設施。
圖中有三種顏色的線,橙色是ASM istioD和ASM sidecar之間的xDS通訊(用途包括同步流量配置給各POD中的envoy等),黑色是物理鏈路,綠色虛線是邏輯鏈路。物理鏈路的視角是POD和VM,對應的邏輯視角是serviceentry、service和workloadentry。
示例(http_workload_demo)包含如下元素:
hello1 deployment(映象http_springboot_v1)hello2 deployment(映象http_springboot_v2)hello2 docker container(映象http_springboot_v1)hello2 servicehello2 serviceentryhello2 workloadentry實驗特製映象
映象http_springboot-{version}是一個基於springboot開發的http服務(原始碼在這裡):
version不同返回的結果資訊不同。這樣設計的目的是在流量轉移實驗中展示不同路由的請求結果。v1返回Hello {input}v2返回Bonjour {input}v3返回Hola {input}。環境變數HTTP_HELLO_BACKEND用於告訴當前服務是否有下游服務。這樣設計的目的是在不借助其他元件的情況下,極簡地展示鏈路資訊;另外可以靈活地無限擴充套件鏈路長度,以演示和驗證各種流量管理場景下的解決方案。如果沒有下游,直接返回類似這樣的資訊:Hello eric(192.168.0.170)如果存在下游,則請求下游並將結果合併,返回資訊類似這樣:Hello eric(172.18.0.216)<-...<-Bonjour eric(172.18.1.97)。1.1 Setup
hello1 deployment示意如下,env定義了下游服務hello2-svc.pod-vm-hello.svc.cluster.local
apiVersion: apps/v1kind: Deploymentmetadata: namespace: pod-vm-hello name: hello1-deploy... spec: containers: - name: hello-v1-deploy image: registry.cn-beijing.aliyuncs.com/asm_repo/http_springboot_v1:1.0.1 env: - name: HTTP_HELLO_BACKEND value: "hello2-svc.pod-vm-hello.svc.cluster.local" ports: - containerPort: 8001
hello2 deployment和hello2 service示意如下,其中映象使用了v2版本,以便區分vm中的hello2 app的返回結果。
apiVersion: apps/v1kind: Deploymentmetadata: namespace: pod-vm-hello name: hello2-deploy-v2... spec: containers: - name: hello-v1-deploy image: registry.cn-beijing.aliyuncs.com/asm_repo/http_springboot_v2:1.0.1 ports: - containerPort: 8001---apiVersion: v1kind: Servicemetadata: namespace: pod-vm-hello name: hello2-svc labels: app: hello2-svcspec: ports: - port: 8001 name: http selector: app: hello2-deploy
hello2 serviceentry和hello2 workloadentry示意如下,ServiceEntry通過workloadSelector中定義的app: hello2-deploy找到相應的pod和workload entry。WorkloadEntry與VM(ecs例項)一一對應,address中定義了ecs的ip。
apiVersion: networking.istio.io/v1alpha3kind: ServiceEntrymetadata: name: mesh-expansion-hello2-svc namespace: pod-vm-hellospec: hosts: - hello2-svc.pod-vm-hello.svc.cluster.local location: MESH_INTERNAL ports: - name: http number: 8001 protocol: HTTP resolution: STATIC workloadSelector: labels: app: hello2-deploy---apiVersion: networking.istio.io/v1alpha3kind: WorkloadEntrymetadata: name: vm1 namespace: pod-vm-hellospec: address: 192.168.0.170 labels: app: hello2-deploy class: vm version: v1
ecs中的hello2 app使用如下命令啟動。啟用--network host的目的是在hello2返回資訊時使用ecs的ip。也可以不啟用,這時返回的是docker container的ip,以便通過埠對映實現,在一個ecs示例中啟動多個http_springboot例項。
docker run \\--rm \\--network host \\--name http_v1 \\registry.cn-beijing.aliyuncs.com/asm_repo/http_springboot_v1:1.0.1
完整的setup指令碼參見setup-pod-vm.sh,不再冗述。示例實驗環境搭建好後,我們進入驗證環節。
1.2 流量驗證
本例的流量始於hello1 POD內部,因此我們的驗證環境是POD$hello1_pod中的hello-v1-deploy容器。
驗證過程如下方指令碼所示:
獲取並進入驗證環境所在的容器在這個容器中分別向自身和hello2-svc.pod-vm-hello.svc.cluster.local發起請求alias k="kubectl --kubeconfig $USER_CONFIG"hello1_pod=$(k get pod -l app=hello1-deploy -n pod-vm-hello -o jsonpath={.items..metadata.name})for i in {1..5}; do echo ">>> test hello1 -> hello2" k exec "$hello1_pod" -c hello-v1-deploy -n pod-vm-hello -- curl -s localhost:8001/hello/eric echo echo ">>> test hello2 directly" k exec "$hello1_pod" -c hello-v1-deploy -n pod-vm-hello -- curl -s hello2-svc.pod-vm-hello.svc.cluster.local:8001/hello/eric echodone
我們期待的結果是,流量交替發向hello2 pod和hello2 app:
▶ sh sh/pod-vm-test.sh>>> test hello1 -> hello2Hello eric(172.18.0.216)<-Hello eric(192.168.0.170)>>> test hello2 directlyHello eric(192.168.0.170)>>> test hello1 -> hello2Hello eric(172.18.0.216)<-Bonjour eric(172.18.1.97)>>> test hello2 directlyBonjour eric(172.18.1.97)...
到此,示例的驗證完畢。通過這個示例,我們看到WorkloadEntry的引入,使vm中的非kubernetes容器應用可以非常容易地加入服務網格中,與POD成為同級別的負載。
在此基礎上,我們很容易想到可以藉助服務網格的流量轉移,將ecs上的應用無損地遷移到POD中。接下來,我們繼續基於這個示例來演示流量的切換。
2 從VM遷移到POD首先我們在示例驗證結果的基礎上,刪除hello2 pod,讓從hello1發出的流量全部進入ecs中的hello2 app。這是模擬遺留應用網格化的初始狀態。如下方左圖所示。
接下來,我們把hello2 pod加入網格,即前序示例的狀態。這是模擬遺留應用網格化的中間過程。
最後,我們刪除hello2 workloadentry,此時從hello1發出的流量將全部進入ack中的hello2 pod。這是模擬遺留應用完成網格化的終態。如下方右圖所示。
2.1 流量驗證
驗證過程如下方指令碼所示:
alias k="kubectl --kubeconfig $USER_CONFIG"alias m="kubectl --kubeconfig $MESH_CONFIG"verify_in_loop(){ for i in {1..5}; do echo ">>> test hello1 -> hello2" k exec "$hello1_pod" -c hello-v1-deploy -n pod-vm-hello -- curl -s localhost:8001/hello/eric echo echo ">>> test hello2 directly" k exec "$hello1_pod" -c hello-v1-deploy -n pod-vm-hello -- curl -s hello2-svc.pod-vm-hello.svc.cluster.local:8001/hello/eric echodone}hello1_pod=$(k get pod -l app=hello1-deploy -n pod-vm-hello -o jsonpath={.items..metadata.name})echo "1 delete pod and test serviceentry only routes to workloadentry:"k delete -f yaml/hello2-deploy-v2.yamlverify_in_loopecho "2 add pod and test serviceentry routes to pod and workloadentry:"k apply -f yaml/hello2-deploy-v2.yamlverify_in_loopecho "3 delete workloadentry and test serviceentry only routes to pod:"m delete workloadentry vm1 -n pod-vm-helloverify_in_loop
驗證結果如下所示:
第一步刪除pod後,流量全部進入ecs中的hello2 app第二步加入pod後,流量交替傳送到ack中的hello2 pod和ecs中的hello2 app第三步刪除workloadentry後,流量全部進入ack中的hello2 pod1 delete pod and test serviceentry only routes to workloadentry:d_demo/sh/migrate-test.shdeployment.apps "hello2-deploy-v2" deleted>>> test hello1 -> hello2Hello eric(172.18.0.216)<-Hello eric(192.168.0.170)>>> test hello2 directlyHello eric(192.168.0.170)>>> test hello1 -> hello2Hello eric(172.18.0.216)<-Hello eric(192.168.0.170)>>> test hello2 directlyHello eric(192.168.0.170)...2 add pod and test serviceentry routes to pod and workloadentry:deployment.apps/hello2-deploy-v2 created>>> test hello1 -> hello2Hello eric(172.18.0.216)<-Hello eric(192.168.0.170)>>> test hello2 directlyHello eric(192.168.0.170)>>> test hello1 -> hello2Hello eric(172.18.0.216)<-Bonjour eric(172.18.1.98)>>> test hello2 directlyBonjour eric(172.18.1.98)...3 delete workloadentry and test serviceentry only routes to pod:workloadentry.networking.istio.io "vm1" deleted>>> test hello1 -> hello2Hello eric(172.18.0.216)<-Bonjour eric(172.18.1.98)>>> test hello2 directlyBonjour eric(172.18.1.98)>>> test hello1 -> hello2Hello eric(172.18.0.216)<-Bonjour eric(172.18.1.98)>>> test hello2 directlyBonjour eric(172.18.1.98)...
到此,從VM遷移到POD驗證完畢。因為本例的核心是展示從VM遷移到POD的可能性,只演示了遷移過程的3個邊界狀態,沒有展示通過virtualservice進行平滑切流,從而實現流量無損的遷移。接下來的流量管理示例,virtualservice將作為一號位閃亮登場。
3 VM流量管理通過前兩節的展示我們看到:使用``Workload Entry可以方便地將非容器應用加入服務網格,並與POD同等待遇;此外,可以最終使用POD替代全部非容器應用。但是,遷移與否以及遷移的節奏依賴於諸多因素。在沒有啟程遷移時,依然可以通過Workload Entry讓非容器應用享受到一定的網格化能力。接下來的示例將展示流量遷移。
示例的拓撲如下圖所示,核心目標是將ecs中的hello2 app的多個例項視為同一個服務下的負載,通過virtualservice配置實現不同例項的流量配比。這個實驗將完整地展示入口閘道器和流量轉移,流量從本地向公網發起,經入口閘道器到hello1,再由hello1向ecs中的三個hellp2 app發起請求,流量配比為:30%:60%:10%。
示例(http_workload_traffic_demo)包含如下元素:
hello1 deployment(映象http_springboot_v1)hello2 docker containers(映象http_springboot_v1/http_springboot_v2/http_springboot_v3)入口閘道器:istio-ingressgateway入口流量配置:gateway/virtualservicehello1流量配置:hello1 service/hello1 virtualservice/hello1 destinationrulehello2流量配置:hello2 service/hello2 virtualservice/hello2 destinationrulehello2 serviceentry/hello2 workloadentry3.1 入口流量配置
首先登入ASM管控臺,配置入口閘道器服務,增加8002埠如下所示:
為入口閘道器配置入口流量示意如下。名稱為external-hello-gateway的Gateway將spec.selector.istio定義為入口閘道器的名稱ingressgateway,名稱為gateway-vs的VirtualService將spec.gateways定義為external-hello-gateway。將請求到8002埠的全部流量轉移到hello1-svc。
apiVersion: networking.istio.io/v1alpha3kind: Gatewaymetadata: namespace: external-hello name: external-hello-gatewayspec: selector: istio: ingressgateway servers: - port: number: 8002 name: http protocol: HTTP hosts: - "*"---apiVersion: networking.istio.io/v1alpha3kind: VirtualServicemetadata: namespace: external-hello name: gateway-vsspec: hosts: - "*" gateways: - external-hello-gateway http: - match: - port: 8002 route: - destination: host: hello1-svc
hello1-svc將流量轉移到8001,本例中的負載(POD/VM)均通過8001對外提供服務。
spec: ports: - name: http port: 8002 targetPort: 8001 protocol: TCP
3.2 hello2流量轉移配置
接下來,我們重點關注hello2的流量配置。在hello2 destinationrule中定義了hello2的3個subsets,每個subset通過labels與對應的負載關聯。在hello2 virtualservice中定義了請求路徑規則:路徑字首為/hello的請求,會按照3:6:1的比例轉移到v1/v2/v3對應的負載端點上。
apiVersion: networking.istio.io/v1alpha3kind: DestinationRulemetadata: namespace: external-hello name: hello2-drspec: host: hello2-svc subsets: - name: v1 labels: version: v1 - name: v2 labels: version: v2 - name: v3 labels: version: v3---apiVersion: networking.istio.io/v1alpha3kind: VirtualServicemetadata: namespace: external-hello name: hello2-vsspec: hosts: - hello2-svc http: - name: http-hello-route match: - uri: prefix: /hello route: - destination: host: hello2-svc subset: v1 weight: 30 - destination: host: hello2-svc subset: v2 weight: 60 - destination: host: hello2-svc subset: v3 weight: 10
我們通過執行aliyun cli命令,為ASM例項增加hello2 service/hello2 serviceentry/hello2 workloadentry。命令如下:
aliyun servicemesh AddVmAppToMesh \\ --ServiceMeshId $MESH_ID \\ --Namespace external-hello \\ --ServiceName hello2-svc \\ --Ips "$VM_PRI_1","$VM_PRI_2","$VM_PRI_3" \\ --Ports http:8001 \\ --Labels app=hello-workload
servicemesh:是ASM整合到aliyun cli的子命令ServiceMeshId引數用來指定ASM例項IDNamespace引數用來指定名稱空間ServiceName引數用來指定服務名稱Ips引數用來指定VM的IP,本例使用3個ecs節點Ports引數用來指定服務埠,使用冒號分割,冒號前為協議名稱,冒號後為服務埠號Labels引數用來指定WorkloadEntry的labels和ServiceEntry的spec.workloadSelector.labels,這個引數值是兩者關聯的橋樑。
詳見服務網格 ASM官方文件
建立完畢後可以通過如下命令查詢ASM例項下的VM網格化資訊:
aliyun servicemesh GetVmAppMeshInfo --ServiceMeshId $MESH_ID
自動生成的WorkloadEntry預設是沒有version標籤的,為了展示流量轉移,我們需要分別為3個WorkloadEntry增加與上述DestinationRule中配置相應的標籤資訊。
登入ASM管控臺,在控制平面中找到指定的WorkloadEntry並進行編輯。
示意如下:
spec: address: 192.168.0.170 labels: app: hello-workload version: v1
最後,我們在3個ecs節點上分別啟動hello2.示意如下:
# vm/ssh1.shdocker run \\--rm \\--network host \\--name http_v1 \\registry.cn-beijing.aliyuncs.com/asm_repo/http_springboot_v1:1.0.1# vm/ssh2.shdocker run \\--rm \\--network host \\--name http_v2 \\registry.cn-beijing.aliyuncs.com/asm_repo/http_springboot_v2:1.0.1# vm/ssh3.shdocker run \\--rm \\--network host \\--name http_v3 \\registry.cn-beijing.aliyuncs.com/asm_repo/http_springboot_v3:1.0.1
3.3 流量驗證
實驗環境搭建完畢後,通過驗證指令碼test_traffic_shift.sh進行驗證:
獲取入口閘道器IP迴圈呼叫8002埠並根據響應資訊統計路由IP=$(k -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')for i in {1..100}; do resp=$(curl -s "$IP":8002/hello/eric) echo "$resp" >>test_traffic_shift_resultdoneecho "expected 30%(Hello eric)-60%(Bonjour eric)-10%(Hola eric):"sort test_traffic_shift_result | grep -v "^[[:space:]]*$"| uniq -c | sort -nrk1
驗證結果如下:
Test route n a loopexpected 30%(Hello eric)-60%(Bonjour eric)-10%(Hola eric): 61 Hello eric(172.18.1.99)<-Bonjour eric(192.168.0.171) 31 Hello eric(172.18.1.99)<-Hello eric(192.168.0.170) 8 Hello eric(172.18.1.99)<-Hola eric(192.168.0.172)
到此,VM流量管理驗證完畢。我們看到,通過配置WorkloadEntry將VM加入到網格中,然後再為其增加與POD配置方式一致的DestinationRule和VirtualService,即可實現服務網格對VM上應用流量轉移的配置管理。
4 尚未網格化本篇的示例展示了使用WorkloadEntry將虛擬機器/裸金屬上的應用加入服務網格的過程,目的是以WorkloadEntry為主視角,展示WorkloadEntry的作用和使用方法。
WorkloadEntry的引入解決了從網格內的POD向VM中的應用請求的流量管理。但是反方向的請求單靠WorkloadEntry是不能解決的,因為VM中的應用無法找到網格內的POD。到目前為止,我們的VM還沒有真正意義地實現網格化,只有完全實現網格化,VM內才能為應用提供sidecar,進而通過POD對應的service,將VM應用的請求路由到POD。
接下來的兩篇將分別以http/grpc協議來展示真正意義的網格化VM與POD之間的相互通訊。