
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}。 环境变量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和hello2 service示意如下,其中镜像使用了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容器。
验证过程如下方脚本所示:
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 pod1 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%。
...
示例(http_workload_traffic_demo)包含如下元素:
hello1 deployment(镜像http_springboot_v1) 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 workloadentry3.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 servicemesh:是ASM集成到aliyun cli的子命令 ServiceMeshId参数用来指定ASM实例ID Namespace参数用来指定命名空间 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.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之间的相互通信。
本文为阿里云原创内容,未经允许不得转载。