CI/CD Pipeline Deployment with Docker (Phase 2)

CI/CD Pipeline Deployment with Docker (Phase 2)

ยทPavan Kalyan Meda

Learn how to build and automate a complete CI/CD pipeline using Jenkins, GitHub, Maven, SonarQube, Docker, and Amazon ECR, then deploy applications with Docker containers.

CI/CD Pipeline Deployment with Docker (Phase 2)

Introduction

In Phase 2, you'll build a complete Continuous Integration and Continuous Deployment (CI/CD) pipeline using Jenkins, GitHub, Maven, and Docker.

Before implementing the pipeline, it's important to understand the core concepts behind Jenkins and CI/CD. These concepts provide the foundation for automating software build, testing, and deployment processes.


What Is Jenkins?

Jenkins is an open-source automation server written in Java that enables teams to automate software development tasks such as building, testing, and deploying applications.

It supports numerous plugins, allowing seamless integration with tools like:

  • GitHub
  • Maven
  • Docker
  • SonarQube
  • Kubernetes
  • AWS
  • Azure
  • Google Cloud

Jenkins plays a central role in implementing CI/CD pipelines by automating repetitive development tasks.


What Is a CI/CD Pipeline?

A Continuous Integration and Continuous Deployment (CI/CD) pipeline is an automated workflow that moves code from development to production through a series of predefined stages.

A typical CI/CD pipeline includes:

  1. Source Code Management
  2. Build
  3. Testing
  4. Code Quality Analysis
  5. Artifact Creation
  6. Deployment

Automating these stages improves software quality, accelerates delivery, and reduces manual errors throughout the software development lifecycle.


What Is a Jenkins Pipeline?

A Jenkins Pipeline is a collection of plugins that enables you to define and automate the entire software delivery process as code.

Instead of manually performing build and deployment tasks, Jenkins executes each stage automatically based on instructions defined in a Jenkinsfile.


Types of Jenkins Pipelines

Jenkins supports two pipeline syntaxes.

Pipeline TypeDescription
Declarative PipelineA structured and simplified syntax based on a Groovy Domain-Specific Language (DSL). It is easier to read, maintain, and recommended for most projects.
Scripted PipelineThe original Jenkins pipeline syntax written entirely in Groovy. It provides greater flexibility but requires more scripting and is generally more complex.

Declarative Pipeline

The Declarative Pipeline provides a clean, structured approach for defining CI/CD workflows.

Features include:

  • Simple syntax
  • Easy maintenance
  • Built-in validation
  • Recommended for most Jenkins projects

It uses a Groovy-based DSL that simplifies pipeline configuration.


Scripted Pipeline

The Scripted Pipeline is based entirely on the Groovy programming language.

It provides:

  • Complete control over pipeline execution
  • Advanced scripting capabilities
  • High flexibility for complex workflows

Because of its flexibility, Scripted Pipelines are generally more verbose and require a deeper understanding of Groovy.


What Is a Jenkinsfile?

A Jenkinsfile is a text file stored in your source code repository that defines the CI/CD pipeline.

Written in Groovy syntax, it describes every stage of the build process, such as:

  • Source code checkout
  • Dependency installation
  • Build
  • Testing
  • Code analysis
  • Docker image creation
  • Deployment

Using a Jenkinsfile enables Pipeline as Code, allowing version control and easier collaboration.


What Is Continuous Integration (CI)?

Continuous Integration (CI) is the practice of automatically building and testing software whenever developers commit code changes.

Each commit triggers automated processes that:

  • Compile the source code
  • Execute automated tests
  • Detect integration issues early
  • Prepare the application for deployment

CI helps maintain code quality and reduces integration problems during development.


Create a Jenkins Pipeline Project

After understanding the core concepts, create a new Jenkins Pipeline project.

  1. Open the Jenkins Dashboard.
  2. Click New Item.
  3. Enter a project name.
  4. Select Pipeline as the project type.
  5. Click OK.

Configure the Pipeline Project

Provide a suitable Project Name, select Pipeline, and click OK to create the project.

Configure GitHub Integration

What Is GitHub?

GitHub is a cloud-based Git repository hosting platform used for source code management, version control, and team collaboration.

Jenkins integrates with GitHub to automatically fetch source code and trigger pipeline executions.


Add the GitHub Repository

Inside the Pipeline configuration:

  1. Navigate to the Pipeline section.
  2. Provide the GitHub repository URL.

This repository serves as the source code for the Jenkins pipeline.


Configure GitHub Credentials

To securely access private repositories, configure GitHub credentials.

  1. Click Add beside the Credentials field.
  2. Enter your GitHub Username and Password or Personal Access Token (PAT).
  3. Save the credentials.

Jenkins will use these credentials whenever it communicates with the repository.


Specify the Jenkinsfile Path

Provide the Script Path that points to the Jenkinsfile within your repository.

Example:

Jenkinsfile  

If the Jenkinsfile is stored inside another directory, specify the relative path.

Example:

ci/Jenkinsfile  

Click Save after completing the configuration.


Configure Maven

What Is Maven?

Apache Maven is a build automation and dependency management tool for Java applications.

It simplifies project builds by:

  • Managing project dependencies
  • Compiling source code
  • Running tests
  • Packaging applications
  • Generating artifacts such as JAR and WAR files

Jenkins integrates with Maven to automate the build process.


Configure Maven in Jenkins

Navigate to the build stage of the Jenkins Pipeline and configure Maven as the build tool.



Build and Test Stage

The following stage builds the Java application and generates a JAR file.

stage('Build and Test') {  
    steps {  
        sh 'ls -ltr'

        // Build the project and create a JAR file  
        sh 'cd /var/lib/jenkins/workspace/My_First_Project/Files && mvn clean package'  
    }  
}  

What This Stage Does

CommandPurpose
ls -ltrDisplays the contents of the current working directory for verification.
mvn clean packageCleans previous builds, compiles the project, executes tests, and packages the application into a JAR file.

At the end of this stage, Maven produces the packaged application artifact, which can be used in the subsequent deployment stages of the CI/CD pipeline.


Next Step

With the build process configured successfully, the next stage is to integrate SonarQube into the Jenkins pipeline for automated code quality analysis and static code inspection.

Integrate SonarQube into the Jenkins Pipeline

What Is SonarQube?

SonarQube is an open-source platform for continuous code quality inspection. It performs static code analysis to identify issues in your source code before deployment.

SonarQube helps development teams detect:

  • Bugs
  • Security vulnerabilities
  • Code smells
  • Duplicate code
  • Maintainability issues
  • Technical debt

Integrating SonarQube into a Jenkins pipeline enables automated code quality checks as part of the CI/CD process.

Install the SonarQube Scanner Plugin

To integrate SonarQube with Jenkins, install the SonarQube Scanner plugin.

  1. Open the Jenkins Dashboard.
  2. Navigate to Manage Jenkins.
  3. Select Plugins.
  4. Search for SonarQube Scanner.
  5. Install the plugin.
  6. Restart Jenkins if prompted.


Configure SonarQube in Jenkins

After installing the plugin:

  1. Navigate to Manage Jenkins โ†’ System.
  2. Scroll to the SonarQube Servers section.
  3. Click Add SonarQube.

This section allows Jenkins to communicate with your SonarQube server.


Generate a SonarQube Authentication Token

Jenkins authenticates with SonarQube using a generated access token.

Access the SonarQube Dashboard

Open your SonarQube server in a web browser and sign in.

Create a User Token

From your SonarQube account:

  1. Open My Account.
  2. Navigate to Security.
  3. Select Generate Tokens.
  4. Enter a token name.
  5. Choose the token type.
  6. Set an expiration date if required.
  7. Click Generate.

Copy the generated token immediately, as it will not be displayed again.


Add the Token to Jenkins Credentials

Store the generated token securely in Jenkins.

  1. Open Manage Jenkins โ†’ Credentials.
  2. Add a new Secret Text credential.
  3. Paste the generated SonarQube token.
  4. Assign a Credential ID (for example, sonarqube).

Jenkins will use this credential during pipeline execution.

Add Static Code Analysis to the Jenkins Pipeline

Create a new stage named Static Code Analysis.

stage('Static Code Analysis') {  
    environment {  
        SONAR_HOST_URL = "http://13.210.222.6:9000"  
    }

    steps {  
        withCredentials([  
            string(  
                credentialsId: 'sonarqube',  
                variable: 'SONAR_AUTH_TOKEN'  
            )  
        ]) {

            sh '''  
                cd Files && mvn sonar:sonar \  
                    -Dsonar.login=$SONAR_AUTH_TOKEN \  
                    -Dsonar.host.url=${SONAR_HOST_URL}  
            '''  
        }  
    }  
}  

Static Code Analysis Workflow

Stage Configuration

Create a dedicated pipeline stage named Static Code Analysis to execute SonarQube scans.


Configure the SonarQube Server

Define the SonarQube server URL using the environment variable:

SONAR_HOST_URL  

This tells Maven where the SonarQube server is running.


Authenticate Securely

Retrieve the SonarQube access token from Jenkins Credentials using the Credentials Binding plugin.

This prevents sensitive authentication details from being exposed in the Jenkinsfile.


Execute Code Analysis

Run the Maven SonarQube plugin to analyze the project source code.

During analysis, SonarQube inspects the codebase for:

  • Bugs
  • Security vulnerabilities
  • Code smells
  • Duplicate code
  • Maintainability issues

Improve Code Quality

After analysis completes, review the SonarQube dashboard to identify and resolve issues before deployment.

Integrating SonarQube into the CI/CD pipeline helps enforce consistent code quality throughout the development lifecycle.


Integrate AWS Elastic Container Registry (Amazon ECR)

What Is Amazon ECR?

Amazon Elastic Container Registry (Amazon ECR) is a fully managed container image registry provided by AWS.

Amazon ECR enables you to:

  • Store Docker images securely.
  • Push and pull container images.
  • Integrate with Amazon ECS, Amazon EKS, and Kubernetes.
  • Manage image versions.
  • Scale container image storage automatically.

In this pipeline, Docker images are built locally and pushed to an Amazon ECR repository.


Create an Amazon ECR Repository

Before pushing Docker images, create a private repository.

  1. Sign in to the AWS Management Console.
  2. Search for Elastic Container Registry (ECR).
  3. Open the ECR service.
  4. Click Create Repository.
  5. Choose Private Repository.
  6. Enter a repository name.
  7. Create the repository.


Repository Created Successfully

Once the repository has been created successfully, open it and navigate to the View Push Commands section.

AWS provides Docker commands that can be incorporated into the Jenkins pipeline.


Build and Push Docker Images

Add the following stage to the Jenkins Pipeline.

stage('Build and Push Docker Image') {

    environment {  
        DOCKER_IMAGE = 'gettingstarted'  
        AWS_REGION = 'ap-southeast-2'  
        ECR_REGISTRY_URL = '339713116286.dkr.ecr.ap-southeast-2.amazonaws.com/my_first_project'  
    }

    steps {

        script {

            withCredentials([  
                [  
                    $class: 'AmazonWebServicesCredentialsBinding',  
                    credentialsId: 'Aws Credentials',  
                    accessKeyVariable: 'AWS_ACCESS_KEY_ID',  
                    secretKeyVariable: 'AWS_SECRET_ACCESS_KEY'  
                ]  
            ]) {

                sh "aws ecr get-login-password --region ${AWS_REGION} | docker login --username AWS --password-stdin ${ECR_REGISTRY_URL}"

            }

            sh "docker build -t ${DOCKER_IMAGE}:${BUILD_NUMBER} ./Files"

            sh "docker tag ${DOCKER_IMAGE}:${BUILD_NUMBER} ${ECR_REGISTRY_URL}:latest"

            sh "docker push ${ECR_REGISTRY_URL}:latest"

        }  
    }  
}  

Pipeline Workflow

The Build and Push Docker Image stage performs the following tasks:

StepDescription
Define Environment VariablesConfigure the Docker image name, AWS Region, and Amazon ECR repository URL.
Authenticate with Amazon ECRRetrieve AWS credentials securely from Jenkins and authenticate Docker with Amazon ECR.
Build Docker ImageBuild the Docker image from the application's source code.
Tag the ImageTag the image using the Amazon ECR repository URL.
Push to Amazon ECRUpload the Docker image to the private Amazon ECR repository.

At the end of this stage, the Docker image is securely stored in Amazon ECR and is ready for deployment.


Configure AWS Credentials in Jenkins

To allow Jenkins to authenticate with AWS services:

  1. Install the AWS Credentials Plugin.
  2. Navigate to Manage Jenkins โ†’ Credentials.
  3. Add your AWS credentials.
  4. Provide:
    • AWS Access Key ID
    • AWS Secret Access Key
  5. Save the credentials using an appropriate Credential ID.

These credentials are securely referenced within the Jenkins Pipeline when interacting with Amazon ECR.


Next Step

With GitHub, Maven, SonarQube, Docker, and Amazon ECR successfully integrated, the next stage is to configure Docker within the Jenkins Pipeline and automate container deployment as part of the complete CI/CD workflow.

Configure Docker in the Jenkins Pipeline

What Is Docker?

Docker is an open-source containerization platform that enables developers to build, package, and deploy applications in lightweight, portable containers.

A Docker container includes everything an application requires to run, including:

  • Application source code
  • Runtime
  • System libraries
  • Dependencies
  • Configuration files

Because containers package all required components together, applications run consistently across development, testing, and production environments.


Configure Docker Credentials in Jenkins

To enable Jenkins to build Docker images and push them to Amazon Elastic Container Registry (Amazon ECR), configure Docker integration within Jenkins.

Install the required Docker plugin and configure the necessary credentials so Jenkins can communicate securely with Docker and Amazon ECR.


Install the AWS CLI

The AWS Command Line Interface (AWS CLI) allows Jenkins to authenticate with AWS services and interact with Amazon ECR.

Install AWS CLI using the following commands:

curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"

unzip awscliv2.zip

sudo ./aws/install

aws --version  

Verify that the AWS CLI is installed successfully before proceeding with the pipeline configuration.


Complete CI/CD Pipeline Configuration

After configuring Jenkins, Maven, SonarQube, Docker, AWS CLI, and Amazon ECR, the CI pipeline is ready.

The following files are used to automate the entire build process.

Jenkinsfile

The Jenkinsfile defines the complete CI workflow, including:

  • Maven build
  • Static code analysis using SonarQube
  • Docker image creation
  • Docker image push to Amazon ECR
pipeline {  
    agent any

    stages {

        stage('Build and Test') {  
            steps {  
                sh 'ls -ltr'

                // Build the project and create a JAR file  
                sh 'cd /var/lib/jenkins/workspace/My_First_Project/Files && mvn clean package'  
            }  
        }

        stage('Static Code Analysis') {

            environment {  
                SONAR_HOST_URL = "http://13.210.222.6:9000"  
            }

            steps {

                withCredentials([  
                    string(  
                        credentialsId: 'sonarqube',  
                        variable: 'SONAR_AUTH_TOKEN'  
                    )  
                ]) {

                    sh '''  
                        cd Files && mvn sonar:sonar \  
                            -Dsonar.login=$SONAR_AUTH_TOKEN \  
                            -Dsonar.host.url=${SONAR_HOST_URL}  
                    '''  
                }  
            }  
        }

        stage('Build and Push Docker Image') {

            environment {  
                DOCKER_IMAGE = 'gettingstarted'  
                AWS_REGION = 'ap-southeast-2'  
                ECR_REGISTRY_URL = '339713116286.dkr.ecr.ap-southeast-2.amazonaws.com/my_first_project'  
            }

            steps {

                script {

                    withCredentials([  
                        [  
                            $class: 'AmazonWebServicesCredentialsBinding',  
                            credentialsId: 'Aws Credentials',  
                            accessKeyVariable: 'AWS_ACCESS_KEY_ID',  
                            secretKeyVariable: 'AWS_SECRET_ACCESS_KEY'  
                        ]  
                    ]) {

                        sh "aws ecr get-login-password --region ${AWS_REGION} | docker login --username AWS --password-stdin ${ECR_REGISTRY_URL}"

                    }

                    sh "docker build -t ${DOCKER_IMAGE}:${BUILD_NUMBER} ./Files"

                    sh "docker tag ${DOCKER_IMAGE}:${BUILD_NUMBER} ${ECR_REGISTRY_URL}:latest"

                    sh "docker push ${ECR_REGISTRY_URL}:latest"

                }  
            }  
        }  
    }

    post {

        failure {  
            echo 'One or more stages failed, but the pipeline will continue...'  
        }

    }  
}  

Maven Project Configuration (pom.xml)

The following pom.xml defines the Spring Boot application, project dependencies, Java version, and Maven plugins required to build the application.

\<?xml version="1.0" encoding="UTF-8"?>

\<project xmlns="http://maven.apache.org/POM/4.0.0"  
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0  
         http://maven.apache.org/xsd/maven-4.0.0.xsd">

    \<modelVersion>4.0.0\</modelVersion>

    \<groupId>com.pavan\</groupId>  
    \<artifactId>spring-boot-demo\</artifactId>  
    \<version>1.0\</version>

    \<name>spring-boot-demo\</name>

    \<parent>  
        \<groupId>org.springframework.boot\</groupId>  
        \<artifactId>spring-boot-starter-parent\</artifactId>  
        \<version>2.2.4.RELEASE\</version>  
    \</parent>

    \<properties>  
        \<project.build.sourceEncoding>UTF-8\</project.build.sourceEncoding>  
        \<maven.compiler.source>17\</maven.compiler.source>  
        \<maven.compiler.target>17\</maven.compiler.target>  
        \<java.version>17\</java.version>  
    \</properties>

    \<dependencies>

        \<dependency>  
            \<groupId>org.springframework.boot\</groupId>  
            \<artifactId>spring-boot-starter-web\</artifactId>  
        \</dependency>

        \<dependency>  
            \<groupId>org.springframework.boot\</groupId>  
            \<artifactId>spring-boot-starter-thymeleaf\</artifactId>  
        \</dependency>

        \<dependency>  
            \<groupId>org.springframework.boot\</groupId>  
            \<artifactId>spring-boot-starter-test\</artifactId>  
            \<scope>test\</scope>  
        \</dependency>

        \<dependency>  
            \<groupId>org.springframework.boot\</groupId>  
            \<artifactId>spring-boot-devtools\</artifactId>  
            \<optional>true\</optional>  
        \</dependency>

    \</dependencies>

    \<build>

        \<finalName>spring-boot-web\</finalName>

        \<plugins>

            \<plugin>  
                \<groupId>org.springframework.boot\</groupId>  
                \<artifactId>spring-boot-maven-plugin\</artifactId>

                \<configuration>  
                    \<mainClass>com.example.demo.DemoApplication\</mainClass>  
                \</configuration>

                \<executions>  
                    \<execution>  
                        \<goals>  
                            \<goal>repackage\</goal>  
                        \</goals>  
                    \</execution>  
                \</executions>

            \</plugin>

            \<plugin>  
                \<groupId>org.apache.maven.plugins\</groupId>  
                \<artifactId>maven-compiler-plugin\</artifactId>  
                \<version>3.8.1\</version>

                \<configuration>  
                    \<source>${java.version}\</source>  
                    \<target>${java.version}\</target>  
                \</configuration>

            \</plugin>

        \</plugins>

    \</build>

\</project>  

Dockerfile

The Dockerfile packages the Spring Boot application into a Docker image.

# Base image  
FROM adoptopenjdk/openjdk11:alpine-jre

# Application artifact  
ARG artifact=target/spring-boot-web.jar

WORKDIR /opt/app

COPY ${artifact} app.jar

ENTRYPOINT ["java","-jar","app.jar"]  

This Dockerfile performs the following tasks:

  • Uses OpenJDK as the base image.
  • Copies the generated Spring Boot JAR file.
  • Creates a working directory.
  • Starts the application automatically when the container launches.

Continuous Integration Completed

At this stage, the Continuous Integration (CI) process is fully configured.

The pipeline automatically performs the following tasks:

  1. Retrieves source code from GitHub.
  2. Builds the application using Maven.
  3. Executes automated tests.
  4. Performs static code analysis using SonarQube.
  5. Builds a Docker image.
  6. Pushes the Docker image to Amazon ECR.

Before moving to deployment, verify that the Jenkins pipeline completes successfully.


Next Phase: Continuous Deployment (CD)

What Is Continuous Deployment?

Continuous Deployment (CD) is the practice of automatically deploying every successfully tested application version to a production or staging environment without manual intervention.

Unlike Continuous Integration, which focuses on building and testing code, Continuous Deployment automates the release process, enabling faster and more reliable software delivery.


Deploy the Docker Image

The next step is to deploy the Docker image stored in Amazon ECR.


Authenticate with Amazon ECR

Log in to your private Amazon ECR repository.

aws ecr get-login-password \  
--region ap-southeast-2 \  
| docker login \  
--username AWS \  
--password-stdin \  
339713116286.dkr.ecr.ap-southeast-2.amazonaws.com/jenkins  


Pull the Docker Image

Download the latest Docker image from the Amazon ECR repository.

docker pull 339713116286.dkr.ecr.ap-southeast-2.amazonaws.com/jenkins  

This ensures the latest application version is available on the deployment server.

Verify the Downloaded Image

List all locally available Docker images.

docker images  

Verify that the downloaded image appears in the list.


Run the Docker Container

Start a container using the downloaded Docker image.

docker run -d -p 8080:8080 \  
339713116286.dkr.ecr.ap-southeast-2.amazonaws.com/jenkins  

This command:

  • Creates a Docker container.
  • Runs it in detached mode.
  • Maps container port 8080 to host port 8080.

Once running, the application becomes accessible through the mapped port.

Verify the Deployment

Open a web browser and access the application using:

http://\<SERVER_PUBLIC_IP>:8080  

If the deployment is successful, the Spring Boot application should be accessible through the exposed port.


Conclusion

In this two-phase CI/CD implementation, you successfully built an automated software delivery pipeline using Jenkins, GitHub, Maven, SonarQube, Docker, Amazon ECR, and the AWS CLI.

Throughout this guide, you learned how to:

  • Configure Jenkins Pipelines.
  • Integrate GitHub as the source code repository.
  • Build Java applications with Maven.
  • Perform automated code quality analysis using SonarQube.
  • Build Docker images.
  • Store container images securely in Amazon Elastic Container Registry (Amazon ECR).
  • Authenticate with AWS using Jenkins credentials.
  • Deploy Docker containers from Amazon ECR.
  • Complete both Continuous Integration (CI) and Continuous Deployment (CD) workflows.

By automating these stages, you can deliver applications more reliably, reduce manual effort, and accelerate software releases using modern DevOps practices.

Hi! I'm ERICA. Ask me anything!