diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..ed5429bb8 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,60 @@ +# Multi-stage Dockerfile for Spring PetClinic +# Stage 1: Build +FROM eclipse-temurin:21-jdk-jammy AS builder + +WORKDIR /app + +# Copy Maven wrapper and pom.xml for dependency caching +COPY .mvn/ .mvn/ +COPY mvnw pom.xml ./ + +# Download dependencies +RUN ./mvnw dependency:go-offline -B + +# Copy source code +COPY src ./src + +# Build application +RUN ./mvnw clean package -DskipTests -B + +# Extract JAR layers for better caching +RUN mkdir -p target/extracted && \ + java -Djarmode=layertools -jar target/*.jar extract --destination target/extracted + +# Stage 2: Runtime +FROM eclipse-temurin:21-jre-jammy + +# Install curl for health checks +RUN apt-get update && \ + apt-get install -y --no-install-recommends curl && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +# Create non-root user +RUN groupadd -r spring && useradd -r -g spring spring + +WORKDIR /app + +# Copy extracted layers from builder +COPY --from=builder /app/target/extracted/dependencies/ ./ +COPY --from=builder /app/target/extracted/spring-boot-loader/ ./ +COPY --from=builder /app/target/extracted/snapshot-dependencies/ ./ +COPY --from=builder /app/target/extracted/application/ ./ + +# Change ownership +RUN chown -R spring:spring /app + +# Switch to non-root user +USER spring:spring + +EXPOSE 8080 + +# Health check +HEALTHCHECK --interval=30s --timeout=3s --start-period=60s --retries=3 \ + CMD curl -f http://localhost:8080/actuator/health || exit 1 + +# JVM options for containers +ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0" + +# Run application +ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS org.springframework.boot.loader.launch.JarLauncher"] \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index b2313a1e6..8dd63798a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,21 +1,73 @@ +version: '3.9' + services: + petclinic: + build: + context: . + dockerfile: Dockerfile + image: petclinic:local + container_name: petclinic-app + ports: + - "8080:8080" + environment: + - SPRING_PROFILES_ACTIVE=postgres + - SPRING_DATASOURCE_URL=jdbc:postgresql://postgres:5432/petclinic + - SPRING_DATASOURCE_USERNAME=petclinic + - SPRING_DATASOURCE_PASSWORD=petclinic + - JAVA_OPTS=-XX:MaxRAMPercentage=75.0 + depends_on: + postgres: + condition: service_healthy + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 60s + networks: + - petclinic-network + + # MySQL Database mysql: - image: mysql:9.5 + image: mysql:9.2 + container_name: petclinic-mysql ports: - "3306:3306" environment: - - MYSQL_ROOT_PASSWORD= - - MYSQL_ALLOW_EMPTY_PASSWORD=true + - MYSQL_ROOT_PASSWORD=root - MYSQL_USER=petclinic - MYSQL_PASSWORD=petclinic - MYSQL_DATABASE=petclinic volumes: - "./conf.d:/etc/mysql/conf.d:ro" + - mysql-data:/var/lib/mysql + networks: + - petclinic-network + + # PostgreSQL Database postgres: - image: postgres:18.1 + image: postgres:15-alpine + container_name: petclinic-postgres ports: - "5432:5432" environment: - - POSTGRES_PASSWORD=petclinic - - POSTGRES_USER=petclinic - POSTGRES_DB=petclinic + - POSTGRES_USER=petclinic + - POSTGRES_PASSWORD=petclinic + volumes: + - postgres-data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U petclinic -d petclinic"] + interval: 10s + timeout: 5s + retries: 5 + networks: + - petclinic-network + +networks: + petclinic-network: + driver: bridge + +volumes: + mysql-data: + postgres-data: \ No newline at end of file