Phase 2: Hands-On – Building a Jenkins CI/CD Pipeline
With the infrastructure in place, the next step is to create a Jenkins Pipeline that automates the software delivery process. Before building the pipeline, it's important to understand the core concepts that make Continuous Integration and Continuous Deployment (CI/CD) possible.
Understanding the Core Concepts
What Is Jenkins?
Jenkins is an open-source automation server written in Java that helps automate the process of building, testing, and deploying software applications.
It integrates with a wide range of development tools and enables teams to implement Continuous Integration (CI) and Continuous Deployment (CD) practices efficiently.
What Is a CI/CD Pipeline?
A Continuous Integration and Continuous Deployment (CI/CD) pipeline is an automated workflow that moves application code from development to production.
A typical CI/CD pipeline performs tasks such as:
- Retrieving source code from a version control system
- Building the application
- Running automated tests
- Performing code quality analysis
- Packaging the application
- Building container images
- Deploying the application
Automating these stages improves software quality, reduces manual effort, and accelerates release cycles.
What Is a Jenkins Pipeline?
A Jenkins Pipeline is a collection of plugins that enables you to define and automate the stages of a CI/CD workflow.
Pipeline definitions are stored as code, making them version-controlled, repeatable, and easier to maintain.
Jenkins supports two pipeline syntaxes:
Declarative Pipeline
Declarative Pipeline is the recommended and modern approach for creating Jenkins pipelines.
Characteristics:
- Simple and structured syntax
- Easier to read and maintain
- Uses a Groovy-based Domain Specific Language (DSL)
- Ideal for most CI/CD workflows
Scripted Pipeline
Scripted Pipeline is the original Jenkins pipeline syntax and provides greater flexibility for advanced use cases.
Characteristics:
- Written entirely in Groovy
- Offers complete programmatic control
- Supports complex logic and custom workflows
- More verbose than Declarative Pipeline
What Is a Jenkinsfile?
A Jenkinsfile is a text file that defines the entire CI/CD pipeline as code.
It is stored alongside the application's source code in the Git repository and contains all pipeline stages, including:
- Source code checkout
- Build
- Testing
- Static code analysis
- Docker image creation
- Deployment
Using a Jenkinsfile ensures that pipeline definitions are version-controlled and reproducible.
What Is Continuous Integration (CI)?
Continuous Integration (CI) is the practice of automatically building and testing an application whenever code changes are committed to the repository.
Each code commit triggers an automated process that:
- Retrieves the latest source code
- Compiles the application
- Executes automated tests
- Detects integration issues early
CI helps development teams identify defects quickly and maintain a stable codebase.
Create a Jenkins Pipeline Project
After understanding the core concepts, the next step is to create a Jenkins Pipeline project.
- Open the Jenkins Dashboard.
- Click New Item.
- Enter a project name.
- Select Pipeline as the project type.
- Click OK.
Important: Include a single screenshot of the "New Item" page showing the selection of the Pipeline project type. This is the only screenshot required in this section because it introduces the pipeline creation process.
Configure the Pipeline Project
After creating the project:
- Open the project configuration page.
- Scroll to the Pipeline section.
- Configure the pipeline according to your preferred method.
In the next section, you'll connect Jenkins to a GitHub repository and configure the pipeline to retrieve the application's source code automatically.
Configure GitHub Integration
The next step is to connect Jenkins to a GitHub repository so that it can automatically retrieve the application's source code during pipeline execution.
What Is GitHub?
GitHub is a cloud-based Git repository hosting platform used to manage source code, collaborate with teams, and maintain version-controlled software projects.
Jenkins integrates with GitHub to automatically clone repositories, trigger builds, and execute CI/CD pipelines whenever code changes are pushed.
Configure the Git Repository
Open the Pipeline project's configuration page.
Under the Pipeline or Source Code Management section:
- Enter the GitHub repository URL.
- Verify that the repository is accessible from the Jenkins server.
Important: Include a single screenshot showing the GitHub Repository URL configuration.
Configure GitHub Credentials
To securely access a private GitHub repository:
- Click Add Credentials.
- Select Username with Password (or Personal Access Token).
- Enter your GitHub credentials.
- Save the credentials.
- Select the newly created credentials from the Jenkins project.
This allows Jenkins to authenticate with GitHub without exposing sensitive information inside the pipeline.
Configure the Jenkinsfile Path
Specify the location of the Jenkinsfile inside your repository.
Example:
Files/Jenkinsfile
If the Jenkinsfile is stored in the repository root, simply specify:
Jenkinsfile
Save the project configuration after completing the GitHub setup.
Build the Application with Maven
What Is Apache Maven?
Apache Maven is a build automation and dependency management tool primarily used for Java applications.
Within a Jenkins pipeline, Maven is responsible for:
- Downloading project dependencies
- Compiling source code
- Running unit tests
- Packaging the application
Configure the Build Stage
Add a Build and Test stage to the Jenkins Pipeline.
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'
}
}
Build Stage Overview
This stage performs the following tasks:
- Lists the project files using
ls -ltr. - Navigates to the project directory.
- Executes:
mvn clean package
This command:
- Cleans previous build artifacts.
- Downloads project dependencies.
- Compiles the application.
- Executes unit tests.
- Packages the application into a JAR file.
Integrate SonarQube
Static code analysis helps identify bugs, vulnerabilities, duplicated code, and maintainability issues before deployment.
What Is SonarQube?
SonarQube is an open-source code quality platform that continuously analyzes source code and reports:
- Bugs
- Security vulnerabilities
- Code smells
- Duplicated code
- Maintainability metrics
- Technical debt
Integrating SonarQube into the Jenkins pipeline enables automated quality checks for every build.
Install the SonarQube Scanner Plugin
From the Jenkins Dashboard:
- Navigate to Manage Jenkins.
- Open Plugins (or Manage Plugins, depending on the Jenkins version).
- Install the SonarQube Scanner plugin.
- Restart Jenkins if prompted.
After installation, a new SonarQube Servers section becomes available under Manage Jenkins → System Configuration.
Important: Include one screenshot showing the SonarQube Scanner Plugin installation or the SonarQube Server Configuration page.
Configure the SonarQube Server
- Navigate to Manage Jenkins → System.
- Locate the SonarQube Servers section.
- Click Add SonarQube.
Generate a SonarQube Access Token
Open the SonarQube web interface.
Navigate to:
My Account → Security → Generate Tokens
Create a new token by:
- Providing a token name.
- Selecting the token type.
- Choosing an expiration period (optional).
- Clicking Generate.
Copy the generated token.
Add the Token to Jenkins Credentials
Store the generated SonarQube token securely in Jenkins.
Navigate to:
Manage Jenkins → Credentials
Create a new Secret Text credential.
Example:
| Field | Value |
|---|---|
| ID | sonarqube |
| Secret | Generated SonarQube Token |
The pipeline will reference this credential during static code analysis.
Configure the Static Code Analysis Stage
Add the following stage to the Jenkins Pipeline.
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}
'''
}
}
}
Pipeline Overview
This stage performs the following operations:
- Defines the SonarQube server URL.
- Retrieves the authentication token securely from Jenkins Credentials.
- Executes the Maven Sonar plugin.
- Uploads the analysis results to the SonarQube server.
- Generates a complete code quality report.
The report provides insights into:
- Bugs
- Vulnerabilities
- Code smells
- Maintainability
- Coverage metrics
- Technical debt
With GitHub integration, Maven builds, and SonarQube analysis configured, the Jenkins pipeline is now capable of retrieving source code, compiling the application, and performing automated static code quality analysis. The next step is to integrate AWS services and continue the deployment pipeline.
Configure Amazon Elastic Container Registry (Amazon ECR)
The next step is to configure Amazon Elastic Container Registry (Amazon ECR), where Docker images will be securely stored before deployment.
What Is Amazon ECR?
Amazon Elastic Container Registry (Amazon ECR) is a fully managed container image registry service provided by AWS.
It enables developers to securely:
- Store Docker images
- Push and pull container images
- Manage image versions
- Integrate seamlessly with AWS services such as ECS, EKS, Lambda, and Jenkins
Using Amazon ECR eliminates the need to manage your own private Docker registry while providing a scalable and highly available solution.
Create an Amazon ECR Repository
Before pushing Docker images, create a private repository.
- Sign in to the AWS Management Console.
- Search for Amazon ECR.
- Open the Elastic Container Registry service.
- Select Private Registry.
- Click Create Repository.
- Enter a repository name.
- Click Create Repository.
Important: Include a single screenshot showing the completed Amazon ECR repository. This is the only image required in this section.
Configure the Jenkins Pipeline for Amazon ECR
After creating the repository, AWS provides Docker push commands.
These commands can be integrated directly into the Jenkins Pipeline.
Add the following stage.
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 {
// Authenticate with Amazon ECR
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}"
}
// Build Docker image
sh "docker build -t ${DOCKER_IMAGE}:${BUILD_NUMBER} ./Files"
// Tag Docker image
sh "docker tag ${DOCKER_IMAGE}:${BUILD_NUMBER} ${ECR_REGISTRY_URL}:latest"
// Push Docker image
sh "docker push ${ECR_REGISTRY_URL}:latest"
}
}
}
Build and Push Workflow
This pipeline stage performs the following tasks:
- Defines the required environment variables.
- Retrieves AWS credentials securely from Jenkins Credentials.
- Authenticates Jenkins with Amazon ECR.
- Builds the Docker image.
- Tags the image with the repository name.
- Pushes the image to Amazon ECR.
This automates the entire image publishing process without requiring manual Docker commands.
Configure AWS Credentials in Jenkins
Jenkins requires permission to communicate with AWS services.
Install the AWS Credentials Plugin and configure AWS IAM credentials.
Create a new AWS credential containing:
| Field | Description |
|---|---|
| Access Key ID | AWS IAM Access Key |
| Secret Access Key | AWS IAM Secret Key |
Store these credentials securely in Jenkins and reference them within the pipeline using the configured credentialsId.
Configure Docker Integration
What Is Docker?
Docker is an open-source containerization platform that packages applications and their dependencies into lightweight, portable containers.
Docker enables applications to run consistently across different environments, making deployments faster and more reliable.
Within this pipeline, Docker is responsible for:
- Building the application image
- Tagging the image
- Publishing the image to Amazon ECR
- Running the application as a container
To enable Docker support within Jenkins, install the required Docker plugins and ensure the Jenkins user has permission to execute Docker commands.
Install the AWS CLI
The AWS Command Line Interface (AWS CLI) is required so Jenkins can authenticate with Amazon ECR and execute AWS commands during the pipeline.
Install AWS CLI on the Jenkins server.
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
aws --version
Verify the installation.
The version command should display the installed AWS CLI version, confirming that Jenkins can now interact with AWS services.
Summary
At this stage, the Jenkins pipeline is capable of:
- Authenticating with AWS
- Building Docker images
- Connecting securely to Amazon ECR
- Tagging container images
- Publishing images to a private Amazon ECR repository
With Amazon ECR integration complete, the CI/CD pipeline is ready for the final deployment stage, where Docker containers will be pulled from Amazon ECR and deployed to the target environment.
Complete Jenkins Pipeline Configuration
At this stage, all required services have been configured. The final step for Continuous Integration (CI) is to create a Jenkinsfile, which defines the complete build pipeline as code.
The pipeline performs the following operations:
- Build the application using Maven
- Perform static code analysis using SonarQube
- Build the Docker image
- Authenticate with Amazon ECR
- Push the Docker image to Amazon ECR
Jenkinsfile
Create a file named Jenkinsfile in the root directory of your GitHub repository.
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
The project uses Apache Maven to compile, package, and manage project dependencies.
The following pom.xml file configures a Spring Boot application built with Java 17.
pom.xml
\<?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>
\<parent>
\<groupId>org.springframework.boot\</groupId>
\<artifactId>spring-boot-starter-parent\</artifactId>
\<version>2.2.4.RELEASE\</version>
\</parent>
\<properties>
\<java.version>17\</java.version>
\<maven.compiler.source>17\</maven.compiler.source>
\<maven.compiler.target>17\</maven.compiler.target>
\</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>
\</dependencies>
\<build>
\<finalName>spring-boot-web\</finalName>
\<plugins>
\<plugin>
\<groupId>org.springframework.boot\</groupId>
\<artifactId>spring-boot-maven-plugin\</artifactId>
\</plugin>
\</plugins>
\</build>
\</project>
Docker Configuration
Docker packages the Spring Boot application into a lightweight container image that can be deployed consistently across environments.
Dockerfile
Create a file named Dockerfile.
FROM adoptopenjdk/openjdk11:alpine-jre
ARG artifact=target/spring-boot-web.jar
WORKDIR /opt/app
COPY ${artifact} app.jar
ENTRYPOINT ["java","-jar","app.jar"]
Continuous Integration Complete
At this stage, the Jenkins pipeline is fully configured.
The pipeline now performs the following automated workflow:
- Clone the source code from GitHub.
- Build the project using Maven.
- Execute static code analysis using SonarQube.
- Build the Docker image.
- Push the Docker image to Amazon Elastic Container Registry (Amazon ECR).
Before moving to deployment:
- Verify the Jenkins pipeline configuration.
- Trigger a pipeline build.
- Confirm that all stages complete successfully.
Important: Include a single screenshot showing the Jenkins pipeline successfully completing all stages. This serves as the primary verification image for the Continuous Integration phase.
Continuous Deployment (CD)
What Is Continuous Deployment?
Continuous Deployment (CD) is the practice of automatically deploying successfully validated application changes to the target environment after the Continuous Integration process completes.
Unlike Continuous Integration, which focuses on building and testing code, Continuous Deployment automates the release process, ensuring that production environments always receive the latest validated application version.
Deploy the Docker Image
After the Docker image has been published to Amazon ECR, deploy it to a Docker host.
Authenticate with Amazon ECR
Authenticate Docker with Amazon ECR.
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 application image from Amazon ECR.
docker pull \
339713116286.dkr.ecr.ap-southeast-2.amazonaws.com/jenkins
Verify the Image
List the available Docker images.
docker images
Run the Container
Create a container from the downloaded image.
docker run -d -p 8080:8080 \
339713116286.dkr.ecr.ap-southeast-2.amazonaws.com/jenkins
This command:
- Creates a Docker container.
- Maps container port 8080 to the host.
- Starts the application in detached mode.
Verify the Deployment
Open the application in a web browser.
http://\<EC2-Public-IP>:8080
If everything has been configured correctly, the Spring Boot application should now be accessible.
Important: Include a single screenshot showing the deployed application running successfully. This provides readers with confirmation that the Continuous Deployment process completed successfully.
Conclusion
In this guide, you successfully built a complete CI/CD pipeline using Jenkins, Maven, Docker, SonarQube, Amazon ECR, and AWS.
The pipeline automates the entire software delivery lifecycle by:
- Retrieving source code from GitHub
- Building the application with Maven
- Performing static code analysis using SonarQube
- Creating Docker images
- Publishing images to Amazon ECR
- Deploying containers using Docker
By integrating these tools, you can build reliable, repeatable, and production-ready deployment pipelines that accelerate software delivery while maintaining code quality and deployment consistency.