All Products
Search
Document Center

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

Last Updated:Oct 31, 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, you can 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, you need to 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

In most cases, you can perform a canary release based on one of the following items:

  • 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 parameters 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 requests are attached with the x-mse-tag: gray header after Microservices Governance is activated and the agent is installed, the 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 Deploy Jenkins in an ACK cluster and then create and deploy an application.

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 application and the applications involved in the call process, and click OK.

    Parameter

    Description

    Lane Group Name

    Enter a name for 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.

    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 the involved applications are properly selected. To modify the information about the lane group, click 编辑 and edit the information.

  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

    Add Node Tag

    Manually add a tag for your canary application nodes. The tag is used to distinguish the nodes from other nodes.

    Enter lane information

    Lane Tag: the tag of the matched traffic in the lane.

    Confirm Matching Relationship: allows you to check whether the number of tagged application nodes is as expected.

    Configure Routing and Canary Release 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://github.com/aliyun/alibabacloud-microservice-demo/tree/master/mse-simple-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 构建 on the right of the pipeline.

  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, find the cluster that you want to manage and click its name. In the left-side pane, choose Workloads > Deployments.

  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 does not exist.