非容器应用与K8s工作负载的服务网格化实践-2 基于ASM的Workload Entry实践

本文涉及的产品
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
容器镜像服务 ACR,镜像仓库100个 不限时长
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
简介: Istio从1.6版本开始在流量管理中引入了新的资源类型Workload Entry,用以将虚拟机(VM)或者裸金属(bare metal)进行抽象,使其在网格化后作为与Kubernetes中的POD同等重要的负载,具备流量管理、安全管理、可视化等能力。通过WorkloadEntry可以简化虚拟机/裸金属的网格化配置过程。

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等外部流量设施。

2-1-workload-demo.png

图中有三种颜色的线,橙色是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 deploymenthello2 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容器。

验证过程如下方脚本所示:

  • 获取并进入验证环境所在的容器
  • 在这个容器中分别向自身和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-2-workload-demo.png

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%。

2-3-workload-traffic-demo.png

示例(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 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

2-4-istio-ingressgateway.png

为入口网关配置入口流量示意如下。名称为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实例下的VM网格化信息:

aliyun servicemesh GetVmAppMeshInfo --ServiceMeshId $MESH_ID

自动生成的WorkloadEntry默认是没有version标签的,为了展示流量转移,我们需要分别为3个WorkloadEntry增加与上述DestinationRule中配置相应的标签信息。

登录ASM管控台,在控制平面中找到指定的WorkloadEntry并进行编辑。

2-5-asm-workloadentry.png

示意如下:

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配置方式一致的DestinationRuleVirtualService,即可实现服务网格对VM上应用流量转移的配置管理。

4 尚未网格化

本篇的示例展示了使用WorkloadEntry将虚拟机/裸金属上的应用加入服务网格的过程,目的是以WorkloadEntry为主视角,展示WorkloadEntry的作用和使用方法。

WorkloadEntry的引入解决了从网格内的POD向VM中的应用请求的流量管理。但是反方向的请求单靠WorkloadEntry是不能解决的,因为VM中的应用无法找到网格内的POD。到目前为止,我们的VM还没有真正意义地实现网格化,只有完全实现网格化,VM内才能为应用提供sidecar,进而通过POD对应的service,将VM应用的请求路由到POD。

接下来的两篇将分别以http/grpc协议来展示真正意义的网格化VM与POD之间的相互通信。

相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
目录
相关文章
|
1天前
|
Kubernetes 容器 Perl
在K8S中,如何查看Pod中某个容器日志?
在K8S中,如何查看Pod中某个容器日志?
|
1天前
|
Kubernetes 负载均衡 网络协议
在K8S中,Pod能否实现对容器健康检查,如果服务有异常,该如何处理?
在K8S中,Pod能否实现对容器健康检查,如果服务有异常,该如何处理?
|
1天前
|
Kubernetes 容器 Perl
在k8S中,如何向Pod中指定容器传递环境变量?有哪些方式?
在k8S中,如何向Pod中指定容器传递环境变量?有哪些方式?
|
1天前
|
Kubernetes 算法 容器
在k8S中,Pod的容器重启策略有哪些?
在k8S中,Pod的容器重启策略有哪些?
|
9天前
|
Docker 容器
Docker cp 将宿主机上的文件复制到容器中
Docker cp 将宿主机上的文件复制到容器中
11 0
|
4天前
|
JSON JavaScript 开发者
Composerize神器:自动化转换Docker运行命令至Compose配置,简化容器部署流程
【8月更文挑战第7天】Composerize神器:自动化转换Docker运行命令至Compose配置,简化容器部署流程
Composerize神器:自动化转换Docker运行命令至Compose配置,简化容器部署流程
|
1天前
|
缓存 开发者 Docker
Dockerfile是Docker容器化过程中的核心组件,它允许开发者以一种可重复、可移植的方式自动化地构建Docker镜像
【8月更文挑战第19天】Dockerfile是构建Docker镜像的脚本文件,含一系列指令定义镜像构建步骤。每条大写指令后跟至少一个参数,按序执行,每执行一条指令即生成新的镜像层。常用指令包括:FROM指定基础镜像;RUN执行构建命令;EXPOSE开放端口;CMD指定容器启动行为等。优化策略涉及减少镜像层数、选择轻量基础镜像、利用缓存及清理冗余文件。示例:基于Python应用的Dockerfile包括设置工作目录、复制文件、安装依赖等步骤。掌握Dockerfile有助于高效自动化构建镜像,加速应用部署。
|
6天前
|
监控 Ubuntu Docker
如何在Docker容器启动时自动运行脚本
【8月更文挑战第13天】在Docker容器启动时自动运行脚本可通过以下方式实现:1) 使用`ENTRYPOINT`或`CMD`指令在Dockerfile中直接指定启动脚本,如`ENTRYPOINT [&quot;/startup.sh&quot;]`;2) 启动容器时通过`--entrypoint`参数指定脚本路径;3) 利用supervisor等进程管理工具自动启动与监控脚本,确保其稳定运行。确保脚本具有执行权限并正确设置依赖资源路径。
|
15天前
|
Shell 云计算 Docker
零基础到容器技术大神,一键解锁Docker实战秘籍!从零搭建,见证你的技术飞跃,让代码在云端翩翩起舞!
【8月更文挑战第5天】在云计算与微服务当道的今天,容器技术如汹涌浪潮般席卷IT领域。对新手而言,它或许充满神秘,但无须担忧,让我们一同揭开它的面纱。容器是一种轻量级软件打包技术,允许应用及其依赖被打包,在独立的虚拟环境中运行。Docker作为容器界的明星,简化了容器的创建与管理。从安装Docker开始,运行首个容器,深入容器内部执行命令,直至构建自定义镜像,我们将逐步掌握这项关键技术。这不仅是一场技术之旅,更是思维方式的革新,让我们携手探索未来。
56 6
|
17天前
|
运维 Ubuntu Shell
Docker命令宝典:解锁容器化技术的无限可能,从镜像管理到容器操作,全面解析与实战指南!
【8月更文挑战第3天】Docker简化了应用的部署与运行,掌握其基本命令对开发者和运维人员至关重要。通过`docker images`可查看本地镜像;使用`docker pull`拉取如最新版Ubuntu镜像;`docker rmi`用于删除不再需要的镜像。运行容器可通过`docker run`命令,结合`-it`等选项提供交互式环境。`docker ps`显示运行中的容器,加上`-a`则列出所有容器。`docker stop`和`docker start`分别用于停止和重启容器,而`docker rm`则删除容器。
44 5

相关产品

  • 容器服务Kubernetes版
  • 推荐镜像

    更多