A complete end-to-end CI/CD pipeline that demonstrates how to build, test, and deploy a Java application to AWS infrastructure that is provisioned on-demand using Terraform.
This project showcases the integration of several DevOps practices:
- Continuous Integration: Automated testing and building with Jenkins
- Continuous Deployment: Automated deployment to cloud infrastructure
- Infrastructure as Code (IaC): AWS resources provisioned via Terraform
- Containerization: Application packaged and deployed as Docker containers
┌─────────────────────────────────────────────────────────────────────────────┐
│ CI/CD PIPELINE FLOW │
└─────────────────────────────────────────────────────────────────────────────┘
┌──────────┐ ┌─────────────────────────────────────────────────────┐
│ GitHub │────▶│ JENKINS │
│ Repo │ │ ┌──────┐ ┌─────────┐ ┌───────┐ ┌─────────────┐ │
└──────────┘ │ │ Test │─▶│ Version │─▶│ Build │─▶│ Build Image │ │
│ └──────┘ │Increment│ │ JAR │ └──────┬──────┘ │
│ └─────────┘ └───────┘ │ │
└───────────────────────────────────────────┼─────────┘
│
┌───────────────────────────────────────────▼─────────┐
│ DOCKER HUB │
│ (Image Registry) │
└───────────────────────────────────────────┬─────────┘
│
┌────────────────────────────────────────────────────────────▼─────────┐
│ AWS INFRASTRUCTURE │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ VPC (10.0.0.0/16) │ │
│ │ ┌───────────────────────────────────────────────────────────┐ │ │
│ │ │ Subnet (10.0.10.0/24) │ │ │
│ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │
│ │ │ │ EC2 Instance (Amazon Linux) │ │ │ │
│ │ │ │ ┌─────────────────┐ ┌─────────────────────────┐ │ │ │ │
│ │ │ │ │ Java App │ │ PostgreSQL │ │ │ │ │
│ │ │ │ │ (Port 8080) │ │ (Port 5432) │ │ │ │ │
│ │ │ │ └─────────────────┘ └─────────────────────────┘ │ │ │ │
│ │ │ │ Docker Compose │ │ │ │
│ │ │ └─────────────────────────────────────────────────────┘ │ │ │
│ │ └───────────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────┘
| Category | Technology |
|---|---|
| Application | Java 8, Spring Boot 2.3.x |
| Build Tool | Maven 3.9.x |
| CI/CD | Jenkins (with Shared Library) |
| Containerization | Docker, Docker Compose |
| Infrastructure | Terraform, AWS (VPC, EC2) |
| Image Registry | Docker Hub |
├── src/ # Application source code
│ ├── main/
│ │ ├── java/com/example/ # Java application files
│ │ │ └── Application.java # Spring Boot entry point
│ │ └── resources/static/ # Static web resources
│ │ └── index.html # Simple frontend
│ └── test/java/ # Unit tests
│ └── AppTest.java # JUnit test cases
├── terraform/ # Infrastructure as Code
│ ├── main.tf # AWS resource definitions
│ ├── varibales.tf # Terraform variables
│ └── entry-script.sh # EC2 user data script
├── Jenkinsfile # CI/CD pipeline definition
├── Dockerfile # Container image definition
├── docker-compose.yaml # Multi-container orchestration
├── server-cmds.sh # Deployment commands for EC2
└── pom.xml # Maven build configuration
The Jenkins pipeline executes the following stages sequentially:
┌────────┐ ┌───────────┐ ┌───────────┐ ┌────────────┐ ┌───────────┐ ┌────────┐
│ Test │──▶│ Version │──▶│ Build │──▶│ Build & │──▶│ Provision │──▶│ Deploy │
│ │ │ Increment │ │ JAR │ │ Push Image │ │ Infra │ │ │
└────────┘ └───────────┘ └───────────┘ └────────────┘ └───────────┘ └────────┘
Runs Maven tests to ensure code quality before proceeding.
mvn testAutomatically increments the application version in pom.xml using a shared library function. This ensures each build produces a uniquely versioned artifact.
Compiles the Java application and packages it into an executable JAR file.
- Builds a Docker image with the JAR file
- Tags it with the incremented version
- Pushes to Docker Hub registry
Uses Terraform to create AWS infrastructure on-demand:
- VPC with custom CIDR block
- Public subnet
- Internet Gateway
- Security Groups (SSH + Application ports)
- EC2 instance with Docker pre-installed
- Waits for EC2 instance initialization (90 seconds)
- Copies deployment scripts via SSH
- Pulls Docker image and starts containers using Docker Compose
A minimal Spring Boot application that serves a simple web page:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}Why Spring Boot? It provides a production-ready framework with minimal configuration, making it ideal for demonstrating DevOps pipelines without complex application setup.
FROM amazoncorretto:8-alpine3.17-jre
EXPOSE 8080
COPY ./target/java-maven-app-*.jar /usr/app/
WORKDIR /usr/app
CMD java -jar java-maven-app-*.jarKey Points:
- Uses Amazon Corretto (AWS-optimized JRE) for compatibility with AWS deployments
- Alpine-based image for smaller footprint
- Copies the built JAR from Maven's target directory
- Exposes port 8080 for the web application
services:
java-maven-app:
image: ${IMAGE} # Dynamically set via environment variable
ports:
- 8080:8080
postgres:
image: postgres:16
ports:
- 5432:5432Why Docker Compose? It allows defining multi-container applications. Here it runs:
- The Java application
- A PostgreSQL database (for potential future use)
| Resource | Purpose |
|---|---|
aws_vpc |
Isolated network environment |
aws_subnet |
Network segment within VPC |
aws_internet_gateway |
Enables internet access |
aws_default_route_table |
Routes traffic to internet gateway |
aws_default_security_group |
Firewall rules (SSH: 22, App: 8000) |
aws_instance |
EC2 server running the application |
#!/bin/bash
sudo yum update -y && sudo yum install -y docker
sudo systemctl start docker
sudo usermod -aG docker ec2-user
# Install docker-compose
sudo curl -SL "...docker-compose..." -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-composeThis script runs automatically when the EC2 instance launches, installing Docker and Docker Compose.
The pipeline leverages a Jenkins Shared Library for reusable functions:
library identifier: 'jenkins-shared-library-master@main', retriever: modernSCM([...])Shared Library Functions Used:
| Function | Purpose |
|---|---|
incrementVersionMvn() |
Bumps version in pom.xml |
buildJar() |
Runs Maven build |
buildImage() |
Creates Docker image |
dockerLogin() |
Authenticates with Docker Hub |
dockerPush() |
Pushes image to registry |
Why Shared Libraries? They promote code reuse across multiple Jenkins pipelines and keep the Jenkinsfile clean and maintainable.
#!/usr/bin/env bash
export IMAGE=${IMAGE:-$1}
export DOCKER_USER=${DOCKER_USER:-$2}
export DOCKER_PWD=${DOCKER_PWD:-$3}
echo "$DOCKER_PWD" | docker login -u "$DOCKER_USER" --password-stdin
docker-compose -f docker-compose.yaml up --detachThis script runs on the EC2 instance to:
- Authenticate with Docker Hub
- Pull and start the containers in detached mode
Before using this pipeline, ensure you have:
- Jenkins server with Maven tool configured (
maven-3.9.11) - Jenkins Shared Library configured
- Credentials configured:
docker-hub- Docker Hub username/passwordaws_access_key_id- AWS access keyaws_secret_access_key- AWS secret keyserver-ssh-key- SSH key for EC2 access
- AWS account with appropriate IAM permissions
- S3 bucket for Terraform state (
tf-practice-remote-backend) - EC2 key pair created (
tf-cicd-key)
- Java 8 JDK
- Maven 3.x
- Docker
- Terraform
mvn clean packagemvn testdocker build -t java-maven-app .export IMAGE=java-maven-app
docker-compose upAccess the application at http://localhost:8080
| Concept | Implementation |
|---|---|
| CI/CD Pipeline | Jenkins multi-stage pipeline |
| Infrastructure as Code | Terraform managing AWS resources |
| Immutable Infrastructure | New EC2 instance per deployment |
| Configuration Management | Terraform variables & environment variables |
| Containerization | Docker packaging of application |
| Container Orchestration | Docker Compose for multi-container setup |
| Version Control | Git-based workflow with automated versioning |
| Secrets Management | Jenkins credentials store |
| Remote State | Terraform S3 backend for state management |
- Use private subnets with NAT gateway
- Implement HTTPS with SSL certificates
- Use AWS Secrets Manager for sensitive data
- Enable VPC flow logs
- Implement proper IAM roles instead of access keys
- Add application load balancer for high availability
- Jenkins Pipeline Documentation
- Terraform AWS Provider
- Docker Compose Documentation
- Spring Boot Documentation