All Products
Search
Document Center

Microservices Engine:Implement an end-to-end canary release based on Spring Boot applications

Last Updated:May 06, 2025

Java agents support end-to-end canary release based on Kubernetes service discovery. Spring Boot applications are unable to connect to third-party registries. Due to this reason, Spring Boot applications deployed in Kubernetes clusters call other applications based on Kubernetes service discovery. This topic describes how to use Microservices Engine (MSE) to implement an end-to-end canary release for Spring Boot applications deployed in Kubernetes clusters.

Prerequisites

Limits

Important

This feature is in public preview and is only available in the following regions: China (Beijing), China (Shanghai), China (Hangzhou), China (Shenzhen), China (Zhangjiakou), US (Silicon Valley), and Singapore. Other regions are not supported.

Demo overview

In this demo, applications are deployed in the ACK console. The following figure shows the architecture of demo applications. Application call scenarios involve discovery of Spring Cloud applications based on Nacos instances and discovery of Spring Boot applications based on Kubernetes services.

  • Gateway: works as a Spring Cloud gateway. It is used as an ingress for backend traffic and calls Application A based on Nacos service discovery.

  • A: a Spring Cloud application. Its nodes are registered with a Nacos instance for service discovery. Application A calls Application B based on Kubernetes service discovery and calls Application D based on Nacos service discovery.

  • B: a Spring Boot application. It calls Application D based on Kubernetes service discovery.

  • D: a Spring Cloud application. Its nodes are registered with a Nacos instance for service discovery.

image

Procedure

Step 1: Deploy base and canary applications

  1. Log on to the ACK console.

  2. In the left-side navigation pane, click Clusters. Then, click the name of the cluster that you want to manage.

  3. In the left-side navigation pane, choose Workloads > Deployments.

  4. In the upper part of the page, select the namespace of the cluster, and click Create from YAML.

  5. Use the following YAML code to deploy applications.

    Show YAML code

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nacos-server
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: nacos-server
      template:
        metadata:
          labels:
            msePilotAutoEnable: "off"
            app: nacos-server
        spec:
          containers:
            - name: nacos-server
              image: "registry.cn-hangzhou.aliyuncs.com/mse-governance-demo/nacos-server:v2.1.2"
              env:
                - name: MODE
                  value: standalone
                - name: JVM_XMS
                  value: 512M
                - name: JVM_XMX
                  value: 512M
                - name: JVM_XMN
                  value: 256M
              imagePullPolicy: Always
              livenessProbe:
                failureThreshold: 3
                initialDelaySeconds: 15
                periodSeconds: 10
                successThreshold: 1
                tcpSocket:
                  port: 8848
                timeoutSeconds: 3
              readinessProbe:
                failureThreshold: 5
                initialDelaySeconds: 15
                periodSeconds: 15
                successThreshold: 1
                tcpSocket:
                  port: 8848
                timeoutSeconds: 3
              resources:
                requests:
                  cpu: '1'
                  memory: 2Gi
          dnsPolicy: ClusterFirst
          restartPolicy: Always
    
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: nacos-server
    spec:
      type: ClusterIP
      ports:
        - name: nacos-server-8848-8848
          port: 8848
          protocol: TCP
          targetPort: 8848
        - name: nacos-server-9848-9848
          port: 9848
          protocol: TCP
          targetPort: 9848
      selector:
        app: nacos-server
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: spring-cloud-gateway
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: spring-cloud-gateway
      strategy:
        rollingUpdate:
          maxSurge: 100%
          maxUnavailable: 25%
        type: RollingUpdate
      template:
        metadata:
          labels:
            msePilotAutoEnable: "on"
            msePilotCreateAppName: "spring-cloud-gateway"
            mseNamespace: "mse-springboot-demo"
            app: spring-cloud-gateway
            sidecar.istio.io/inject: 'false'
            aliyun.com/agent-version: "4.2.5-proxyless"
        spec:
          containers:
            - image: "registry.cn-hangzhou.aliyuncs.com/mse-governance-demo/spring-cloud-gateway:3.1.0-heterogeneous"
              imagePullPolicy: Always
              env:
                - name: nacos.host
                  value: "nacos-server"
                - name: nacos.namespace
                  value: "public"
              name: "spring-cloud-gateway"
              resources:
                requests:
                  cpu: "1"
                  memory: "2Gi"
                limits:
                  cpu: "1"
                  memory: "2Gi"
              ports:
                - name: http-port
                  containerPort: 20000
    ---
    apiVersion: v1
    kind: Service
    metadata:
      annotations:
        service.beta.kubernetes.io/alibaba-cloud-loadbalancer-spec: slb.s1.small
        service.beta.kubernetes.io/alicloud-loadbalancer-address-type: internet
      name: spring-cloud-gateway-slb
    spec:
      ports:
        - port: 80
          protocol: TCP
          targetPort: 20000
      selector:
        app: spring-cloud-gateway
      type: LoadBalancer
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: spring-cloud-a
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: spring-cloud-a
      strategy:
        rollingUpdate:
          maxSurge: 100%
          maxUnavailable: 25%
        type: RollingUpdate
      template:
        metadata:
          labels:
            msePilotAutoEnable: "on"
            msePilotCreateAppName: "spring-cloud-a"
            mseNamespace: "mse-springboot-demo"
            app: "spring-cloud-a"
            sidecar.istio.io/inject: 'false'
            aliyun.com/agent-version: "4.2.5-proxyless"
        spec:
          containers:
            - name: spring-cloud-a
              image: "registry.cn-hangzhou.aliyuncs.com/mse-governance-demo/spring-cloud-a:3.1.0-heterogeneous"
              imagePullPolicy: Always
              env:
                - name: nacos.host
                  value: "nacos-server"
                - name: nacos.namespace
                  value: "public"
              resources:
                requests:
                  cpu: "1"
                  memory: "2Gi"
                limits:
                  cpu: "1"
                  memory: "2Gi"
              ports:
                - name: http-port
                  containerPort: 20001
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: spring-cloud-a-gray
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: spring-cloud-a
      strategy:
        rollingUpdate:
          maxSurge: 100%
          maxUnavailable: 25%
        type: RollingUpdate
      template:
        metadata:
          labels:
            msePilotAutoEnable: "on"
            msePilotCreateAppName: "spring-cloud-a"
            mseNamespace: "mse-springboot-demo"
            alicloud.service.tag: gray
            app: "spring-cloud-a"
            sidecar.istio.io/inject: 'false'
            aliyun.com/agent-version: "4.2.5-proxyless"
        spec:
          containers:
            - name: spring-cloud-a
              image: "registry.cn-hangzhou.aliyuncs.com/mse-governance-demo/spring-cloud-a:3.1.0-heterogeneous"
              imagePullPolicy: Always
              env:
                - name: nacos.host
                  value: "nacos-server"
                - name: nacos.namespace
                  value: "public"
              resources:
                requests:
                  cpu: "1"
                  memory: "2Gi"
                limits:
                  cpu: "1"
                  memory: "2Gi"
              ports:
                - name: http-port
                  containerPort: 20001
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: spring-cloud-a
    spec:
      ports:
        - port: 20001
          protocol: TCP
          targetPort: 20001
      selector:
        app: spring-cloud-a
      type: ClusterIP
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: spring-boot-b
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: spring-boot-b
      strategy:
        rollingUpdate:
          maxSurge: 100%
          maxUnavailable: 25%
        type: RollingUpdate
      template:
        metadata:
          labels:
            msePilotAutoEnable: "on"
            msePilotCreateAppName: "spring-boot-b"
            mseNamespace: "mse-springboot-demo"
            app: "spring-boot-b"
            sidecar.istio.io/inject: 'false'
            aliyun.com/agent-version: "4.2.5-proxyless"
        spec:
          containers:
            - name: spring-boot-b
              image: "registry.cn-hangzhou.aliyuncs.com/mse-governance-demo/spring-boot-b:3.1.0-heterogeneous"
              imagePullPolicy: Always
              resources:
                requests:
                  cpu: "1"
                  memory: "2Gi"
                limits:
                  cpu: "1"
                  memory: "2Gi"
              ports:
                - name: http-port
                  containerPort: 20002
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: spring-boot-b-gray
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: spring-boot-b
      strategy:
        rollingUpdate:
          maxSurge: 100%
          maxUnavailable: 25%
        type: RollingUpdate
      template:
        metadata:
          labels:
            msePilotAutoEnable: "on"
            msePilotCreateAppName: "spring-boot-b"
            mseNamespace: "mse-springboot-demo"
            alicloud.service.tag: gray
            app: "spring-boot-b"
            sidecar.istio.io/inject: 'false'
            aliyun.com/agent-version: "4.2.5-proxyless"
        spec:
          containers:
            - name: spring-boot-b
              image: "registry.cn-hangzhou.aliyuncs.com/mse-governance-demo/spring-boot-b:3.1.0-heterogeneous"
              imagePullPolicy: Always
              resources:
                requests:
                  cpu: "1"
                  memory: "2Gi"
                limits:
                  cpu: "1"
                  memory: "2Gi"
              ports:
                - name: http-port
                  containerPort: 20002
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: spring-boot-b
    spec:
      ports:
        - port: 20002
          protocol: TCP
          targetPort: 20002
      selector:
        app: spring-boot-b
      type: ClusterIP
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: spring-cloud-d
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: spring-cloud-d
      strategy:
        rollingUpdate:
          maxSurge: 100%
          maxUnavailable: 25%
        type: RollingUpdate
      template:
        metadata:
          labels:
            msePilotAutoEnable: "on"
            msePilotCreateAppName: "spring-cloud-d"
            mseNamespace: "mse-springboot-demo"
            app: "spring-cloud-d"
            sidecar.istio.io/inject: 'false'
            aliyun.com/agent-version: "4.2.5-proxyless"
        spec:
          containers:
            - name: spring-cloud-d
              image: "registry.cn-hangzhou.aliyuncs.com/mse-governance-demo/spring-cloud-d:3.1.0-heterogeneous"
              imagePullPolicy: Always
              env:
                - name: nacos.host
                  value: "nacos-server"
                - name: nacos.namespace
                  value: "public"
              resources:
                requests:
                  cpu: "1"
                  memory: "2Gi"
                limits:
                  cpu: "1"
                  memory: "2Gi"
              ports:
                - name: http-port
                  containerPort: 20004
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: spring-cloud-d-gray
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: spring-cloud-d
      strategy:
        rollingUpdate:
          maxSurge: 100%
          maxUnavailable: 25%
        type: RollingUpdate
      template:
        metadata:
          labels:
            msePilotAutoEnable: "on"
            msePilotCreateAppName: "spring-cloud-d"
            mseNamespace: "mse-springboot-demo"
            alicloud.service.tag: gray
            app: "spring-cloud-d"
            sidecar.istio.io/inject: 'false'
            aliyun.com/agent-version: "4.2.5-proxyless"
        spec:
          containers:
            - name: spring-cloud-d
              image: "registry.cn-hangzhou.aliyuncs.com/mse-governance-demo/spring-cloud-d:3.1.0-heterogeneous"
              imagePullPolicy: Always
              env:
                - name: nacos.host
                  value: "nacos-server"
                - name: nacos.namespace
                  value: "public"
              resources:
                requests:
                  cpu: "1"
                  memory: "2Gi"
                limits:
                  cpu: "1"
                  memory: "2Gi"
              ports:
                - name: http-port
                  containerPort: 20004
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: spring-cloud-d
    spec:
      ports:
        - port: 20004
          protocol: TCP
          targetPort: 20004
      selector:
        app: spring-cloud-d
      type: ClusterIP

Step 2: Configure the end-to-end canary release feature

  1. Log on to the MSE console, and select a region in the top navigation bar.

  2. In the left-side navigation pane, choose Microservices Governance > Full link grayscale.

  3. On the Full link grayscale page, select mse-springboot-demo from the microservice namespace drop-down list.

  4. If no lane group has been created in the selected microservice namespace, click Create Lane Group and Lane. If a lane group has already been created in the selected microservice namespace, click + Create Lane Group.

  5. In the Create Lane Group panel, configure the parameters, and click OK.

    Parameter

    Example

    Lane Group Name

    Enter a custom name for the lane group, such as mse-springboot-demo.

    Ingress Type

    Select Java Microservice Gateway.

    Lane Group Traffic Entry

    Select spring-cloud-gateway.

    Lane Group Application

    Select spring-cloud-a, spring-boot-b, and spring-cloud-d.

    image

  6. If no lane has been created in the selected microservice namespace, click Create First Split Lane in the lower part of the Full link grayscale page. If a lane has already been created in the selected microservice namespace, click Create Lane.

  7. In the Create Lane panel, configure the parameters, and click OK.

    Parameter

    Description

    Add Node Tag

    Add a tag for canary application nodes to distinguish them from base application nodes.

    Enter lane information

    Lane Name: Enter an informative lane name.

    Lane Tag: Enter a tag name for matched traffic in this lane. In this example, this parameter is set to gray.

    Confirm Matching Relationship: Check whether the number of application nodes that have the specified tag meets your expectations.

    Lane Status: Turn on the switch.

    Add Canary Release Rule

    Specify the rule for routing requests to application nodes in the lane.

    • Canary Release Mode: Select Canary Release by Content.

    • Canary Release Condition: Select Meet All Conditions.

      Configuration details in this example:

      • Parameter Type: Select Header.

      • Parameter: Enter x-springboot-demo.

      • Condition: Select ==.

      • Value: Enter 1.

    image

Step 3: Verify the result

  1. Log on to the ACK console.

  2. In the left-side navigation pane, click Clusters. Then, click the name of the cluster that you want to manage.

  3. In the left-side navigation pane of the Cluster Information page, choose Network > Services. On the Services page, copy the external IP address of spring-cloud-gateway-slb, and perform the following verification operations.

    Verify the result in the base environment

    Send a request to the Spring Cloud gateway based on the route /A/A/a, and check that all traffic is routed to the base nodes. This indicates that the end-to-end canary release configuration is effective.

    # Test command
    curl http://xxx.xx.131.81/A/A/a
    
    # Test result
    A:192.168.0.17:base -(java)- B:192.168.100.231:base - D:192.168.0.19:base
    Verify the result in the canary environment

    Send a request to the Spring Cloud gateway based on the route /A/A/a and add x-springboot-demo=1 to Header. Check that all traffic is routed to the canary nodes. This indicates that the end-to-end canary release configuration is effective.

    # Test command
    curl -H "x-springboot-demo:1" http://xxx.xx.131.81/A/A/a
    
    # Test result
    A:192.168.100.234:gray -(java)- B:192.168.0.21:gray - D:192.168.0.14:gray

    Tab 2 Body