Ruddra.com

Writing Jenkins Pipeline For OpenShift Deployment: Part Two

Writing Jenkins Pipeline For OpenShift Deployment: Part Two
Photo by Max Prieß on Unsplash

In previous article, we covered step 1-5 which would prepare the code and make sure it is ready to be deployed to OpenShift. Now we are going to use the next steps to deploy that code.

Step 6: create build configuration in OpenShift

After Step 5, we will have a nice compressed file containing what we need for building our app in openshift. Before start building the app, we need to have a Build Configuration inside OpenShift App. Basically Build Configuration is sort of a skeleton which contains instructions on how your source will be build. There are many strategies for build config. We will be using Docker strategy for building our code. That is why we have put the Dockerfile inside our compressed file. In build config, you need to provide from which source you want to build your code from. We will be using binary. Meaning we will provide a compressed file to openshift so that it can start building the image from it.

stage('Create Image Builder') {
    when {
        expression {
            openshift.withCluster() {
            openshift.withProject(DEV_PROJECT) {
                return !openshift.selector("bc", "${TEMPLATE_NAME}").exists();
                }
            }
        }
    }
    steps {
        script {
            openshift.withCluster() {
                openshift.withProject(DEV_PROJECT) {
                    openshift.newBuild("--name=${TEMPLATE_NAME}", "--docker-image=docker.io/nginx:mainline-alpine", "--binary=true")
                }
            }
        }
    }
}

This stage of pipeline will only execute once when the pipeline is executed for first time. Later when pipeline is executed for second time, third time… and so on, this stage will be skipped. Because build configuration is made only once. You can check if a build configuration is being created using oc get bc in terminal (in project dev) from your local machine. You can also check it using web interface of openshift in CI/CD projects > Builds. openshift.withCluster and openshift.withProject is basically openshift APIs open to Jenkins plugin. Using these APIs, you can execute commands inside openshift cluster and projects. Our new-build command is being executed inside DEV_PROJECT. Value of DEV_PROJECT is defined in environment variable, and the value is dev.

NB: Build Config, Deployment Config, Service, Image, Storage etc are Kubernetes basics. These terms will come more frequently in next stages. If you have some idea about them, it might help you to understand this tutorial even more. 😄

Step 7: build image

Outcome of a successful build is an Image. So after a successful build, you will see an image inside OpenShift in web interface(at OpenShift_Url > console > DEV Project > browse > images). The most recent build outcomes to image tagged by latest. This is an important information, which will come handy in our next steps. In our build configuration(defined in last step), we configured that the build will executed from binary. Meaning if will expect a archive file to start the build process. As we have a archive file from step 4, we will use that file for this purpose.

stage('Build Image') {
  steps {
      script {
          openshift.withCluster() {
          openshift.withProject(env.DEV_PROJECT) {
              openshift.selector("bc", "$TEMPLATE_NAME").startBuild("--from-archive=${ARTIFACT_FOLDER}/${APPLICATION_NAME}_${BUILD_NUMBER}.tar.gz", "--wait=true")
              }
          }
      }
  }
}

openshift.selector selects the build config named python-nginx (this value is set in ${TEMPLATE_NAME}). Using that build config, it initiates a build from the tar.gz file. After the build is complete, you will see an image named python-nginx and it should be tagged as latest.

Step 8: deploy to OpenShift in DEV

In this stage, we will deploy the image to DEV_PROJECT. Basically a Deployment Configuration is created in this step. Deployment Configuration is kind of like Build Config, but this is related to deployment. We will be using new-app to create our project from image built in last stage.

stage('Deploy to DEV') {
  when {
      expression {
        openshift.withCluster() {
          openshift.withProject(env.DEV_PROJECT) {
            return !openshift.selector('dc', "${TEMPLATE_NAME}").exists()
            }
        }
    }
  }
  steps {
    script {
        openshift.withCluster() {
            openshift.withProject(env.DEV_PROJECT) {
                def app = openshift.newApp("${TEMPLATE_NAME}:latest")
                app.narrow("svc").expose("--port=${PORT}");
                def dc = openshift.selector("dc", "${TEMPLATE_NAME}")
                while (dc.object().spec.replicas != dc.object().status.availableReplicas) {
                    sleep 10
                  }
              }
          }
      }
  }
}

This step is executed only in first time execution of pipeline. In next pipeline builds, this stage is skipped. But still, a deployment will be automatically done after each build is completed successfully. Because of Image Change Trigger. Meaning, after each new image, an automated deployment will initiate. New deployment means, it will generate new pods and will destroy the old pods. There are two strategies for this change, rolling strategy and recreate strategy. app.narrow("svc").expose("--port=${PORT}"); this command will use the service built by new-app; then expose its route to port 8081, this value is set in Environment variable as well. You can see the new service and ports as well using oc get svc and oc get routes in dev project from your local machine.

Step 9: promote image to STAGE

In this stage, jenkins will prompt you to promote or abort regarding if you want to continue deployment to stage project or not. If you click in promote(in Jenkins), then openshift promote the image created in step 6 to stage and tag it as promoteToQA(as per defined in environment).

stage('Promote to STAGE?') {
  steps {
      timeout(time:15, unit:'MINUTES') {
           input message: "Promote to STAGE?", ok: "Promote"
      }
      script {
          openshift.withCluster() {
          openshift.tag("${DEV_PROJECT}/${TEMPLATE_NAME}:latest", "${STAGE_PROJECT}/${TEMPLATE_NAME}:${STAGE_TAG}")
            }
        }
   }
}

Step 10: deploy to OpenShift in STAGE

In this step, we will be looking if any previous deployment is done in STAGE PROJECT. If it exists, then we will delete service, route, deployment config of that deployment, and create a new app using the new image promoted in last step. Then will expose the route for this application in 8081 port.

stage('Rollout to STAGE') {
  steps {
      script {
            openshift.withCluster() {
              openshift.withProject(STAGE_PROJECT) {
                  if (openshift.selector('dc', '${TEMPLATE_NAME}').exists()) {
                      openshift.selector('dc', '${TEMPLATE_NAME}').delete()
                      openshift.selector('svc', '${TEMPLATE_NAME}').delete()
                       openshift.selector('route', '${TEMPLATE_NAME}').delete()
                }
            openshift.newApp("${TEMPLATE_NAME}:${STAGE_TAG}").narrow("svc").expose("--port=${PORT}")
            }
         }
      }
  }
}

Step 11: scale in STAGE

We will be using openshiftScale API provided by Jenkins OpenShift Plugin. What it does is that, it creates replication of pods according the number of replication information provided to it via parameter replicaCount.

stage('Scale in STAGE') {
  steps {
      script {
          openshiftScale(namespace: "${STAGE_PROJECT}", deploymentConfig: "${TEMPLATE_NAME}", replicaCount: '3')
        }
    }
}

FYI: This API will be deprecated from oc version 3.11 This is the last step of our pipeline. Lets save it in a file named Jenkinsfile. Please commit and push your changes to your Git repository.

The Jenkins file should look like this code on github.

Conclusion

Lets discuss more about how to deploy our application using this pipeline in next article.

Feel free to comment if you find anything unclear. Thanks for reading. Cheers!!

Last updated: September 16, 2020


Share Your Thoughts
M↓ Markdown