AB
The sixth part of our end-to-end CI/CD series, covering how to implement a pipeline for a full-stack Java application with frontend, backend, and H2 database components.
In this tutorial, we will learn how to set up a CI/CD pipeline for a full-stack Java application that consists of frontend, backend, and an embedded H2 database. This approach demonstrates how to handle more complex applications that involve multiple components working together.
The sample code for this project is available at Secret Santa Generator GitHub Repository.
Before starting this project, make sure you have the following prerequisites:
For this project, we need to use an EC2 instance of type t2.large to ensure sufficient resources for running Jenkins and other services like Docker, SonarQube, and Trivy.
Steps to install Jenkins on an EC2 instance:
sudo apt update -y
sudo apt install openjdk-11-jdk -y
java -version
sudo wget -O /usr/share/keyrings/jenkins-keyring.asc \
https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key
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
sudo apt-get update
sudo apt-get install jenkins
Don’t forget to open port 8080 in security group to access Jenkins.
Open the Jenkins URL in the browser and get the initial admin password by following the instructions in the browser.
Install suggested plugins.
Set up the admin user.
Once Jenkins is set up, you can access the Jenkins dashboard.
You need to install the following plugins:
To install these plugins:
Configure the global tools by going to Manage Jenkins → Global Tool Configuration:
The Secret Santa Generator application consists of three main components:
This type of application requires a comprehensive pipeline that can handle building and testing all components together.
https://github.com/jaiswaladi246/secretsanta-generator.git
pipeline {
agent any
tools{
maven 'maven3'
jdk 'jdk11'
}
environment{
SONARQUBE_HOME = tool name: 'sonarqube'
}
stages {
stage('Checkout Code') {
steps {
git url: 'https://github.com/jaiswaladi246/secretsanta-generator.git', branch: 'master'
}
}
stage('Compile Code') {
steps {
sh 'mvn clean compile'
}
}
stage('Run Tests') {
steps {
sh 'mvn test'
}
}
stage('Sonar Analysis') {
steps {
withSonarQubeEnv('sonarqube') {
sh ''' $SONARQUBE_HOME/bin/sonar-scanner -Dsonar.projectKey=secretsanta-generator -Dsonar.qualitygate.wait=true -Dsonar.qualitygate.timeout=600000 -Dsonar.url=http://172.31.24.10:9000 -Dsonar.login=admin -Dsonar.java.binaries=.
'''
}
}
}
stage('OWASP Dependency Check') {
steps {
dependencyCheck additionalArgs: '-DskipTests --scan ./', odcInstallation: 'DP'
dependencyCheckPublisher pattern: '**/dependency-check-report.xml'
}
}
stage('Maven Install') {
steps {
sh 'mvn clean install -DskipTests=true'
}
}
stage('Build Docker Image') {
steps {
script{
withDockerRegistry([credentialsId: 'docker-hub-credentials', toolName: 'docker']) {
sh 'docker build -t jaiswaladi246/secretsanta-generator:latest .'
sh 'docker push jaiswaladi246/secretsanta-generator:latest'
}
}
}
}
stage('Scan Docker Image with Trivy') {
steps {
sh 'trivy image jaiswaladi246/secretsanta-generator:latest'
}
}
stage('Deploy the image to a container') {
steps {
script{
withDockerRegistry([credentialsId: 'docker-hub-credentials', toolName: 'docker']) {
sh 'docker stop secretsanta-generator || true'
sh 'docker rm secretsanta-generator || true'
sh 'docker run -d --name secretsanta-generator -p 8080:8080 jaiswaladi246/secretsanta-generator:latest'
}
}
}
}
}
post {
success {
echo 'Pipeline completed successfully!'
}
failure {
echo 'Pipeline failed!'
}
}
}
Let’s examine the components of our pipeline:
Checkout Code:
Compile Code:
Run Tests:
SonarQube Analysis:
OWASP Dependency Check:
Maven Install:
Build Docker Image:
Scan Docker Image with Trivy:
Deploy the Docker Container:
When building a CI/CD pipeline for a full-stack application, there are a few special considerations:
The H2 database used in this application is an in-memory database that is embedded within the Spring Boot application. This means:
For applications with persistent databases, you would need to add stages for:
Since the frontend (Thymeleaf templates) is packaged together with the backend in this Spring Boot application, they are built and deployed together. For applications with separate frontend and backend components, you would need:
For a full-stack application, end-to-end testing is crucial. You might want to add:
Before running the pipeline, add Docker Hub credentials to Jenkins:
Install Docker on the Jenkins server/EC2 instance:
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)
sudo usermod -a -G docker jenkins
newgrp docker
sudo systemctl restart jenkins
Set up SonarQube on the Jenkins server using Docker:
docker run -d --name sonarqube -p 9000:9000 sonarqube:lts-community
Access SonarQube by navigating to http://<jenkins-server-ip>:9000
in your browser.
The default username and password are both: admin
To create a token for connecting Jenkins with SonarQube:
Now add this token in Jenkins credentials:
Install Trivy on the Jenkins server for container vulnerability scanning:
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 Secret Santa Generator application, H2 is configured in the application.properties
file:
# H2 Database Configuration
spring.datasource.url=jdbc:h2:mem:santadb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
This configuration:
santadb
/h2-console
The benefit of using H2 for this pipeline is that it requires no external database setup, making the deployment process simpler.
After the pipeline completes successfully:
http://<jenkins-server-ip>:8080
http://<jenkins-server-ip>:8080/h2-console
Once deployed, test the application by:
When working with a full-stack application in CI/CD, you might encounter these common issues:
Memory Issues: Full-stack applications require more memory, especially when running tests. Ensure your Jenkins server has sufficient memory.
Port Conflicts: If you’re deploying multiple applications, you might encounter port conflicts. Use different ports or Docker networking to resolve this.
Frontend Build Failures: JavaScript and CSS builds can be sensitive to Node.js versions and dependencies. Ensure you’re using compatible versions in your build environment.
Database Integration: Even with H2, you might face issues with database schema migrations or test data. Use Spring Boot’s testing features to manage test databases.
In this tutorial, we’ve created a comprehensive CI/CD pipeline for a full-stack Java application with frontend, backend, and H2 database components. This approach:
By following this guide, you now have the knowledge to implement CI/CD for more complex Java applications that include multiple tiers and components.
This concludes our six-part series on building end-to-end CI/CD pipelines for Java applications. Throughout this series, we’ve covered various aspects of CI/CD implementation, from basic pipelines to complex deployments involving multiple components and platforms. We hope this knowledge helps you implement robust CI/CD practices in your own projects.