CICD with Gitlab

CICD with Gitlab

Hello DevOps Enthusiasts! We are back with an article which shows an end-to-end CICD integration of a Java Springboot application using Gitlab.

🙏 Before we get into the article, Please like our Facebook page and follow us on twitter by using the links on the top right corner of this page! 🙏

Here is what we are are going to learn as part of this article.

About GitLab

GitLab is a web-based Git repository hosting service. It offers all of the distributed revision control and source code management (SCM) functionality of Git as well as adding its own features. It is an open source tool that combines the ability to develop, secure, deploy and operate software in a single YAML based file. The YAML based CICD approach makes it easy to read, understand, develop and maintain the build and deploy configuration for the application compared to other CICD tools like Jenkins.

Features of Gitlab:

  • Version control and repository management based on Git
  • Issue management, bug tracking and boards
  • Continuous Integration/Deployment tool (GitLab CI/CD)
  • Service Desk which is a ticketing system provided by GitLab
  • GitLab Wiki
  • Integration with various IDEs like Visual Studio, Eclipse, IntelliJ, etc.
  • Integration with various DevOps tools like JIRA, Jenkins, Slack, Docker, Kubernetes, etc.

Basic structure of Gitlab yaml file

Here is the code base which we are using in this article.

We can see a YAML file with the name.gitlab-ci.yml at the root of the repository. In the beginning of this file, we can see the list of stages we are going to use for this demo. Here we have 5 stages which will be executed one after the other. The environment variables which are common across all the stages can be defined under the variables section. In each stage, we can see the steps to execute under the script path. The gitlab spins up a new container to execute the script and we can give the image name in the image field.

We can create the strategy for your app and GitLab runs the pipeline according to what we have defined. The pipeline status is also displayed by GitLab under the “pipeline” field in CI/CD section. We can even rollback the changes if anything fails and if need be. Also, we will be creating all the required environment secrets such as Docker Authentication token, SonarCloud authentication token and kubeconfig files under settings -> CICD -> Variables.

SettingVariablesInGitlab
Setting Variables

GITLAB Pipeline stages of our application

cicd
Stages of the Application

The application is a Java Springboot application which uses Gradle for compilation and unit Testing, SonarCloud for source code analysis, Docker for creating a container image, AWS for hosting the Kubernetes cluster and helm charts for Kubernetes deployment.

Below table describes the different DevOps tools we are using here and its function.

Tool Name Function
GitLab CI CICD tool
Gradle Compilation and unit testing
SonarCloud Code coverage and code analysis
Docker Containerize the application and creates an image
DockerHub Docker image repository
AWS Hosts the application in cloud
Kubernetes Deploy the image in the server in the cluster as an application or a micro service
Helm Image (Package Manager) to orchestrate the k8s deployment

 

Build Stage

In this build job, we do a compile and perform unit test using gradle commands. We are also creating an artifact and we are setting expiration as 1 week. That means, the artifact will be available for 1 week after which it will be deleted from Gitlab’s workspace. We use this artifact to pass it as the workspace to the next jobs such as SonarQube analysis and Docker build and push jobs.

buildJob:
  stage: build
  image: openjdk:11-jdk
  script:
    - echo "Doing a gradle build."
    - chmod +x gradlew && ./gradlew build jacocoTestReport
  artifacts:
    paths:
    - build/*
    expire_in: 1 week

The output of the build stage is shown below:

Console Output of Build Job in GitLab
Build Job Output

Code Analysis Stage

After compilation stage is passed, it moves to the source code analysis stage using sonar cloud. We pass the required parameters for the sonar scanner. Note that we are generating the auth token from the sonar cloud and creating the environment variable called SONAR_AUTH_TOKEN and passing it for the sonar.login argument.

sonarqubeJob:
  stage: sonarqube
  image: sonarsource/sonar-scanner-cli
  dependencies: 
    - buildJob
  script:
    - echo "Doing a sonarqube scan"
    - sonar-scanner -Dsonar.projectKey=samplespringbootapp -Dsonar.sources=. -Dsonar.login=${SONAR_AUTH_TOKEN} -Dsonar.organization=kaviyaorg -Dsonar.host.url=${SONAR_HOST_URL} -Dsonar.java.binaries=build/classes/java/main
Console output of SonarCloud Job in GitLab
SonarCloud Stage

Containerization Stage

In this stage, we are creating a docker image and then tagging the image it with the required name and publishing it to the docker hub. Please note that we have pushed the image created to a public repo but in real world scenario, the created images will be published to the privately hosted repos. Also, we have passed the artifacts from the build stage to this stage as a dependency.

The before script argument is used to specify steps to do before the execution stage. Here, we are logging into the docker hub using the repo name and docker authentication token before doing a docker build and docker push.

dockerBuildAndPushJob:
  stage: dockerBuildAndPush
  image: docker:20.10.16
  dependencies: 
    - buildJob
  before_script:
  - docker login -u $DOCKER_REPO_NAME -p $DOCKER_AUTH_TOKEN
  script:
    - echo "In docker build stage"
    - docker build --tag "${DOCKER_REPO_NAME}/${CI_PROJECT_NAME}:${CI_COMMIT_SHORT_SHA}" --tag "${DOCKER_REPO_NAME}/${CI_PROJECT_NAME}:latest" .     
    - echo "In docker push stage"
    - docker push ${DOCKER_REPO_NAME}/${CI_PROJECT_NAME}:${CI_COMMIT_SHORT_SHA}
    - docker push ${DOCKER_REPO_NAME}/${CI_PROJECT_NAME}:latest

The output of this stage is shown below:

Docker Build and Push console output in GitLab Job
Docker Build and Push

Deployment Stage

We are going to use the Kubernetes cluster deployed in AWS using KOPS by referring our previous blog which is based on the official KOPS documentation.

We have two jobs for the deployment. One deploys to the Dev namespace in the kubernetes cluster and the next job will deploy to the “prod” namespace in the cluster.

We are using helm charts to deploy to kubernetes. Here is the official documentation for Helm installation. Inside the templates folder, there is one deployment.yaml file that will deploy the image to the cluster by creating a pod. Also we have a service.yaml file which will expose the pod to the port 8081. Please note that, in the service.yaml file, we have used the type LoadBalancer which will make use of the internal load balancer type provided by the cluster hosting solution, which is AWS in this case. Thus, we can see an Elastic Load Balancer (ELB) is created after the deployment.

What are helm charts and their advantages is perhaps a topic for another article 🙂 We have also committed the kubernetes.yaml file for reference if you want to deploy without the helm charts.

The commands to create namespaces are below:

kubectl create namespace dev
kubectl create namespace prod

Deployment to Dev Namespace

The deployment to Dev job will deploy to the dev namespace and the values required to deploy are coming from the values_dev.yaml file. We also export the kubeconfig file from the kubernetes cluster as a GitLab environment variable and exporting the value before the script execution using the before_script field. We do a helm upgrade while pointing to the location of the template files and which values.yaml file it should refer for the parameters.

Below is the code snippet for the deployment to dev job.

deployDevJob:
  stage: deploydev
  #image: 
  #  name: bitnami/kubectl
  #  entrypoint: ['']
  before_script:
    - export KUBECONFIG=$KUBECONFIG
  #script:
  #  - kubectl apply -f kubernetes.yaml
  image: 
    name: alpine/helm:3.2.1
    entrypoint: ['']
  script:
    - echo Deployment to Dev Region
    - helm upgrade ${APP_NAME} ./mychart --install --values=./mychart/values_dev.yaml --namespace=dev

Below is the console output:

Deployment to Dev Namespace in Kubernetes from gitlab CI
Console Output for Deployment to Dev Namespace

The application can be accessed by the AWS ELB url that is created automatically and appending port 8081 to it. Below is the output for the same.

Application running in Dev Kubernetes Namespace
Application running in Dev Namespace

Deploy to Prod Namespace:

This job deploys the docker image to the prod namespace of the k8s cluster in AWS using helm.

This deployment job executes only for the main branch as part of release strategy

The kubernetes deployment file will be passed from helm templates folder. The values for the deployment and service will be passed during run time using values_prod.yaml file in helm folder.

Below is the gitlab code for the deployment to prod job.

deployProdJob:
  stage: deployprod
  #image: 
  #  name: bitnami/kubectl
  #  entrypoint: ['']
  before_script:
    - export KUBECONFIG=$KUBECONFIG
  #script:
  #  - kubectl apply -f kubernetes.yaml
  image: 
    name: alpine/helm:3.2.1
    entrypoint: ['']
  script:
    - echo Deploy to Prod
    - helm upgrade ${APP_NAME} ./mychart --install --values=./mychart/values_prod.yaml --namespace=prod 
  rules:
    - if: $CI_COMMIT_BRANCH == "main"

Below is the console output for the deployment.

Deployment to Prod Namespace in Kubernetes from gitlab CI
Console Output for Prod Deployment Job

The application can be accessed by the AWS ELB url that is created automatically and appending port 8081 to it. Below is the output for the same.

Application running in PROD Namespace in Kubernetes from gitlab CI
Application running in Prod

Conclusion

We have reached the end of this article. Here, we learned how to integrate an application from end to end using gitlab and there by enabling CICD with ease. We can see how easy it is to integrate different tools with Gitlab and how simple is its YAML based CICD file.

Once again, all the codebase used in this article is available here! Feel free to fork and work on it and get your hands dirty!  Please share your thoughts, questions and suggestions in the comment section below.

Please subscribe to our blog to stay up to date on any new articles!

Copy link
Powered by Social Snap