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 service
- hello2 serviceentry
- hello2 workloadentry
實驗特制鏡像
鏡像http_springboot-{version}是一個基于springboot開發的http服務(源代碼在
這裡):
- version不同傳回的結果資訊不同。這樣設計的目的是在流量轉移實驗中展示不同路由的請求結果。
- v1傳回
Hello {input}
- v2傳回
Bonjour {input}
- v3傳回
。Hola {input}
- v1傳回
- 環境變量
用于告訴目前服務是否有下遊服務。這樣設計的目的是在不借助其他元件的情況下,極簡地展示鍊路資訊;另外可以靈活地無限擴充鍊路長度,以示範和驗證各種流量管理場景下的解決方案。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/v1
kind: Deployment
metadata:
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 和
示意如下,其中鏡像使用了v2版本,以便區分vm中的hello2 app的傳回結果。
apiVersion: apps/v1
kind: Deployment
metadata:
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: v1
kind: Service
metadata:
namespace: pod-vm-hello
name: hello2-svc
labels:
app: hello2-svc
spec:
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/v1alpha3
kind: ServiceEntry
metadata:
name: mesh-expansion-hello2-svc
namespace: pod-vm-hello
spec:
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/v1alpha3
kind: WorkloadEntry
metadata:
name: vm1
namespace: pod-vm-hello
spec:
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
echo
done
我們期待的結果是,流量交替發向hello2 pod和hello2 app:
▶ sh sh/pod-vm-test.sh
>>> test hello1 -> hello2
Hello eric(172.18.0.216)<-Hello eric(192.168.0.170)
>>> test hello2 directly
Hello eric(192.168.0.170)
>>> test hello1 -> hello2
Hello eric(172.18.0.216)<-Bonjour eric(172.18.1.97)
>>> test hello2 directly
Bonjour 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
echo
done
}
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.yaml
verify_in_loop
echo "2 add pod and test serviceentry routes to pod and workloadentry:"
k apply -f yaml/hello2-deploy-v2.yaml
verify_in_loop
echo "3 delete workloadentry and test serviceentry only routes to pod:"
m delete workloadentry vm1 -n pod-vm-hello
verify_in_loop
驗證結果如下所示:
- 第一步删除pod後,流量全部進入ecs中的hello2 app
- 第二步加入pod後,流量交替發送到ack中的hello2 pod和ecs中的hello2 app
- 第三步删除workloadentry後,流量全部進入ack中的hello2 pod
1 delete pod and test serviceentry only routes to workloadentry:d_demo/sh/migrate-test.sh
deployment.apps "hello2-deploy-v2" deleted
>>> test hello1 -> hello2
Hello eric(172.18.0.216)<-Hello eric(192.168.0.170)
>>> test hello2 directly
Hello eric(192.168.0.170)
>>> test hello1 -> hello2
Hello eric(172.18.0.216)<-Hello eric(192.168.0.170)
>>> test hello2 directly
Hello eric(192.168.0.170)
...
2 add pod and test serviceentry routes to pod and workloadentry:
deployment.apps/hello2-deploy-v2 created
>>> test hello1 -> hello2
Hello eric(172.18.0.216)<-Hello eric(192.168.0.170)
>>> test hello2 directly
Hello eric(192.168.0.170)
>>> test hello1 -> hello2
Hello eric(172.18.0.216)<-Bonjour eric(172.18.1.98)
>>> test hello2 directly
Bonjour eric(172.18.1.98)
...
3 delete workloadentry and test serviceentry only routes to pod:
workloadentry.networking.istio.io "vm1" deleted
>>> test hello1 -> hello2
Hello eric(172.18.0.216)<-Bonjour eric(172.18.1.98)
>>> test hello2 directly
Bonjour eric(172.18.1.98)
>>> test hello1 -> hello2
Hello eric(172.18.0.216)<-Bonjour eric(172.18.1.98)
>>> test hello2 directly
Bonjour 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%。
- hello2 docker containers(鏡像http_springboot_v1/http_springboot_v2/http_springboot_v3)
- 入口網關:istio-ingressgateway
- 入口流量配置:gateway/virtualservice
- hello1流量配置:hello1 service/hello1 virtualservice/hello1 destinationrule
- hello2流量配置:hello2 service/hello2 virtualservice/hello2 destinationrule
- hello2 serviceentry/hello2 workloadentry
3.1 入口流量配置
首先登入
ASM管控台,配置入口網關服務,增加8002端口如下所示:
apiVersion: istio.alibabacloud.com/v1beta1
kind: IstioGateway
metadata:
name: ingressgateway
namespace: istio-system
spec:
ports:
...
- name: http-workload
port: 8002
targetPort: 8002
為入口網關配置入口流量示意如下。名稱為
external-hello-gateway
的Gateway将
spec.selector.istio
定義為入口網關的名稱
ingressgateway
,名稱為
gateway-vs
的VirtualService将
spec.gateways
定義為
external-hello-gateway
。将請求到8002端口的全部流量轉移到
hello1-svc
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
namespace: external-hello
name: external-hello-gateway
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 8002
name: http
protocol: HTTP
hosts:
- "*"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
namespace: external-hello
name: gateway-vs
spec:
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/v1alpha3
kind: DestinationRule
metadata:
namespace: external-hello
name: hello2-dr
spec:
host: hello2-svc
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
- name: v3
labels:
version: v3
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
namespace: external-hello
name: hello2-vs
spec:
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
-
:是ASM內建到servicemesh
的子指令aliyun cli
-
參數用來指定ASM執行個體IDServiceMeshId
-
參數用來指定命名空間Namespace
-
參數用來指定服務名稱ServiceName
-
參數用來指定VM的IP,本例使用3個ecs節點Ips
-
參數用來指定服務端口,使用冒号分割,冒号前為協定名稱,冒号後為服務端口号Ports
-
參數用來指定WorkloadEntry的labels和ServiceEntry的Labels
,這個參數值是兩者關聯的橋梁。spec.workloadSelector.labels
詳見
服務網格 ASM官方文檔建立完畢後可以通過如下指令查詢ASM執行個體下的VM網格化資訊:
aliyun servicemesh GetVmAppMeshInfo --ServiceMeshId $MESH_ID
自動生成的WorkloadEntry預設是沒有version标簽的,為了展示流量轉移,我們需要分别為3個WorkloadEntry增加與上述DestinationRule中配置相應的标簽資訊。
登入
,在控制平面中找到指定的WorkloadEntry并進行編輯。
示意如下:
spec:
address: 192.168.0.170
labels:
app: hello-workload
version: v1
最後,我們在3個ecs節點上分别啟動hello2.示意如下:
# vm/ssh1.sh
docker run \
--rm \
--network host \
--name http_v1 \
registry.cn-beijing.aliyuncs.com/asm_repo/http_springboot_v1:1.0.1
# vm/ssh2.sh
docker run \
--rm \
--network host \
--name http_v2 \
registry.cn-beijing.aliyuncs.com/asm_repo/http_springboot_v2:1.0.1
# vm/ssh3.sh
docker 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_result
done
echo "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 loop
expected 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之間的互相通信。