AB
The first part of our end-to-end CI/CD series, focusing on setting up a robust pipeline for Java applications using Jenkins, Docker, and Tomcat.
In this tutorial, we’ll walk through the process of creating a robust end-to-end Continuous Integration and Continuous Deployment (CI/CD) pipeline for a Java application. This is the first part of our E2E CI/CD series, focusing on Jenkins integration with Docker and Tomcat. We’ll leverage industry-standard tools to automate building, testing, and deploying applications efficiently.
The sample code for this project is available at Spring PetClinic GitHub Repository.
Before starting this project, make sure you have the following prerequisites:
In this guide, we’ll set up Jenkins on an AWS EC2 instance and configure it to build and deploy a Java application using Docker and Tomcat. Follow these steps for a seamless setup.
Note: To restart jenkins service use: sudo systemctl restart jenkins
Before proceeding, ensure you have the following:
8080
for Jenkins and 22
for SSH.Select an Instance Type:
Ubuntu 22.04
AMI.t2.large
instance for optimal performance.Configure Security Group:
8080
for Jenkins.22
for SSH.Access the Instance:
ssh -i "<your-key-pair>.pem" ubuntu@<public-ip-address>
Jenkins requires Java to run. Install OpenJDK using the following commands:
sudo apt update -y
sudo apt install openjdk-17-jdk -y
java -version
Verify that the installation is successful by checking the Java version.
Add Jenkins Repository:
curl -fsSL https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key | sudo tee "/usr/share/keyrings/jenkins-keyring.asc"
echo deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc] https://pkg.jenkins.io/debian-stable binary/ | sudo tee /etc/apt/sources.list.d/jenkins.list > /dev/null
Install Jenkins:
sudo apt update -y
sudo apt install jenkins -y
Start Jenkins Service:
sudo systemctl start jenkins
sudo systemctl status jenkins
Access Jenkins:
http://<public-ip>:8080
.Unlock Jenkins:
sudo cat /var/lib/jenkins/secrets/initialAdminPassword
Once Jenkins is running, install the following plugins:
You can add plugins from Manage Jenkins > Manage Plugins
.
Next we need to configure it so go to Global Tool Configuration:
sudo apt install docker.io -y
sudo systemctl start docker
sudo usermod -aG docker jenkins
pipeline {
agent any
tools {
maven 'maven3'
jdk 'jdk11'
}
}
stages {
stage('git checkout') {
steps {
git url: 'https://github.com/your-repository/your-project.git'
}
}
}
For any help we can use pipeline syntax to get the help.
stages {
stage('compile') {
steps {
sh 'mvn compile'
}
}
}
stages {
stage('unit testing') {
steps {
sh 'mvn test'
}
}
}
For this first we need to configure the sonarqube in jenkins:
Also go to Manage Jenkins –> Configure System –> SonarQube servers. Here we need to add the sonarqube server url and the credentials which we have created in the previous step.
What is the difference between configure system and global tool configuration?
Configure system is for system level configuration like SonarQube servers, Docker, Kuberenetes etc. Global tool configuration is for tool/plugin specific configuration like JDK, Maven, Git etc.
Next we need to go to global tool configuration and add the sonarqube scanner:
Now go to the pipeline section and add the sonarqube scanner. First we need to create an environment variable for the sonarqube server url:
pipeline {
environment {
SONARQUBE_URL = tool name: 'sonar-scanner' // It should be same as the name of the sonarqube scanner in the global tool configuration.
}
}
For below take help of pipeline syntax:
stages {
stage('sonarqube analysis') {
steps {
withSonarQubeEnv('sonarqube') { // It should be same as the name of the sonarqube server in the global tool configuration.
sh '''
$SONARQUBE_URL/bin/sonar-scanner -Dsonar.projectKey=myproject -Dsonar.\ qualitygate.wait=true -Dsonar.projectName=myproject -Dsonar.\ projectVersion=1.0 -Dsonar.sources=src -Dsonar.java.binaries=.
'''
}
}
}
}
Understand the report in the sonarqube server, understand the issues, and learn how to assign the issues to the developers.
For this first we need to install the OWASP dependency check plugin in jenkins:
Next we need to configure the OWASP dependency check in jenkins:
Next we need to configure the OWASP dependency check in the pipeline:
pipeline {
stages {
stage('OWASP dependency check') {
steps {
dependencyCheck additionalArguments: '--scan ./', odcInstallation: 'owasp-dependency-check'
dependencyCheckPublisher pattern: '**/dependency-check-report.xml', healthy: '0', unhealthy: '1'
}
}
}
}
You can check the report in the jenkins server. Under Build History –> Last build –> Under Workspace –> target –> site –> dependency-check-report.html
pipeline {
stages {
stage('build') {
steps {
sh 'mvn clean install'
}
}
}
}
Where can we get the jar file? We get the jar file in the target folder. So the path is ./target/my-app-1.0-SNAPSHOT.jar. Go to the ec2 instance and check the target folder.
For this we need to install docker plugin in the jenkins server:
Next we need to configure the docker in global tool configuration:
Next we need to configure the docker in the pipeline. Before that we need to give the credentials for docker hub:
pipeline {
agent any
tools {
docker 'docker'
}
}
Next we need to do the docker build:
stages {
stage('docker build') {
steps {
script {
withDockerRegistry([credentialsId: 'dockerhub', toolName: 'docker']) {
sh 'docker build -t mydockerhubusername/mydockerimage:mytag .'
}
}
}
}
}
Once build is done you can see the image in ec2.
Before pushing the image we can use trivy to scan the image for vulnerabilities:
Next we need to configure the trivy in the pipeline:
pipeline {
tools {
trivy 'trivy'
}
}
stages {
stage('trivy scan') {
steps {
sh 'trivy image mydockerhubusername/mydockerimage:mytag'
sh 'trivy image mydockerhubusername/mydockerimage:mytag > trivy-report.txt'
}
}
}
Next is push the image to docker hub:
stages {
stage('docker push') {
steps {
script {
withDockerRegistry([credentialsId: 'dockerhub', toolName: 'docker']) {
sh 'docker push mydockerhubusername/mydockerimage:mytag'
}
}
}
}
}
stages {
stage('deploy using docker') {
steps {
sh 'docker run -p 8082:8082 mydockerhubusername/mydockerimage:mytag'
}
}
}
Here is the complete pipeline:
pipeline {
agent any
tools {
maven "maven3"
jdk "jdk17"
dockerTool "docker" // Keep the correct tool declaration for Docker
}
environment {
SONARQUBE_URL = tool name: 'sonarqube-scanner' // This line should match the name set in Jenkins
}
stages {
stage('Git Code Checkout') {
steps {
git branch: 'main', changelog: false, poll: false, url: 'https://github.com/spring-projects/spring-petclinic'
}
}
stage('Code Compile') {
steps {
sh "mvn compile"
}
}
stage('Code Unit Testing') {
steps {
sh "mvn test"
}
}
stage('SonarQube Analysis') {
steps {
// Use 'withSonarQubeEnv' directly for the analysis
withSonarQubeEnv(credentialsId: 'sonar-cred', installationName: 'sonarqube-scanner') {
sh '''
$SONARQUBE_URL/bin/sonar-scanner \
-Dsonar.projectKey=myproject \
-Dsonar.projectName=myproject \
-Dsonar.projectVersion=1.0 \
-Dsonar.sources=src \
-Dsonar.java.binaries=.
'''
}
}
}
stage('OWASP Dependency Check') {
steps {
dependencyCheck additionalArguments: '--scan ./', odcInstallation: 'owasp-dependency-check'
dependencyCheckPublisher pattern: '**/dependency-check-report.xml', healthy: '0', unhealthy: '1'
}
}
stage('Build') {
steps {
sh 'mvn clean install'
}
}
stage("Docker Build") {
steps {
script {
withDockerRegistry(credentialsId: 'docker', toolName: 'docker') {
sh 'docker build -t howaboutpullsomeimages/mydockerimage:latest .'
}
}
}
}
stage("Docker Push") {
steps {
script {
withDockerRegistry(credentialsId: 'docker', toolName: 'docker') {
sh 'docker push howaboutpullsomeimages/mydockerimage:latest'
}
}
}
}
stage("Deploy Using Docker") {
steps {
sh 'docker run -p 8082:8082 howaboutpullsomeimages/mydockerimage:latest'
}
}
}
}
In the jenkins server/ec2 instance, install Docker with these commands:
sudo apt-get update
sudo apt-get install docker.io -y
sudo systemctl start docker
sudo docker run hello-world
sudo systemctl enable docker
docker --version
sudo usermod -a -G docker $(whoami)
newgrp docker
To set up SonarQube on the Jenkins server where Docker is installed, run this Docker command:
docker run -d --name sonarqube -p 9000:9000 sonarqube:lts-community
Now go to the Jenkins server browser and navigate to the IP address of the Jenkins server followed by :9000.
The default username and password are both: admin
To create a token to connect Jenkins with SonarQube:
Now add this token in Jenkins credentials:
Install Trivy in the same EC2 instance where Jenkins is installed:
sudo apt-get install wget apt-transport-https gnupg lsb-release
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo apt-key add -
echo deb https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main | sudo tee -a /etc/apt/sources.list.d/trivy.list
sudo apt-get update
sudo apt-get install trivy
In the next part of this series, we’ll expand our CI/CD pipeline to include Kubernetes for container orchestration, providing a more scalable and robust deployment strategy.