All Products
Search
Document Center

Microservices Engine:Implement an end-to-end canary release by using CI/CD pipelines in Jenkins

Last Updated:Mar 12, 2024

You can use Jenkins to build CI/CD pipelines and implement an end-to-end canary release by using the CI/CD pipelines. After you use pipeline scripts to integrate the build, deployment, and test stages into one process, you can determine the subsequent steps that you need to perform based on the canary release result. If a new version is proven to be stable, adjust the routing rule to gradually increase the percentage of canary traffic that is routed to the new version until all traffic is routed to the new version. If an issue is found in the new version, immediately roll back to the old version and troubleshoot the issue. The entire process implements automated management from the build stage to the canary release stage. This ensures the security and stability of service updates. This topic describes how to implement an end-to-end canary release by using CI/CD pipelines in Jenkins.

Overall architecture

The following canary release policies are used in most cases:

  • A specific small percentage of traffic.

  • Specific rules, such as specific headers or cookies.

  • Tags that are used to mark canary traffic and displayed on the client or web browser. For example, you can use the tags that are carried in headers.

The following figure shows the canary release process of the demo described in this topic.

image

Preparations

Enable Microservices Governance for microservice applications in an ACK cluster

  1. Activate Microservices Governance. For more information, see Activate Microservices Governance.

  2. Enable Microservices Governance for microservice applications in a Container Service for Kubernetes (ACK) cluster. For more information, see Enable Microservices Governance for microservice applications in an ACK cluster.

Deploy demo applications

  1. Log on to the ACK console. In the left-side navigation pane, click Clusters.

  2. On the Clusters page, find the cluster that you want to manage and click the name of the cluster or click Details in the Actions column. The details page of the cluster appears.

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

  4. On the Deployments page, click Create from YAML.

  5. Configure the template and click Create.

    In this example, the following applications are deployed: spring-cloud-a (Application A), spring-cloud-b (Application B), spring-cloud-c (Application C), nacos-server, and spring-cloud-zuul. The nacos-server application is deployed as a registry and the spring-cloud-zuul application is deployed as an ingress application. The applications are called in the following order: spring-cloud-zuul -> spring-cloud-a -> spring-cloud-b -> spring-cloud-c.

    The spring-cloud-zuul application handles the default 100 QPS and additional 10 QPS for requests that are attached with the x-mse-tag: gray header. This header is a built-in canary header in MSE. If the x-mse-tag: gray header is attached after Microservices Governance is activated and the agent is installed, requests attached with this header are automatically routed to downstream nodes that are tagged with gray. You can replace gray with the tag values of other nodes based on your business requirements.

    • Yet Another Markup Language (YAML) code for the spring-cloud-zuul ingress application

      Show code

      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: spring-cloud-zuul
      spec:
        replicas: 1
        selector:
          matchLabels:
            app: spring-cloud-zuul
        template:
          metadata: 
            labels:
              app: spring-cloud-zuul
              msePilotCreateAppName: spring-cloud-zuul
          spec:
            containers:
              - name: spring-cloud-zuul
                image: registry.cn-shanghai.aliyuncs.com/yizhan/spring-cloud-zuul:1.0.1
                imagePullPolicy: Always
                ports:
                  - containerPort: 20000
    • YAML code for the base version of spring-cloud-a

      Show code

      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: spring-cloud-a
      spec:
        replicas: 2
        selector:
          matchLabels:
            app: spring-cloud-a
        template:
          metadata:
            labels:
              app: spring-cloud-a
              msePilotCreateAppName: spring-cloud-a
          spec:
            containers:
            - name: spring-cloud-a
              image: registry.cn-shanghai.aliyuncs.com/yizhan/spring-cloud-a:0.1-SNAPSHOT
              imagePullPolicy: Always
              ports:
              - containerPort: 20001
              livenessProbe:
                tcpSocket:
                  port: 20001
                initialDelaySeconds: 10
                periodSeconds: 30
    • YAML code for the base version of spring-cloud-b

      Show code

      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: spring-cloud-b
      spec:
        replicas: 2
        selector:
          matchLabels:
            app: spring-cloud-b
        strategy:
        template:
          metadata: 
            labels:
              app: spring-cloud-b
              msePilotCreateAppName: spring-cloud-b
          spec:
            containers:
            - name: spring-cloud-b
              image: registry.cn-shanghai.aliyuncs.com/yizhan/spring-cloud-b:0.1-SNAPSHOT
              imagePullPolicy: Always
              ports:
              - containerPort: 8080
              livenessProbe:
                tcpSocket:
                  port: 20002
                initialDelaySeconds: 10
                periodSeconds: 30
    • YAML code for the base version of spring-cloud-c

      Show code

      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: spring-cloud-c
      spec:
        replicas: 1
        selector:
          matchLabels:
            app: spring-cloud-c
        template:
          metadata:  
            labels:
              app: spring-cloud-c
              msePilotCreateAppName: spring-cloud-c
          spec:
            containers:
            - name: spring-cloud-c
              image: registry.cn-shanghai.aliyuncs.com/yizhan/spring-cloud-c:0.1-SNAPSHOT
              imagePullPolicy: Always
              ports:
              - containerPort: 20003
              livenessProbe:
                tcpSocket:
                  port: 20003
                initialDelaySeconds: 10
                periodSeconds: 30
    • YAML code for nacos-server

      Show code

      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: nacos-server
      spec:
        replicas: 1
        selector:
          matchLabels:
            app: nacos-server
        template:
          metadata:
            labels:
              app: nacos-server
          spec:
            containers:
            - env:
              - name: MODE
                value: standalone
              image: nacos/nacos-server:v2.2.0
              imagePullPolicy: Always
              name: nacos-server
            dnsPolicy: ClusterFirst
            restartPolicy: Always
      
      # Create a Server Load Balancer (SLB) instance for the spring-cloud-zuul application.
      ---
      apiVersion: v1
      kind: Service
      metadata:
        annotations:
          service.beta.kubernetes.io/alibaba-cloud-loadbalancer-spec: slb.s1.small
        name: zuul-slb
      spec:
        ports:
          - port: 80
            protocol: TCP
            targetPort: 20000
        selector:
          app: spring-cloud-zuul
        type: LoadBalancer
  6. After the applications are deployed, view the traffic that passes through Application A in the MSE console, and verify that all traffic is routed to untagged nodes and no traffic is routed to canary nodes.

Configure the permissions to push images to an image repository

In this example, the source code needs to be packaged into images and then pushed to an image repository. You must make sure that Jenkins has the permissions to push images to the image repository. For more information, see Set up Jenkins to build an application delivery pipeline..

Create a lane

  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, click Create Lane Group and Lane. If a lane group is available in the microservices namespace that you select, click + Create Lane Group.

  4. In the Create Lane Group panel, select the ingress applications and the applications involved in the call process, and click OK.

    Parameter

    Description

    Lane Group Name

    The custom name of the lane group.

    Ingress Type

    Select Java Microservice Gateway.

    Lane Group Traffic Entry

    Select your ingress application from the drop-down list.

    Lane Group Application

    Select all applications related to your ingress application or ingress gateway.

    Enable Message Canary Release

    If you turn on this switch, canary release for messaging is enabled for all applications in the lane group.

    • Client Filtering: All messages are pulled and filtered on the MSE agent that corresponds to the consumer. We recommend that you estimate the number of messages in advance.

    • Server Filtering: Messages are filtered on the server by using the SQL-92 expression. You must make sure that the RocketMQ broker support the SQL-92 expression.

    After the lane group is created, the lane group is displayed in the section of applications involved in the lane group on the Full link grayscale page. Check whether the ingress application and other involved applications are properly selected. To modify the information about the lane group, click the 编辑 icon on the right.

  5. On the Full link grayscale page, select the same microservice namespace as the lane group that you created. In the lower part of the page, click Click to Create First Split Lane.

    If a lane is available in the microservice namespace that you select, click Create Lane.

    Important

    After you configure an end-to-end canary release for applications, these applications no longer support features such as canary release and tag-based routing.

  6. In the Create Lane panel, configure the parameters and add the applications that match the rule to the gray lane. Then, click OK.

    Parameter

    Description

    Lane Name

    The custom name of the lane.

    Lane Tag

    Log on to the ACK console and add alicloud.service.tag: {tag} to spec.template.metadata.labels in the YAML file of the application.

    Add Application

    After the application tag is configured, click the refresh icon next to the drop-down list in the Add Application section to update tag information. Then, select a tag from the drop-down list. The tagged applications are automatically added.

    Routing rules

    • Path: the path to be matched. You can select multiple paths. If you do not specify this parameter, a random path is matched.

    • Condition Mode: the relationship between routing conditions.

    • Conditions

      • Parameter Type: the source of a parameter. Valid values: Parameter (request parameter), Header (request header), Cookie, and Body Content (JSON-formatted request body).

      • Parameter: the name of the parameter.

      • Condition: the match rule.

      • Value: the value that is used to match the parameter.

Important

Traffic that does not meet the conditions is routed to an untagged application node in the base version.

After the configuration is complete, the gateway routes traffic based on specific rules.

  • Traffic that does not meet the rule for canary release is routed to the base version.1

  • Traffic that meets the rule for canary release is routed to the canary version.1

Configure a Jenkins pipeline

In this example, the source code needs to be packaged into images and then pushed to an image repository. You must make sure that Jenkins has the permissions to push images to the image repository. For more information, see Set up Jenkins to build an application delivery pipeline.

Create a Secret named jenkins-docker-cfg in the Jenkins namespace by using the generated config.json file.

kubectl create secret generic jenkins-docker-cfg -n jenkins --from-file=/root/.docker/config.json

Create an end-to-end canary release pipeline in Jenkins

You can use Jenkins pipelines for automated releases. A pipeline can provide canary release, observability, and rollback capabilities for application releases to ensure production safety.

  1. In the left-side navigation pane of the Jenkins dashboard, click New Item.

  2. Enter the item name, select a pipeline, and then click OK.

  3. On the page that appears, click the Pipeline tab. On the Pipeline tab, configure the parameters, enter a path in the Script Path field, and then click Save.

    • Definition: Select Pipeline script from SCM from the drop-down list.

    • SCM: Select Git from the drop-down list.

    • Repository URL: Enter a URL of the Git repository. In this example, the repository URL is https://gitee.com/ralf0131/mse-demo.

      Note

      Alibaba Cloud cannot pull the source code from GitHub. In this case, the source code from Gitee is used instead.

    • Script Path: Enter Jenkinsfile.

      The following parameter configurations are provided for your reference. You can also edit a Jenkinsfile and add the file to the specified path of the Git repository. This path is the specified script path of the pipeline.

      Show code

      #!groovy
      pipeline {
      
          // Specify the tag of the build environment. In this example, slave-pipeline is used.
          agent{
              node{
                label 'slave-pipeline'
              }
          }
      
          // A constant parameter. In most cases, this parameter does not need to be modified after the parameter is specified.
          environment{
              IMAGE = sh(returnStdout: true,script: 'echo registry.$image_region.aliyuncs.com/$image_namespace/$image_reponame:$image_tag').trim()
              BRANCH =  sh(returnStdout: true,script: 'echo $branch').trim()
          }
          options {
              // Maintain the maximum number of builds.
              buildDiscarder(logRotator(numToKeepStr: '10'))
          }
      
          parameters {
              string(name: 'image_region', defaultValue: 'cn-shanghai')
              string(name: 'image_namespace', defaultValue: 'yizhan')
              string(name: 'image_reponame', defaultValue: 'spring-cloud-a')
              string(name: 'image_tag', defaultValue: 'gray')
              string(name: 'branch', defaultValue: 'master')
              string(name: 'number_of_pods', defaultValue: '2')
          }
      
          // Go through each stage of the pipeline.
          stages {
      
              stage('Code packaging') {
                  steps{
                      container("maven") {
                          echo "Image building......"
                          sh "cd A && mvn clean package"
                      }
      
                  }
              }
      
      
              stage('Image building and releasing'){
                steps{
                    container("kaniko") {
                        sh "kaniko -f `pwd`/A/Dockerfile -c `pwd`/A --destination=${IMAGE} --skip-tls-verify"
                    }
                }
              }
      
      
      
              stage('Canary deployment') {
                  steps{
                      container('kubectl') {
                          echo "Canary deployment......"
                          sh "cd A && sed -i -E \"s/${env.image_reponame}:.+/${env.image_reponame}:${env.image_tag}/\" A-gray-deployment.yaml"
                          sh "cd A && sed -i -E \"s/replicas:.+/replicas: ${env.number_of_pods}/\" A-gray-deployment.yaml"
                          sh "kubectl apply -f A/A-gray-deployment.yaml -n default"
                      }
                  }
              }
      
              stage('Completing canary deployment') {
                  input {
                      message "Are you sure that you want to enable a full release"
                      ok "OK"
                      parameters {
                          string(name: 'continue', defaultValue: 'true', description: 'true indicates a full release, and false indicates a rollback')
                      }
                  }
                  steps{
                      script {
                          env.continue = sh (script: 'echo ${continue}', returnStdout: true).trim()
                          if (env.continue.equals('true')) {
                              container('kubectl') {
                                  echo "Full releasing......"
                                  sh "cd A && sed -i -E \"s/${env.image_reponame}:.+/${env.image_reponame}:${env.image_tag}/\" A-deployment.yaml"
                                  sh "cd A && sed -i -E \"s/replicas:.+/replicas: ${env.number_of_pods}/\" A-deployment.yaml"
                                  sh "kubectl apply -f A/A-deployment.yaml -n default"
                              }
                          } else {
                              echo 'Rolling back'
                          }
                          container('kubectl') {
                              sh "kubectl delete -f A/A-gray-deployment.yaml -n default"
                          }
                      }
                  }
              }
          }
      }
                                          

Build a Jenkins pipeline

  1. On the Jenkins dashboard, click the 构建 icon.

  2. Click Build.

    Note

    If you build a Jenkins pipeline for the first time, you must pull the configuration from the Git repository and initialize the pipeline. If an error is reported during this process, you can click Build with Parameters to generate relevant parameters, specify the parameters, and then build the Jenkins pipeline again.

    View the deployment status. The code packaging, image building and releasing, and canary deployment stages are complete, and the stage for completing the canary deployment is pending confirmation.

Verify the result

  1. Log on to the ACK console. In the left-side navigation pane, click Clusters.

  2. On the Clusters page, click the name of the cluster that you want to manage and choose Workloads > Deployments in the left-side navigation pane.

  3. On the Deployments page, confirm that the created spring-cloud-a-gray application is displayed and its image version is replaced with the spring-cloud-a:gray version.

  4. On the details page of the cluster, choose Network > Services, select the required namespace, and then click the public endpoint of the zuul-slb service to view the actual call result.

    • If you use a non-canary header, traffic is routed to the nodes for the base version of spring-cloud-a.

      • curl command:

        curl http://182.92.XX.XX/A/a
      • Output:

        A[10.4.XX.XX] -> B[10.4.XX.XX] -> C[10.4.XX.XX]%
    • If you use a header with the canary release tag, traffic is routed to the canary node of spring-cloud-a.

      • curl command:

        curl http://182.92.XX.XX/A/a?name=xiaoming
      • Output:

        Agray[10.4.XX.XX] -> B[10.4.XX.XX] -> C[10.4.XX.XX]%
  5. Log on to the MSE console. On the application details page, confirm that canary traffic is routed to the canary node.

Perform a full release of the application

After the result passes verification, confirm to perform a full release of the application.

  1. On the Jenkins dashboard, click the name of the desired pipeline.

  2. Click the stage for which you want to enable full release, enter true in the Are you sure that you want to enable a full release dialog box, and then click OK.

  3. In the ACK console, confirm that the spring-cloud-a-gray application is deleted and its image version is replaced with the spring-cloud-a:gray version.

  4. In the MSE console, confirm that canary traffic disappears.

Roll back the application

Roll back the application if the result does not pass verification.

  1. On the Jenkins dashboard, click the name of the desired pipeline.

  2. Click the stage for which you want to disable full release, enter false in the Are you sure that you want to enable a full release dialog box, and then click OK.

  3. In the ACK console, confirm that the spring-cloud-a-gray application is deleted and its image version is still the original version.

  4. In the MSE console, confirm that canary traffic disappears.