You can connect Spring Cloud applications to Service Mesh (ASM) so that you can use cloud-native service governance capabilities to manage Spring Cloud services without modifying application code. This topic describes how to use ASM to manage Spring Cloud services.
Prerequisites
An ASM instance of Enterprise Edition or Ultimate Edition is created. For more information, see Create an ASM instance.
An ACK managed cluster is created. For more information, see Create an ACK managed cluster.
The cluster is added to the ASM instance. For more information, see Add a cluster to an ASM instance.
An ingress gateway is deployed. For more information, see Create an ingress gateway.
Background information
Spring Cloud is a standard with different implementations, such as Spring Cloud Netflix, Spring Cloud Alibaba, and Spring Cloud Consul. For ASM, the core difference among Spring Cloud implementations lies in the service registries. The following table specifies whether applications developed using these Spring Cloud implementations can be migrated to ASM.
Spring Cloud implementation | Service registry | Whether migration is supported without code modification |
Spring Cloud Alibaba | Microservices Engine (MSE) Nacos (an Alibaba Cloud service) | Yes |
Spring Cloud Alibaba | Self-managed Nacos | Yes |
Spring Cloud Netflix | Eureka | Supported. The version of the ASM instance must be 1.13.4.53 or later. |
Spring Cloud Consul | Consul | Supported. The version of the ASM instance must be 1.13.4.53 or later. |
Spring Cloud Zookeeper | Zookeeper | Supported. The version of the ASM instance must be 1.13.4.53 or later. |
Demo introduction
In this example, Spring Cloud with the Nacos registry is used. You can download the demo sample code from the nacos-examples repository on GitHub.
Spring Cloud services include a consumer service and a provider service. The provider service has two versions: V1 and V2. Both versions of the provider service are registered with the Nacos registry. The consumer service obtains the endpoints of the two versions of the provider service from the Nacos registry and sends requests to the endpoints in a load-balancing manner. The consumer service exposes port 8080 and provides an echo interface. After the requests are forwarded to the provider service, the provider service returns corresponding responses, and then the consumer service delivers the responses. Different versions of the provider service return different responses.
The provider service of V1 responds to an echo request with the following information:
Hello Nacos Discovery From v1xxx
.The provider service of V2 responds to an echo request with the following information:
Hello Nacos Discovery From v2xxx
.
The xxx
string in a response indicates the specific parameter in the corresponding echo request. For example, if the /echo/world request is sent to the provider service of V1, the Hello Nacos Discovery From v1world
response is returned.
If the consumer and provider services are not deployed in ASM instances, that is, sidecar proxy injection is not enabled for the service pods, you can access the services but you cannot manage the services by using Istio resources.
Step 1: Enable support for Spring Cloud on the ASM control plane
Method 1: Applicable to all Spring Cloud implementations and registries
The version of the ASM instance must be 1.13.4.32 or later.
Use kubectl to connect to the cluster on the control plane. For more information, see Use kubectl on the control plane to access Istio resources.
Create an Envoy filter in the ACK cluster that is added to the ASM instance.
Create an any-spring-cloud-support.yaml file that contains the following content:
apiVersion: networking.istio.io/v1alpha3 kind: EnvoyFilter metadata: labels: provider: "asm" asm-system: "true" name: any-spring-cloud-support namespace: istio-system spec: configPatches: - applyTo: HTTP_FILTER match: proxy: proxyVersion: "^1.*" context: SIDECAR_OUTBOUND listener: portNumber: 8070 filterChain: filter: name: "envoy.filters.network.http_connection_manager" subFilter: name: "envoy.filters.http.router" patch: operation: INSERT_BEFORE value: # reverse_dns filter specification name: com.aliyun.reverse_dns typed_config: "@type": "type.googleapis.com/udpa.type.v1.TypedStruct" type_url: type.googleapis.com/envoy.config.filter.reverse_dns.v3alpha.CommonConfig value: pod_cidrs: - "10.0.128.0/18"
Modify the parameter settings in the file based on your business requirements. Some parameters are described as follows:
portNumber
: specifies the port number of the Spring Cloud service. If there are multiple ports, you can delete this parameter. If the ports can be converged, you can create multiple Envoy filters and configure a port number in each Envoy filter.pod_cidrs
: specifies the pod CIDR block of the ACK or ACK Serverless cluster. Log on to the Container Service for Kubernetes (ACK) console, go to the Clusters page and click the desired cluster. On the cluster details page that appears, click the Cluster Resources tab. Then, click the link in the VPC section to view the CIDR block of the vSwitch in the VPC.
Run the following command to enable the
com.aliyun.reverse_dns
filter for the service:kubectl apply -f any-spring-cloud-support.yaml
Method 2: Applicable to only Nacos registries
Use kubectl to connect to the cluster on the control plane. For more information, see Use kubectl on the control plane to access Istio resources.
Create a service entry.
Create an external-nacos-svc.yaml file that contains the following content:
kind: ServiceEntry metadata: name: external-nacos-svc spec: hosts: - "NACOS_SERVER_HOST" ## Replace this variable with the endpoint of your Nacos server host. Example: mse-xxx-p.nacos-ans.mse.aliyuncs.com. location: MESH_EXTERNAL ports: - number: 8848 name: http resolution: DNS
In the preceding YAML file, port
8848
is the default port used by Nacos. If you use a self-managed Nacos server and a different port, set thenumber
parameter to the port number that you use.Run the following command to create the service entry:
kubectl apply -f external-nacos-svc.yaml
Create an Envoy filter.
Create an external-envoyfilter.yaml file that contains the following content:
apiVersion: networking.istio.io/v1alpha3 kind: EnvoyFilter metadata: labels: provider: "asm" asm-system: "true" name: nacos-subscribe-lua namespace: istio-system spec: configPatches: # The first patch adds the lua filter to the listener/http connection manager. - applyTo: HTTP_FILTER match: proxy: proxyVersion: "^1.*" context: SIDECAR_OUTBOUND listener: portNumber: 8848 filterChain: filter: name: "envoy.filters.network.http_connection_manager" subFilter: name: "envoy.filters.http.router" patch: operation: INSERT_BEFORE value: # lua filter specification name: envoy.lua typed_config: "@type": "type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua" inlineCode: | -- copyright: ASM (Alibaba Cloud ServiceMesh) function envoy_on_request(request_handle) local request_headers = request_handle:headers() -- /nacos/v1/ns/instance/list?healthyOnly=false&namespaceId=public&clientIP=10.122.63.81&serviceName=DEFAULT_GROUP%40%40service-provider&udpPort=53174&encoding=UTF-8 local path = request_headers:get(":path") if string.match(path,"^/nacos/v1/ns/instance/list") then local servicename = string.gsub(path,".*&serviceName.*40([%w.\\_\\-]+)&.*","%1") request_handle:streamInfo():dynamicMetadata():set("context", "request.path", path) request_handle:streamInfo():dynamicMetadata():set("context", "request.servicename", servicename) request_handle:logInfo("subscribe for serviceName: " .. servicename) else request_handle:streamInfo():dynamicMetadata():set("context", "request.path", "") end end function envoy_on_response(response_handle) local request_path = response_handle:streamInfo():dynamicMetadata():get("context")["request.path"] if request_path == "" then return end local servicename = response_handle:streamInfo():dynamicMetadata():get("context")["request.servicename"] response_handle:logInfo("modified response ip to serviceName:" .. servicename) local bodyObject = response_handle:body(true) local body= bodyObject:getBytes(0,bodyObject:length()) body = string.gsub(body,"%s+","") body = string.gsub(body,"(ip\":\")(%d+.%d+.%d+.%d+)","%1"..servicename) response_handle:body():setBytes(body) end
Run the following command to create an Envoy filter:
kubectl apply -f external-envoyfilter.yaml
Step 2: Deploy Spring Cloud services in an ACK cluster
Make sure that an Envoy filter is created before you create deployments so as to intercept the registration process. If specific deployments are created before you create the Envoy filter, you must enable rolling updates for the deployments.
For Spring Cloud services, you must create Kubernetes service resources and provide a cluster IP address.
Use kubectl to connect to the cluster on the data plane. For more information, see Obtain the kubeconfig file of a cluster and use kubectl to connect to the cluster.
Run the following command to deploy Spring Cloud services:
export NACOS_ADDRESS=xxxx # Replace xxxx with the endpoint of the MSE Nacos registry or a self-managed Nacos registry. We recommend that you use the endpoint of a virtual private cloud (VPC). wget https://alibabacloudservicemesh.oss-cn-beijing.aliyuncs.com/asm-labs/springcloud/demo.yaml -O demo.yaml sed -e "s/NACOS_SERVER_CLUSTERIP/$NACOS_ADDRESS/g" demo.yaml |kubectl apply -f -
Run the following command to check the Spring Cloud services:
kubectl get pods
Expected output:
consumer-bdd464654-jn8q7 2/2 Running 0 25h provider-v1-66bc67fb6d-46pgl 2/2 Running 0 25h provider-v2-76568c45f6-85z87 2/2 Running 0 25h
Step 3: Create an Istio gateway and a virtual service
Create an Istio gateway.
Create a test-gateway.yaml file that contains the following content:
apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: test-gateway spec: selector: istio: ingressgateway # use istio default controller servers: - port: number: 80 name: http protocol: HTTP hosts: - "*"
Use kubectl to connect to the ASM instance based on the information in the kubeconfig file, and then run the following command to create an Istio gateway:
kubectl apply -f test-gateway.yaml
Create a virtual service.
Create a consumer.yaml file that contains the following content:
apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: consumer spec: hosts: - "*" gateways: - test-gateway http: - match: - uri: prefix: / route: - destination: host: consumer.default.svc.cluster.local port: number: 8080
Run the following command to create a virtual service:
kubectl apply -f consumer.yaml
Step 4: Check whether ASM can manage the Spring Cloud services
Query the IP address of the ingress gateway.
Log on to the ASM console. In the left-side navigation pane, choose .
On the Mesh Management page, click the name of the ASM instance. In the left-side navigation pane, choose .
On the Ingress Gateway page, view the Service address of the ingress gateway.
Run the following command to initiate requests from the ingress gateway to the Spring Cloud consumer service:
curl <IP address of the ingress gateway>/echo/world
Expected output:
Hello Nacos Discovery From v1world Hello Nacos Discovery From v2world Hello Nacos Discovery From v1world Hello Nacos Discovery From v2world
The output shows that traffic is routed to V1 and V2 of the provider service in polling mode by default.
Create a destination rule and a virtual service.
Create a service-provider.yaml file that contains the following content:
--- apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: service-provider spec: host: service-provider subsets: - name: v1 labels: label: v1 - name: v2 labels: label: v2
Run the following command to create a destination rule:
kubectl apply -f service-provider.yaml
Create a service-provider1.yaml file that contains the following content.
The virtual service to be created defines that /echo/hello requests are routed to the provider service of V1 and other requests are routed to the provider service of V2.
apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: service-provider spec: hosts: - service-provider http: - name: "hello-v1" match: - uri: prefix: "/echo/hello" route: - destination: host: service-provider subset: v1 - name: "default" route: - destination: host: service-provider subset: v2
Run the following command to create a virtual service:
kubectl apply -f service-provider1.yaml
Run the following command to initiate requests to the Spring Cloud consumer service:
curl <IP address of the ingress gateway>/echo/hello
Expected output:
Hello Nacos Discovery From v1hello Hello Nacos Discovery From v1hello
The output shows that
/echo/hello
requests are routed to the provider service of V1 and other requests are routed to the provider service of V2. This indicates that Spring Cloud traffic is taken over by Istio and Custom Resource Definitions (CRDs) provided by Istio can be used to configure routing rules. In this case, ASM can manage the Spring Cloud services.
FAQ
What do I do if the Spring Cloud services that I deployed do not take effect?
Check whether traffic blocking is enabled for the port or IP address of Nacos.
If you use reverse DNS lookup, you must block the IP address of the pod.
If you use Lua scripts, you must block the Nacos server IP address and cluster IP address.
Make sure that an Envoy filter is created before you create deployments so as to intercept the registration process. If specific deployments are created before you create the Envoy filter, you must enable rolling updates for the deployments.
Check whether you have created Kubernetes service resources and provided a cluster IP address for the Spring Cloud services.
View the method of enabling support for Spring Cloud on the ASM control plane.
Check the sidecar version of the services. If the sidecar image version is earlier than 1.13.4.32, you may have updated only the control plane of the ASM instance but not updated the data plane. In this case, enable rolling updates for the deployments of the services.