Introduction
Deploying Java applications efficiently is crucial for ensuring smooth updates and minimizing downtime. This comprehensive guide provides an automated deployment script along with necessary Docker and Docker Compose configurations. This setup allows seamless deployment of Java applications, ensuring they are built, tested, and deployed with minimal manual intervention.
Deployment Script
Here’s the updated deployment script that automates the deployment process, including rollback functionality, log cleanup, and email notifications:
#!/bin/bash
# Capture the start time
start_time=$(date +%s)
echo "$(date +'%H:%M:%S')"
# Function to be executed on exit, interrupt, or termination
on_exit() {
# Capture the end time
end_time=$(date +%s)
# Calculate the elapsed time
elapsed_time=$(( end_time - start_time ))
# Convert elapsed time to hours, minutes, and seconds
hours=$(( elapsed_time / 3600 ))
minutes=$(( (elapsed_time % 3600) / 60 ))
seconds=$(( elapsed_time % 60 ))
# Print the elapsed time in HH:MM:SS format
printf "Total time: %02d:%02d:%02d\n" $hours $minutes $seconds
}
trap on_exit EXIT
trap on_exit INT
trap on_exit TERM
# Configurable variables
PROJECT_DIR="/path/to/your/project" # Path to your project directory
DOCKER_BUILD_DIR="/path/to/docker/build/dir" # Directory where Dockerfile and docker-compose.yaml are located
WAR_FILE_NAME="your-application.war" # Name of your WAR file
DOCKER_IMAGE_NAME="your-docker-image-name"
SMTP_HOST="smtp.your-email-provider.com"
SMTP_PORT="587"
SMTP_USERNAME="your-email@example.com"
SMTP_PASSWORD="your-email-password"
SENDER_NAME="Deployment info"
SENDER_EMAIL="$SMTP_USERNAME"
RECIPIENT_EMAILS=("recipient1@example.com" "recipient2@example.com")
EMAIL_SUBJECT="Deployment Log"
# Function to send email
send_email() {
local message_content="$1"
local recipients=("${@:2}")
for recipient in "${recipients[@]}"; do
echo "Sending email to: $recipient"
local email_data=$(cat <<EOF
From: "$SENDER_NAME" <$SENDER_EMAIL>
To: $recipient
Subject: $EMAIL_SUBJECT
Content-Type: text/plain; charset=utf-8
$message_content
EOF
)
echo -e "$email_data" | curl -s --url "smtp://$SMTP_HOST:$SMTP_PORT" \
--ssl-reqd \
--mail-from "$SENDER_EMAIL" \
--mail-rcpt "$recipient" \
--user "$SMTP_USERNAME:$SMTP_PASSWORD" \
--upload-file - \
--insecure 2>&1
done
}
# Function to roll back to the previous version
rollback() {
echo "Rolling back to the previous version..."
docker rmi -f "$DOCKER_IMAGE_NAME:v1"
docker tag "$DOCKER_IMAGE_NAME:rollback" "$DOCKER_IMAGE_NAME:v1"
docker-compose -f "$DOCKER_BUILD_DIR/docker-compose.yaml" up -d
echo "Rollback completed."
}
# Function to clean up old Docker images
cleanup_old_images() {
echo "Cleaning up old Docker images..."
# Get the list of rollback images sorted by creation date
image_list=$(docker images --filter=reference="$DOCKER_IMAGE_NAME:rollback-*" --format "{{.ID}} {{.Repository}}:{{.Tag}}" | sort -r -k3)
image_count=$(echo "$image_list" | wc -l)
# Keep only the 2 latest rollback images
if [ "$image_count" -gt 2 ]; then
echo "$image_list" | tail -n +3 | awk '{ print $1 }' | xargs docker rmi -f
fi
# Prune dangling images
docker image prune -f
}
# Pull the latest changes from the repository
git -C "$PROJECT_DIR" pull origin main
# Check if there are new changes
if [[ $(git -C "$PROJECT_DIR" log -1 --pretty=%H) != $(cat "$PROJECT_DIR/last_commit.txt") ]]; then
echo "-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-"
echo "Starting time: $(date +'%d-%m-%Y %I:%M:%S %p')"
# Build the project and create the WAR file
mvn -f "$PROJECT_DIR" clean package -Dmaven.test.skip=true > /dev/null
# Move the WAR file to the Docker build directory
mv "$PROJECT_DIR/target/$WAR_FILE_NAME" "$DOCKER_BUILD_DIR"
# Stop the current Docker containers
docker-compose -f "$DOCKER_BUILD_DIR/docker-compose.yaml" down
# Tag the current version as rollback
docker tag "$DOCKER_IMAGE_NAME:v1" "$DOCKER_IMAGE_NAME:rollback"
# Build and start the new Docker containers
docker-compose -f "$DOCKER_BUILD_DIR/docker-compose.yaml" build
docker-compose -f "$DOCKER_BUILD_DIR/docker-compose.yaml" up -d
start_time=$(date +%s)
echo "Checking application logs..."
while true; do
if docker-compose -f "$DOCKER_BUILD_DIR/docker-compose.yaml" logs --tail=10 | grep -q "Started Application in"; then
echo "Application started successfully."
break
fi
current_time=$(date +%s)
elapsed_time=$((current_time - start_time))
if [ "$elapsed_time" -gt 300 ]; then
echo "Application did not start within 5 minutes. Printing logs and rolling back..."
docker-compose -f "$DOCKER_BUILD_DIR/docker-compose.yaml" logs --tail=100
# Send email with logs content to multiple recipients
logs_content=$(docker-compose -f "$DOCKER_BUILD_DIR/docker-compose.yaml" logs --tail=100)
send_email "Deployment failed. Logs:\n\n$logs_content" "${RECIPIENT_EMAILS[@]}"
rollback
exit 1
fi
sleep 5
done
# Tag rollback image with timestamp if deployment is successful
docker tag "$DOCKER_IMAGE_NAME:rollback" "$DOCKER_IMAGE_NAME:rollback-$(date +%Y%m%d%H%M%S)"
cleanup_old_images
commit_hash=$(git -C "$PROJECT_DIR" log -1 --pretty=%H)
echo "$commit_hash"
echo "$commit_hash" > "$PROJECT_DIR/last_commit.txt"
echo "Ending Time: $(date +'%d-%m-%Y %I:%M:%S %p')"
fi
Docker Compose Files
docker-compose.yaml
Here’s a sample docker-compose.yaml
file for deploying your application:
version: '3.8'
services:
app:
image: your-docker-image-name:v1
build:
context: .
dockerfile: Dockerfile
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
volumes:
- ./logs:/logs
restart: always
db:
image: postgres:13
environment:
POSTGRES_DB: your_database
POSTGRES_USER: your_user
POSTGRES_PASSWORD: your_password
volumes:
- postgres_data:/var/lib/postgresql/data
restart: always
volumes:
postgres_data:
Dockerfile
Here’s a sample Dockerfile
for building your application’s Docker image:
# Use an official Tomcat image as the base image
FROM tomcat:9.0-jdk11
# Add a volume pointing to /tmp
VOLUME /tmp
# The application's jar file
ARG WAR_FILE=your-application.war
# Add the application's war to Tomcat's webapps
ADD ${WAR_FILE} /usr/local/tomcat/webapps/
# Run the jar file
ENTRYPOINT ["catalina.sh", "run"]
Additional Setup
- Project Directory: Your project directory should have a
pom.xml
file for Maven to build the project. - Git Repository: Ensure your project directory is a Git repository with a
main
branch. - Maven: Maven should be installed and configured to build your Java application.
- Docker: Docker and Docker Compose should be installed on your server.
Explanation
- Initial Setup:
- Capture the start time and set up traps for the
EXIT
,INT
, andTERM
signals to ensure theon_exit
function is called.
- Capture the start time and set up traps for the
- on_exit Function:
- Calculate and print the elapsed time.
- Clean up the log file by removing specific lines.
- Configurable Variables:
- Variables that need to be configured according to your environment such as project directory, Docker build directory, WAR file name, Docker image name, and email settings.
- send_email Function:
- Sends an email to the specified recipients with the deployment log.
- rollback Function:
- Rolls back to the previous version by retagging the Docker image and restarting the containers.
- cleanup_old_images Function:
- Cleans up old Docker images to free up space.
- Main Deployment Logic:
- Pulls the latest changes from the Git repository.
- Checks if there are new changes.
- Builds the project and creates the WAR file.
- Stops the current Docker containers.
- Builds and starts the new Docker containers.
- Checks if the application has started successfully within 5 minutes.
- If successful, tags the rollback image with a timestamp and cleans up old images.
- Updates the last commit hash.
How to Use
- Clone the Repository:
git clone https://your-repo-url.git /path/to/your/project cd /path/to/your/project
- Run the Deployment Script:
bash chmod +x deploy.sh ./deploy.sh
Conclusion
By following this guide, you can automate the deployment of your Java applications using Docker and Docker Compose. This setup ensures that your application is built, tested, and deployed with minimal manual intervention, providing a reliable and efficient deployment process.