Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
target/
.git/
.idea/
*.iml
*.log
.env
.DS_Store
17 changes: 6 additions & 11 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,14 @@
#----------------------------------

# Import docker image with maven installed
FROM maven:3.8.3-openjdk-17 as builder
FROM maven:3.8.3-openjdk-17 AS builder

# Add maintainer, so that new user will understand who had written this Dockerfile
MAINTAINER Madhup Pandey<madhuppandey2908@gmail.com>

# Add labels to the image to filter out if we have multiple application running
LABEL app=bankapp

# Set working directory
WORKDIR /src
WORKDIR /app

# Copy source code from local to container
COPY . /src
COPY . /app
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Optimize Docker build performance with selective copying

The current COPY . /app copies all files, which can be inefficient. Consider copying only the necessary files first to leverage Docker's layer caching for dependencies.

-COPY . /app
+COPY pom.xml /app/
+RUN mvn dependency:go-offline
+
+COPY src /app/src/

This change will:

  1. Cache Maven dependencies in a separate layer
  2. Only rebuild when source code changes
  3. Improve build times significantly
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
COPY . /app
COPY pom.xml /app/
RUN mvn dependency:go-offline
COPY src /app/src/


# Build application and skip test cases
RUN mvn clean install -DskipTests=true
Expand All @@ -25,13 +20,13 @@ RUN mvn clean install -DskipTests=true
#--------------------------------------

# Import small size java image
FROM openjdk:17-alpine as deployer
FROM openjdk:17-alpine AS deployer

# Copy build from stage 1 (builder)
COPY --from=builder /src/target/*.jar /src/target/bankapp.jar
COPY --from=builder /app/target/*.jar /app/target/bankapp.jar
Comment on lines +23 to +26
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance security and operational aspects

Consider the following security and operational improvements:

  1. Run the application as a non-root user
  2. Add a HEALTHCHECK instruction
  3. Configure JVM parameters appropriately
FROM openjdk:17-alpine AS deployer

+RUN addgroup -S spring && adduser -S spring -G spring
+USER spring:spring

COPY --from=builder /app/target/*.jar /app/target/bankapp.jar

+HEALTHCHECK --interval=30s --timeout=3s \
+  CMD wget -q --spider http://localhost:8080/actuator/health || exit 1

Committable suggestion skipped: line range outside the PR's diff.


# Expose application port
EXPOSE 8080

# Start the application
ENTRYPOINT ["java", "-jar", "/src/target/bankapp.jar"]
ENTRYPOINT ["java", "-jar", "/app/target/bankapp.jar"]
77 changes: 63 additions & 14 deletions Jenkinsfile
Original file line number Diff line number Diff line change
@@ -1,30 +1,79 @@
@Library("shared-library@DevOps") _

/* groovylint-disable-next-line CompileStatic */
pipeline {
agent {label 'runner_1'}

agent { label 'agent-slave' }
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Update agent label to avoid deprecated terminology

The label 'agent-slave' includes the term "slave", which is deprecated in Jenkins due to its negative connotations. It's recommended to use alternative labels like 'agent' or a more descriptive name that aligns with current best practices.

environment {
VERSION = "${BUILD_NUMBER}"
}
stages {
stage('Checkout code') {
stage('Code Clone') {
steps {
codeCheckout('DevOps', 'https://github.com/joakim077/Springboot-BankApp.git')
echo 'Code Clone Stage'
git url: 'https://github.com/nkantamani2023/Springboot-BankApp.git', branch: 'DevOps'
timeout(time: 5, unit: 'MINUTES')
{
/* groovylint-disable-next-line DuplicateStringLiteral */
git url: params.REPO_URL ?: 'https://github.com/nkantamani2023/Springboot-BankApp.git',
/* groovylint-disable-next-line DuplicateStringLiteral */
branch: params.BRANCH ?: 'DevOps',
shallow: true
}
Comment on lines +11 to +19
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix syntax errors and duplicate git commands in 'Code Clone' stage

There are syntax issues and duplicate git checkout steps in the 'Code Clone' stage:

  • The git step is called twice, which is unnecessary.
  • The timeout block is not correctly formatted with its curly braces.
  • The placement of braces {} is causing improper nesting, which may lead to pipeline execution errors.

Apply this diff to correct the syntax and remove duplication:

 stage('Code Clone') {
     steps {
         echo 'Code Clone Stage'
-        git url: 'https://github.com/nkantamani2023/Springboot-BankApp.git', branch: 'DevOps'
-        timeout(time: 5, unit: 'MINUTES')
-        {
-            /* groovylint-disable-next-line DuplicateStringLiteral */
-            git url: params.REPO_URL ?: 'https://github.com/nkantamani2023/Springboot-BankApp.git',
-            /* groovylint-disable-next-line DuplicateStringLiteral */
-            branch: params.BRANCH ?: 'DevOps',
-            shallow: true
-        }
+        timeout(time: 5, unit: 'MINUTES') {
+            /* groovylint-disable-next-line DuplicateStringLiteral */
+            git url: params.REPO_URL ?: 'https://github.com/nkantamani2023/Springboot-BankApp.git',
+            /* groovylint-disable-next-line DuplicateStringLiteral */
+            branch: params.BRANCH ?: 'DevOps',
+            shallow: true
+        }
     }
 }

This correction:

  • Removes the initial redundant git command.
  • Properly nests the git step within the timeout block.
  • Ensures that parameters are correctly utilized.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
git url: 'https://github.com/nkantamani2023/Springboot-BankApp.git', branch: 'DevOps'
timeout(time: 5, unit: 'MINUTES')
{
/* groovylint-disable-next-line DuplicateStringLiteral */
git url: params.REPO_URL ?: 'https://github.com/nkantamani2023/Springboot-BankApp.git',
/* groovylint-disable-next-line DuplicateStringLiteral */
branch: params.BRANCH ?: 'DevOps',
shallow: true
}
timeout(time: 5, unit: 'MINUTES') {
/* groovylint-disable-next-line DuplicateStringLiteral */
git url: params.REPO_URL ?: 'https://github.com/nkantamani2023/Springboot-BankApp.git',
/* groovylint-disable-next-line DuplicateStringLiteral */
branch: params.BRANCH ?: 'DevOps',
shallow: true
}

}
}
stage('build') {
stage('Code Build & Test') {
steps {
buildImage("springboot-application")
echo 'Code Build Stage'
sh './mvnw clean test'
sh 'docker build -t bankapp .'
}
}
stage('Push Image') {
stage('Push To DockerHub') {
steps {
pushImage("springboot-application")
withCredentials([usernamePassword(
credentialsId:'dockerhub-creds',
usernameVariable:'dockerHubUser',
passwordVariable:'dockerHubPass')]) {
sh 'echo $dockerHubPass | docker login -u $dockerHubUser --password-stdin'
sh "docker image tag bankapp:latest ${env.dockerHubUser}/bankapp:${VERSION}"
sh "docker push ${env.dockerHubUser}/bankapp:${VERSION}"
}
}
}
stage('Deploy'){
steps{
deploy()
stage('Deploy') {
environment {
COMPOSE_PROJECT_NAME = "${JOB_NAME}-${VERSION}"
MAX_RETRIES = 30
RETRY_INTERVAL = 10
}
steps {
script {
// Graceful deployment with zero downtime
sh """
docker compose -p ${COMPOSE_PROJECT_NAME} up -d --build
# Wait for service to be ready
for i in \$(seq 1 \$MAX_RETRIES); do
if curl -sf http://localhost:8080/actuator/health | grep -q '"status":"UP"'; then
echo "Service is healthy"
docker compose -p ${JOB_NAME}-previous down || true
exit 0
fi
echo "Attempt \$i: Service not ready yet..."
sleep \$RETRY_INTERVAL
done
echo "Service failed to become healthy - rolling back"
docker compose -p ${COMPOSE_PROJECT_NAME} down
# Redeploy the previous version
docker compose -p ${JOB_NAME}-previous up -d
exit 1
Comment on lines +43 to +66
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Ensure consistent usage of environment variables for deployment and rollback

There are inconsistencies in how environment variables are used during deployment and rollback:

  • COMPOSE_PROJECT_NAME is defined as ${JOB_NAME}-${VERSION}, but during rollback, the script references ${JOB_NAME}-previous.
  • This mismatch may lead to the wrong Docker Compose projects being brought up or down, potentially causing deployment issues.

Consider updating the script to maintain consistency:

  1. Define a PREVIOUS_COMPOSE_PROJECT_NAME for clarity.

  2. Update the deployment and rollback commands to use the correct project names.

Apply this diff to align the variable usage:

 environment {
     COMPOSE_PROJECT_NAME = "${JOB_NAME}-${VERSION}"
+    PREVIOUS_COMPOSE_PROJECT_NAME = "${JOB_NAME}-previous"
     MAX_RETRIES = 30 
     RETRY_INTERVAL = 10
 }
 
 steps {
     script {
         // Graceful deployment with zero downtime
         sh """
     docker compose -p ${COMPOSE_PROJECT_NAME} up -d --build
     # Wait for service to be ready
     for i in \$(seq 1 \$MAX_RETRIES); do
         if curl -sf http://localhost:8080/actuator/health | grep -q '"status":"UP"'; then
             echo "Service is healthy"
-            docker compose -p ${JOB_NAME}-previous down || true
+            docker compose -p ${PREVIOUS_COMPOSE_PROJECT_NAME} down || true
             exit 0
         fi
         echo "Attempt \$i: Service not ready yet..."
         sleep \$RETRY_INTERVAL
     done
     echo "Service failed to become healthy - rolling back"
     docker compose -p ${COMPOSE_PROJECT_NAME} down
     # Redeploy the previous version
-    docker compose -p ${JOB_NAME}-previous up -d
+    docker compose -p ${PREVIOUS_COMPOSE_PROJECT_NAME} up -d
     exit 1
     """
     }
 }

This ensures that the correct Docker Compose projects are managed during deployment and rollback.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
COMPOSE_PROJECT_NAME = "${JOB_NAME}-${VERSION}"
MAX_RETRIES = 30
RETRY_INTERVAL = 10
}
steps {
script {
// Graceful deployment with zero downtime
sh """
docker compose -p ${COMPOSE_PROJECT_NAME} up -d --build
# Wait for service to be ready
for i in \$(seq 1 \$MAX_RETRIES); do
if curl -sf http://localhost:8080/actuator/health | grep -q '"status":"UP"'; then
echo "Service is healthy"
docker compose -p ${JOB_NAME}-previous down || true
exit 0
fi
echo "Attempt \$i: Service not ready yet..."
sleep \$RETRY_INTERVAL
done
echo "Service failed to become healthy - rolling back"
docker compose -p ${COMPOSE_PROJECT_NAME} down
# Redeploy the previous version
docker compose -p ${JOB_NAME}-previous up -d
exit 1
COMPOSE_PROJECT_NAME = "${JOB_NAME}-${VERSION}"
PREVIOUS_COMPOSE_PROJECT_NAME = "${JOB_NAME}-previous"
MAX_RETRIES = 30
RETRY_INTERVAL = 10
}
steps {
script {
// Graceful deployment with zero downtime
sh """
docker compose -p ${COMPOSE_PROJECT_NAME} up -d --build
# Wait for service to be ready
for i in \$(seq 1 \$MAX_RETRIES); do
if curl -sf http://localhost:8080/actuator/health | grep -q '"status":"UP"'; then
echo "Service is healthy"
docker compose -p ${PREVIOUS_COMPOSE_PROJECT_NAME} down || true
exit 0
fi
echo "Attempt \$i: Service not ready yet..."
sleep \$RETRY_INTERVAL
done
echo "Service failed to become healthy - rolling back"
docker compose -p ${COMPOSE_PROJECT_NAME} down
# Redeploy the previous version
docker compose -p ${PREVIOUS_COMPOSE_PROJECT_NAME} up -d
exit 1

"""
}
}
}
}
post {
failure {
echo 'Deployment failed - initiating rollback'
sh "docker compose -p ${JOB_NAME}-previous up -d"
}

}
}

237 changes: 149 additions & 88 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,96 +1,157 @@
## End-to-End Bank Application Deployment using DevSecOps on AWS EKS
- This is a multi-tier bank an application written in Java (Springboot).

![Login diagram](images/login.png)
![Transactions diagram](images/transactions.png)

### PRE-REQUISITES FOR THIS PROJECT:
- AWS Account
- AWS Ubuntu EC2 instance (t2.medium)
- Install Docker
- Install docker compose
#
### DEPLOYMENT:
| Deployments | Paths |
| -------- | ------- |
| Deployment using Docker and Networking | <a href="#Docker">Click me </a> |
| Deployment using Docker Compose | <a href="#dockercompose">Click me </a> |
| Deployment using Jenkins on EKS | <a href="#">Click me </a> |
| Deployment using Argocd on EKS| <a href="#">Click me </a> |

#
### STEPS TO IMPLEMENT THE PROJECT
- **<p id="Docker">Deployment using Docker</p>**
- Clone the repository
```bash
git clone -b DevOps https://github.com/DevMadhup/Springboot-BankApp.git
```
#
- Install docker, docker compose and provide neccessary permission
```bash
sudo apt update -y

sudo apt install docker.io docker-compose-v2 -y

sudo usermod -aG docker $USER && newgrp docker
```
#
- Move to the cloned repository
```bash
cd Springboot-BankApp
```
#
- Build the Dockerfile
```bash
docker build -t madhupdevops/springboot-bankapp .
```
> [!Important]
> Make sure to change docker build command with your DockerHub username.
#
- Create a docker network
```bash
docker network create bankapp
```
#
- Run MYSQL container
```bash
docker run -itd --name mysql -e MYSQL_ROOT_PASSWORD=Test@123 -e MYSQL_DATABASE=BankDB --network=bankapp mysql
```
#
- Run Application container
```bash
docker run -itd --name BankApp -e SPRING_DATASOURCE_USERNAME="root" -e SPRING_DATASOURCE_URL="jdbc:mysql://mysql:3306/BankDB?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC" -e SPRING_DATASOURCE_PASSWORD="Test@123" --network=bankapp -p 8080:8080 madhupdevops/springboot-bankapp
```
#
- Verify deployment
```bash
docker ps
```
#
- Open port 8080 of your AWS instance and access your application
```bash
http://<public-ip>:8080
```
### Congratulations, you have deployed the application using Docker
#
- **<p id="dockercompose">Deployment using Docker compose</p>**
- Install docker compose
# 🚀 Multi-Tier Bank Application Deployment
This repository contains an **End-to-End Multi-Tier Bank Application** built using **Java (Spring Boot)**. The application is containerized, enabling seamless deployment on AWS EC2 instances using tools like **Docker**, **Docker Compose**, **Jenkins**, and more.

---

## 📌 Project Highlights

- **Technology Stack**: Java, Spring Boot, MySQL, Docker, Jenkins, AWS
- **Deployment Options**:
- Docker Networking
- Docker Compose
- Jenkins-based CI/CD

![App Architecture Diagram](images/login.png)
![Transaction Flow Diagram](images/transactions.png)

---

## 🛠 Pre-requisites

Ensure you have the following:
- **AWS Account**
- **Ubuntu EC2 Instance** (Recommended: `t2.medium`)
- **Installed Tools**:
- Docker
Comment on lines +21 to +25
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance prerequisites section with critical details

The prerequisites section should include:

  1. Minimum EC2 specifications (CPU, RAM, Storage)
  2. Required open ports (8080, 3306)
  3. Required AWS IAM permissions
  4. Docker version requirements
  5. Network requirements

Add these details under the "Pre-requisites" section:

 Ensure you have the following:  
 - **AWS Account**  
-  - **Ubuntu EC2 Instance** (Recommended: `t2.medium`)  
+  - **Ubuntu EC2 Instance**:
+    - Type: t2.medium (minimum)
+    - vCPUs: 2
+    - RAM: 4 GB
+    - Storage: 20 GB
 - **Installed Tools**:  
-  - Docker 
+  - Docker (version 20.10 or higher)
+- **Network Requirements**:
+  - Inbound ports: 8080 (application), 3306 (MySQL)
+  - Outbound: All traffic
+- **AWS Permissions**:
+  - EC2 full access
+  - VPC access
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Ensure you have the following:
- **AWS Account**
- **Ubuntu EC2 Instance** (Recommended: `t2.medium`)
- **Installed Tools**:
- Docker
Ensure you have the following:
- **AWS Account**
- **Ubuntu EC2 Instance**:
- Type: t2.medium (minimum)
- vCPUs: 2
- RAM: 4 GB
- Storage: 20 GB
- **Installed Tools**:
- Docker (version 20.10 or higher)
- **Network Requirements**:
- Inbound ports: 8080 (application), 3306 (MySQL)
- Outbound: All traffic
- **AWS Permissions**:
- EC2 full access
- VPC access

## 🏗 Deployment Steps

### **<p id="docker-networking">Deployment Using EC2 Instance</p>**

#### SSH into Your EC2 instance

```bash
sudo apt update
sudo apt install docker-compose-v2 -y
ssh -i "your pem- key" ubuntu@ec2-44-244-168-242.us-west
```
#
- Run docker-compose file present in the root directory of a project

Create a new Directory:
```bash
docker compose up -d
mkdir dir_name
```
#
- Access it on port 8080
Go into `cd dir_name`

#### Clone the Repository
```bash
git clone -b DevOps https://github.com/nkantamani2023/Springboot-BankApp.git
```
```bash
http://<public-ip>:8080
cd Springboot-BankApp
```
> [!Important]
> If you face issues with exiting docker container while running docker compose, run ``` docker compose down``` and then ``` docker compose up -d ```.
Install Docker
```bash
sudo apt update -y
```
```bash
sudo apt install docker.io && docker-compose-v2 -y
```
```bash
sudo usermod -aG docker $USER && newgrp docker
```

Build the Docker Image
```bash
docker build -t bankapp .
```
Create Docker Network
```bash
docker network create bankapp
```
Run MySQL Container
```bash
docker run -itd --name mysql \
-e MYSQL_ROOT_PASSWORD=${DB_PASSWORD} \
-e MYSQL_DATABASE=${DB_NAME} \
--network=bankapp mysql
```
Comment on lines +70 to +74
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add security considerations and environment variable setup

The MySQL container configuration needs security improvements and proper documentation:

  1. Environment variables are used but not defined
  2. MySQL container security can be enhanced
  3. Missing instructions for setting up environment variables

Add these security enhancements:

+# First, create a .env file (add to .gitignore):
+DB_NAME=BankDB
+DB_USER=root
+DB_PASSWORD=<strong-password>
+
 docker run -itd --name mysql \  
 -e MYSQL_ROOT_PASSWORD=${DB_PASSWORD} \
 -e MYSQL_DATABASE=${DB_NAME} \  
-  --network=bankapp mysql  
+  --network=bankapp \
+  --health-cmd='mysqladmin ping -h localhost' \
+  --health-interval=10s \
+  --health-timeout=5s \
+  --health-retries=3 \
+  mysql:8.0 \
+  --default-authentication-plugin=mysql_native_password \
+  --character-set-server=utf8mb4 \
+  --collation-server=utf8mb4_unicode_ci
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
docker run -itd --name mysql \
-e MYSQL_ROOT_PASSWORD=${DB_PASSWORD} \
-e MYSQL_DATABASE=${DB_NAME} \
--network=bankapp mysql
```
# First, create a .env file (add to .gitignore):
DB_NAME=BankDB
DB_USER=root
DB_PASSWORD=<strong-password>
docker run -itd --name mysql \
-e MYSQL_ROOT_PASSWORD=${DB_PASSWORD} \
-e MYSQL_DATABASE=${DB_NAME} \
--network=bankapp \
--health-cmd='mysqladmin ping -h localhost' \
--health-interval=10s \
--health-timeout=5s \
--health-retries=3 \
mysql:8.0 \
--default-authentication-plugin=mysql_native_password \
--character-set-server=utf8mb4 \
--collation-server=utf8mb4_unicode_ci

Run Application Container
```bash
docker run -itd --name BankApp \
-e SPRING_DATASOURCE_USERNAME=${DB_USER} \
-e SPRING_DATASOURCE_URL="jdbc:mysql://mysql:3306/BankDB?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC" \
-e SPRING_DATASOURCE_PASSWORD=${DB_PASSWORD} \
--network=bankapp \
--health-cmd="curl -f http://localhost:8080/actuator/health || exit 1" \
--health-interval=30s \
--restart=unless-stopped \
--memory="512m" \
--memory-swap="1g" \
-p 8080:8080 bankapp
```
Access the Application
Open Port 8080 of your AWS EC2 instance.
Navigate to: http://<public-ip>:8080
🎉 Congratulations! Your application is live.

🌐 Future Enhancements
CI/CD pipeline with Jenkins (Guide).

![App pipeline flow Diagram](images/jenkins-pipeline.png)
Step 1: Go to the AWS EC2 instance and copy your master-node public ip address and paste it in the browser address-bar with port `8080` .

Step 2: Go to AWS your master-node instaces and select security groups and go to add rule `8080` save this.
![Jenkins Output](images/jenkins-pipeline-op.png)
Step 3: Configure your pipeline.

```groovy
pipeline{
agent { label 'agent-slave' }

stages{
stage("Code Clone"){
steps{
echo "Code Clone Stage"
git url: "https://github.com/nkantamani2023/Springboot-BankApp.git", branch: "DevOps"
}
}
stage("Code Build & Test"){
steps{
echo "Code Build Stage"
sh "docker system prune -f"
sh "docker build -t bankapp ."
}
}
stage("Push To DockerHub"){
steps{
withCredentials([usernamePassword(
credentialsId:"dockerhub-creds",
usernameVariable:"dockerHubUser",
passwordVariable:"dockerHubPass")]){
sh 'echo $dockerHubPass | docker login -u $dockerHubUser --password-stdin'
sh "docker image tag bankapp:latest ${env.dockerHubUser}/bankapp:latest"
sh "docker push ${env.dockerHubUser}/bankapp:latest"
}
}
}
stage("Deploy"){
steps{
script {
try {
sh "docker compose -f docker-compose.yml down"
sh "docker compose -f docker-compose.yml up -d --build"
sh "timeout 300 bash -c 'while ! curl -s http://localhost:8080/actuator/health; do sleep 5; done'"
} catch (Exception e) {
sh "docker compose -f docker-compose.yml down"
sh "docker compose -f docker-compose.yml up -d --build --force-recreate previous-version"
error "Deployment failed: ${e.message}"
}
}
}
}
}
}
```

Step 4: Navigate to bank app dashboard and click on `Build Now` button.

Step 5: Your Pipeline has been created

#### CICD pipeline [guide](cicd.md)
👨‍💻 Author: Kantamani
Binary file added images/jenkins-pipeline-op.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/jenkins-pipeline.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.