Docker and Jenkins: Perfecting Your CI/CD Pipeline

Docker and Jenkins: Perfecting Your CI/CD Pipeline

Unlocking the Benefits of Docker and Jenkins Integration

Continuous integration and continuous deployment (CI/CD) are two of the most important principles in software development. They enable developers to automate testing and build triggers into their workflow so that any changes to code or project documentation are automatically verified, documented, and deployed.

In this article, we will explore how Docker and Jenkins integrating with one another makes perfect sense for a CI/CD pipeline.

We will attempt to simulate a small portion of the DevOps workflow in real-world scenarios. It entails creating a NodeJS application with a large team of developers. The application must be containerized, and the image must be uploaded to the team's Dockerhub repository. Every time the source code is modified, the developers should be able to pull and work on the updated images.

Understanding the workflow

The goal is to automate the process of pushing updated images to Dockerhub whenever there are changes to the application. This ensures that the team is always working with the most up-to-date source code. The pipeline handles the entire process of creating a new image, adding a tag to it, and pushing it to the repository.

The workflow steps are as follows:

  1. Create a GitHub repository and publish a basic NodeJS application.

  2. Create a pipeline project in Jenkins

  3. Make new commits to the source code on GitHub

  4. Allow Jenkins to poll the SCM and generate a new Docker image each time.

  5. Using the pipeline, upload the Docker image to Dockerhub.

Configuring Jenkins and Docker credentials

Let's begin with setting up the credentials required for Jenkins to communicate with Docker.

  1. Navigate to Jenkins -> Manage Jenkins -> Credentials -> System -> Global Credentials

  2. Select "Add Credentials."

  3. Choose "kind" as Username and Password, Scope as global

  4. Enter the username of Dockerhub

  5. Go to Account Settings in Dockerhub, Security, and click on "New Access Token" to get the password.

  6. Create the token after adding a description. Close the window after copying the access token.

  7. Add this access token in the password field of the Jenkins credentials

Enter an ID for the newly entered credentials and a valid description of where the credentials will be used.

Creating a GitHub repository

Fork this repository to get all the source code of NodeJS app, the Dockerfile, and the Jenkinsfile.

Creating a Pipeline in Jenkins

Now let us configure the pipeline

  1. Create a new pipeline project in Jenkins

  2. Add a description

  3. In the Build Trigger section, check the box next to Poll SCM.

  4. Go to the pipeline section

  5. Choose Pipeline script from SCM in the "Definition field."

  6. Select Git as the SCM and enter the repository URL.

  7. Add valid credentials (blank if the repository is public)

  8. Add the branch name and the Jenkinsfile path.

  9. Click Apply, then Save

  10. Run the pipeline to get a stage view of all the pipeline stages.

  11. Verify the image is pushed on the Dockerhub

Additional Optimisation

We can also use GitHub webhooks, which will trigger a "build" job in Jenkins every time a new commit is pushed on the repository. This is a more efficient method of integrating new code.

Understanding the Jenkinsfile

The pipeline script for automating the workflow is included in the Jenkinsfile. It is made up of several stages and environment variables that will aid in the setup.

The stages of the Jenkinsfile are as follows :

  1. Checkout the code from Source Code Management

  2. Build the Docker image

  3. Log in to Dockerhub using your valid credentials.

  4. Push the image to the Dockerhub repository

  5. Logout of Dockerhub

// Jenkinsfile used to build an image of a node app, login to DockerHub and push the image to the repo

pipeline {
    agent any          // can run on any agent (master or slave)
    environment {
        DOCKERHUB_CREDENTIALS = credentials('credentialsID_jenkins')    
    }

    // the stages of the pipeline start
    stages {

        // individual stages 
        stage('SCM Checkout') {
            // steps to be executed inside each stage
            steps{
            git 'git_repository_url'
            }
        }

        stage('Build docker image') {
            steps {       
                bat 'docker build -t nodeapp .'
            }
        }

        stage('Login to Dockerhub') {

            // withCredentials is used to fetch the credentials of Docker   saved into Jenkins 
            steps{
                withCredentials([usernamePassword(credentialsId: 'id_from_jenkins', passwordVariable: 'DOCKER_HUB_PASSWORD', usernameVariable: 'DOCKER_HUB_USERNAME')]) {
                bat "docker login -u $DOCKER_HUB_USERNAME -p $DOCKER_HUB_PASSWORD"
                bat "docker tag nodeapp dockerhub_username/nodeapp:latest"               
                }
            }
        }

        stage('Push the Image') {
            steps{
                bat "docker push dockerhub_username/nodeapp:latest"
            }
        }
    }
//post actions, which will always be executed, no matter the result of previous stages
post {
        always {
            bat 'docker logout'
        }
    }
}

Add the credentials ID of the previously configured Jenkins credentials. The "passwordVariable" and "usernameVariable" variables will be retrieved in the "Login to Dockerhub" stage and stored in "DOCKER HUB USERNAME" and "DOCKER HUB PASSWORD". To push a Docker image to the Dockerhub repository, it must be tagged with the Dockerhub username.

For security reasons, the pipeline will always perform the docker logout stage. This is done to tidy up the workspace.

Checking the successful workflow

The new commit will trigger the pipeline and the image will be built, tagged, and pushed on the Dockerhub. Now, whenever the developers will commit new code on GitHub, a brand new image with an updated tag will be built and pushed to the repository. Team members can now download these images to their local machines, run the application inside the container, and continue the development process.

Stage view of a successful pipeline build

Image pushed to Dockerhub repository

Conclusion

Every development team strives to add features that benefit the clients. However, the developers want to concentrate solely on the coding aspect. Automation's role is to ensure that quality time is spent on development rather than manual tasks. In this case, the role of DevOps becomes critical.

Docker and Jenkins integration is not just a good idea. It is a necessity in software development. If you are new to these technologies, you can start by checking out a bunch of YouTube videos on this topic

This workflow was just one of several other steps in the DevOps process. That's all for this one. Feel free to leave a comment below.

Thank you for reading, and I wish you all the best in your Docker and Jenkins endeavors!