1. 场景介绍
在上云的浪潮中,许多企业选择将业务从自建的Istio+k8s环境迁移到阿里云的服务网格ASM(Alibaba Cloud Service Mesh)和ACK(Alibaba Cloud Container Service Kubernetes)环境中。这种迁移不仅能够充分利用阿里云的托管服务优势,还能显著提升资源利用率和运维效率。
然而,在实际迁移过程中,由于网络限制、成本考量或业务连续性要求,我们常常无法立即将两个集群的服务发现打通。这种情况下,新集群无法直接访问老集群的服务,而新集群又不可能一次性部署所有服务,因此部分客户需要一种过渡方案,让新集群的网关和服务能够调用老集群的服务,确保业务连续性。
本文将详细介绍如何在服务发现不通的情况下,通过配置虚拟服务(VirtualService)和入口服务(ServiceEntry),将新集群的部分南北向和东西向流量转发到老集群的网关,实现平滑迁移。
2. 环境准备
在本示例中,通过多个Istio社区提供的标准示例应用来演示应用在两个环境中迁移的效果。具体来说,我们将在Istio+K8s的环境(下称“老集群”)中部署bookinfo、sleep、httpbin和helloworld,而在ASM+ACK的环境(下称“新集群”)中部署sleep、httpbin和helloworld。由于新集群中没有部署bookinfo,我们将通过bookinfo应用的访问来演示新集群访问老集群的效果。
2.1 老集群环境
在老集群中,我们首先需要安装Istio并部署一些标准应用,包括bookinfo、sleep、httpbin和helloworld。以下是具体的部署步骤和路由配置:
- 安装Istio:按照Istio官方文档或阿里云文档完成Istio的安装和配置。
- 部署标准应用:使用以下命令部署bookinfo、sleep、httpbin和helloworld应用:
kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml kubectl apply -f samples/sleep/sleep.yaml kubectl apply -f samples/httpbin/httpbin.yaml kubectl apply -f samples/helloworld/helloworld.yaml
- 配置路由规则:创建以下Gateway和VirtualService资源,确保流量能够正确路由到各个服务。在下述规则中,我们使用了一个假的域名“bf.example.com”,来模拟常规业务使用外部域名的情况。
# Gateway配置 apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: my-gateway spec: selector: istio: ingressgateway servers: - port: number: 80 name: http protocol: HTTP hosts: - "bf.example.com"
# VirtualService配置 apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: bookinfo spec: hosts: - "bf.example.com" gateways: - my-gateway http: - match: - uri: exact: /productpage - uri: prefix: /static - uri: exact: /login - uri: exact: /logout - uri: prefix: /api/v1/products route: - destination: host: productpage port: number: 9080
# httpbin VirtualService apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: httpbin spec: hosts: - "bf.example.com" gateways: - my-gateway http: - match: - uri: prefix: /get - uri: prefix: /anything route: - destination: host: httpbin port: number: 8000
# helloworld VirtualService apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: helloworld spec: hosts: - "bf.example.com" gateways: - my-gateway http: - match: - uri: prefix: /hello route: - destination: host: helloworld port: number: 5000
2.2 新集群环境
在新集群中,我们安装ASM并部署部分服务,包括sleep、httpbin和helloworld。以下是具体的部署步骤和路由配置:
- 安装ASM:按照阿里云文档完成ASM的安装和配置。
- 部署标准应用:使用以下命令部署sleep、httpbin和helloworld应用:
kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml kubectl delete deploy productpage-v1 reviews-v1 reviews-v2 reviews-v3 ratings-v1 details-v1 kubectl apply -f samples/sleep/sleep.yaml kubectl apply -f samples/httpbin/httpbin.yaml kubectl apply -f samples/helloworld/helloworld.yaml
请注意,在新集群中我们虽然不想部署bookinfo应用,但仍然部署了它其中的Service,并删除了所有服务的Deployment。这是因为新老集群的服务发现没有打通,因此新集群中Service必须和老集群是对等的,以保证对服务网格来说存在相同的服务发现环境。然而对于这些服务并不需要部署真正的工作负载。
- 配置路由规则:创建以下Gateway和VirtualService资源,确保流量能够正确路由到各个服务:
# Gateway配置 apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: my-gateway spec: selector: istio: ingressgateway servers: - port: number: 80 name: http protocol: HTTP hosts: - "bf.example.com"
# httpbin VirtualService apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: httpbin spec: hosts: - "bf.example.com" gateways: - my-gateway http: - match: - uri: prefix: /get - uri: prefix: /anything route: - destination: host: httpbin port: number: 8000
# helloworld VirtualService apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: helloworld spec: hosts: - "bf.example.com" gateways: - my-gateway http: - match: - uri: prefix: /hello route: - destination: host: helloworld port: number: 5000
3. 配置兜底路由
为了确保新集群中的网关能够将无法识别的流量转发到老集群的网关,我们需要在新集群中配置一条兜底路由。以下是具体的配置步骤:
3.1 配置ServiceEntry
首先,我们需要在新集群中注册老集群网关的地址,以便ASM能够识别和路由到老集群。以下是具体的ServiceEntry配置:
apiVersion: networking.istio.io/v1 kind: ServiceEntry metadata: name: dummy namespace: default spec: addresses: - {老集群网关ip} endpoints: - address: {老集群网关ip} hosts: - dummy.com location: MESH_EXTERNAL ports: - name: http number: 80 protocol: HTTP resolution: STATIC
配置说明:
- addresses和endpoints字段指定了老集群网关的IP地址。
- hosts字段定义了一个虚拟主机dummy.com,用于标识老集群网关。这个host是个dummy host,只要不和集群中的已知域名冲突就行,主要用于网格代理识别转发目标。
- location字段设置为MESH_EXTERNAL,表示这是一个外部服务。
- ports字段定义了HTTP协议的80端口。
- resolution字段设置为STATIC,表示静态解析,不需要额外的DNS或服务发现。通过这种方式,网格代理可以直接从ServiceEntry配置中解析到dummy.com对应的内网网关ip地址,对请求进行转发。
3.2 配置VirtualService
接下来,我们需要配置一条VirtualService,将所有无法识别的流量转发到老集群的网关。以下是具体的配置:
apiVersion: networking.istio.io/v1 kind: VirtualService metadata: name: default-route namespace: default spec: gateways: - default/my-gateway hosts: - bf.example.com http: - route: - destination: host: dummy.com
配置说明:
- gateways字段指定了使用的网关。
- hosts字段指定了虚拟主机bf.example.com。
- http字段定义了HTTP路由规则。
- route字段指定了将所有流量转发到dummy.com,即老集群的网关。
4. 配置服务转发
在新集群中,当服务需要通过东西向流量调用老集群中的服务时,我们需要配置VirtualService将流量转发到老集群。以下是具体的配置步骤:
4.1 配置VirtualService
我们需要创建一个VirtualService,将发往productpage服务的请求转发到老集群的网关。以下是具体的配置:
apiVersion: networking.istio.io/v1 kind: VirtualService metadata: name: productpage namespace: default spec: hosts: - productpage http: - rewrite: authority: bf.example.com route: - destination: host: dummy.com port: number: 80
配置说明:
- hosts字段指定了productpage服务。
- http字段定义了HTTP路由规则。
- rewrite字段将请求的authority头改写为bf.example.com,确保请求能够正确到达老集群的网关。
- route字段指定了将流量转发到dummy.com,即老集群的网关,使用80端口。
注意: rewrite字段是必须的,因为它确保了请求的Host头能够正确指向老集群的网关,否则请求可能无法正确到达老集群的服务。
此时通过新集群中的sleep应用向productpage服务发起请求,可以发现虽然集群中没有部署productpage,但是通过流量转发到老网关的方式,访问仍然可以成功。
kubectl exec -it deploy/sleep -- curl productpage:9080/productpage|grep title <title>Simple Bookstore App</title>
5. 总结
通过以上配置,我们可以在服务发现不通的情况下,将新集群的流量转发到老集群,实现平滑迁移。以下是迁移过程中需要重点关注的部分:
环境准备:确保老集群和新集群中的服务和路由配置正确。
ServiceEntry配置:正确注册老集群网关的地址,确保ASM能够识别和路由到老集群。
VirtualService配置:正确配置兜底路由和具体服务的转发规则,确保流量能够正确转发。
rewrite字段:在转发具体服务时,必须配置rewrite字段,确保请求的Host头正确。