Deploy Spring Boot in Openshift with Jenkins Pipeline

pipeline

In this document we are going to cover the steps of how the Continuous Integration and Deployment approach will take place with Gitlab , Jenkins and Openshift Cluster . We will take the recipe project created in previous article and deploy in openshift .

Git branching strategy

Here is the git branching strategy I have taken for the CI CD pipeline :

  • We will have a develop branch and feature branches .
  • developer can work and commit their code in feature branch
  • While merging to development branch , developer needs to raise a merge request and assign it to reviewer
  • When developer commits the code to feature branch , jenkins pipeline will automatically trigger build , unit test and run the static code analysis (i will not cover it here ) and tells the status of the build against the commit id .
  • Reviewer can review the code and merge it to develop branch only if the build in feature is successful . Auto merge of successful build is also possible but not covered here .
  • Once the code is merged to develop , the build in develop branch kicks off and creates and deploys the application in openshift cluster .

We are going to have a Jenkins file in each of recipe-service ,  recipe-cost and config . The github link is here :

https://github.com/jokumar/openshift-project

Multi branch Setup

Created 2 branches on top of master , development & feature-101. Feature-101 will have only static code analysis and development will have build and deployment .

Jenkins Pipeline Setup

I am going to use Jenkins blue ocean for the setup. I will be setting up a multi branch pipeline where any branch being created or changed would be detected by the pipeline scan provided the Jenkins file is present in the project.

Create new credentials and create pipeline. Once completed you should see all the branches available in the repository . If the repository has 2 branches and each of these branches has Jenkins file then you should see both the branches :

Setting Gitlab Token

Create a gitlab token and provide API access to the token. This token  will be used by Jenkins to send status notification during the pipeline execution

Go to Gitlab Account-> Settingsà Access Token

Give a name and select api :

Now go to Jenkins and Credentials .

Create a secret text in global credentials and name it as ‘token’. I will be using it in the jenkins script below :

Enter the id and copy the token from gitlab to Secret field here. Now Jenkins can talk to gitlab API with this token .

Merge request

Now as a developer I will be making some changes in feature-101 and raise a pull request to be merged in development.


The merge button will be only enable if the current pipeline that triggered from the commit is successful . This will let the reviewer know if the code is compiling successfully and all the test are successful.

Go to project settings -> general -> select “Only allow merge requests to be merged if the pipeline succeeds” :

Below pipeline failed and merge button is disabled:

Now committing the right code so that build passes:

See how it bypasses the deployment part and only performs the build part which includes test as well .

We see the merge  button enabled now . Also if we merge by selecting “delete source branch ” then the feature branch will be deleted from jenkins pipeline as well . 

It is also better to select squash commit for feature branch as we don’t want those commit history in our development branch .

On merging to development, the build in development will kick in and it will perform the complete deployment in openshift .


The same process will be applied for config-server ,recipe-cost and user-service project .

Jenkins Script in Stages

The jenkins file is present in the below location :

https://github.com/jokumar/openshift-project/blob/master/recipe-service/Jenkinsfile

I will explain here all the stages that is happening in the Jenkinsfile .

Stage 1 : Initiating the build where I send a running status to the last commit id . I am using this git command to retrieve the commit id : “git rev-parse HEAD “

 steps {
		   withCredentials([string(credentialsId: 'token', variable: 'token')]) {
				sh 'curl --request POST --header "PRIVATE-TOKEN: ${token}" $GITLAB_PROJECT_PATH"/statuses/$(git rev-parse HEAD)?state=running"'
			}
}

Stage 2 : Building the Spring boot code along with unit test . I am also passing the DB credentials which I have configured in Jenkins Credentials with name DB_CRED. I am passing the maven settings as a config file in jenkins through the script here .

steps {
					
					 withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: 'DB_CRED',
						usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD']]) {
						configFileProvider([configFile(fileId: 'maven-settings', variable: 'MAVEN_SETTINGS')]) {
								echo "Config: $MAVEN_SETTINGS"
								sh   'mvn -s $MAVEN_SETTINGS clean package  -Dspring.datasource.username=$USERNAME -Dspring.datasource.password=$PASSWORD -Dspring.profiles.active=dev  -Dspring.cloud.config.uri=$CONFIG_SERVER'
						}
					}
			}

Stage 3 : Create the build config with the open jdk image . There is a when condition to verify if the build config is not already existing . If it exists , this step will be skipped .

when {
				expression {
				  openshift.withCluster(CLUSTER_NAME) {
					  openshift.withProject( PROJECT_DEV ) {
							return (BRANCH_NAME == 'master' || BRANCH_NAME == 'development') && currentBuild.currentResult !='FAILURE' && !openshift.selector("bc",OPENSHIFT_PROJECT_NAME).exists();
							}
				  }
				}
 }
	steps {
				script {
				  openshift.withCluster(CLUSTER_NAME) {
					  openshift.withProject( PROJECT_DEV ) {
							openshift.newBuild("--name=${OPENSHIFT_PROJECT_NAME}", "--image-stream=redhat-openjdk18-openshift:1.1", "--binary")
							}
				  }
				}
}

Stage 4 : Use the build config to create an image of the springboot application on top of redhat-openjdk18 created in previous step

when{
				expression {
					return (BRANCH_NAME == 'master' || BRANCH_NAME == 'development') && currentBuild.currentResult !='FAILURE' ;
				}
		}
          
        steps {
           echo "Pushing The JAR Into OpenShift OpenJDK-Container"
			echo "${currentBuild.currentResult}"
            script {
                openshift.withCluster( CLUSTER_NAME ) {
                    openshift.withProject( PROJECT_DEV ) {
                        openshift.selector("bc", OPENSHIFT_PROJECT_NAME).startBuild("--from-file=target/" + JAR,"--wait")
                    }
               }
			  
              }
          }
    	  

Stage 5 : Tagging the latest image as dev .

 steps {
			script {
			  openshift.withCluster( CLUSTER_NAME ) {
			      openshift.withProject( PROJECT_DEV ) {
						openshift.tag(OPENSHIFT_PROJECT_NAME+":latest", OPENSHIFT_PROJECT_NAME+":dev")
					}
			  }
			}
		  }

Stage 6 : Create the App in Openshift . I am first deleting the build config , deployment config , service and route , if they already exist. I am also setting some of environment variables that are required by my spring boot app in run time.

 steps {
			script {
			  openshift.withCluster( CLUSTER_NAME ) {
			      openshift.withProject( PROJECT_DEV ) {
						if (openshift.selector('dc', '${OPENSHIFT_PROJECT_NAME}').exists()) {
							openshift.selector('dc', '${OPENSHIFT_PROJECT_NAME}').delete()
							openshift.selector('svc', '${OPENSHIFT_PROJECT_NAME}').delete()
							openshift.selector('route', '${OPENSHIFT_PROJECT_NAME}').delete()
						}

						openshift.newApp(OPENSHIFT_PROJECT_NAME+":latest", "--name="+OPENSHIFT_PROJECT_NAME).narrow('svc').expose()
						withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: 'DB_CRED',
							usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD']]) {
							openshift.raw( 'set env dc/$OPENSHIFT_PROJECT_NAME', 'spring.datasource.username=$USERNAME -e spring.datasource.password=$PASSWORD -e spring.profiles.active=dev -e spring.cloud.config.uri=$CONFIG_SERVER' )
						}
						
					}
			  }
			}
		  }
		}

I am passing the environment variable in the beginning of the script :

  environment {
     BRANCH_NAME = sh(returnStdout: true, script: 'git rev-parse --abbrev-ref HEAD').trim()
     CLUSTER_NAME = "https://console.geeks18.ie"
	 PROJECT_DEV="geeks18-dev" 
	 GITLAB_PROJECT_PATH="https://gitlab.geeks18/api/v4/projects/joydipkumar%2Frecipe-service"
	 OPENSHIFT_PROJECT_NAME="recipe-service"
	 sonar_project_name="recipe-service"
	 JAR="recipe-service-0.0.1-SNAPSHOT.jar"
	 PORT='8080'
	 CONFIG_SERVER="http://recipe-config-server-geeks18-dev.geeks18.ie"
  }

At the end of the build , I am sending the status if the build to the git commit:

post {
  
		//Send email and send the status to gitlab on success and failure at the end of build
  
      failure {
		 withCredentials([string(credentialsId: 'token', variable: 'token')]) {
			sh 'curl --request POST --header "PRIVATE-TOKEN:  ${token}" $GITLAB_PROJECT_PATH"/statuses/$(git rev-parse HEAD)?state=failed"'
		}

      }
      success {
		 withCredentials([string(credentialsId: 'token', variable: 'token')]) {
	    	sh 'curl --request POST --header "PRIVATE-TOKEN: ${token}" $GITLAB_PROJECT_PATH"/statuses/$(git rev-parse HEAD)?state=success"'
		}
		
	  }
	  
    }

Conclusion

In this article , we learnt how to set up a multi branch pipeline using Jenkins and how we can write declarative jenkin script to deploy a spring boot application to Openshift with CI CD.

Next article we will see how to use 3scale API gateway on top of these microservices

Digiprove sealCopyright secured by Digiprove © 2020 Geeks 18

Be the first to comment

Leave a Reply

Your email address will not be published.


*