diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..25049b9 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,37 @@ +# ---- Stage 1: Build ---- +# Uses the full JDK image to compile the application with Maven +FROM eclipse-temurin:25-jdk AS build +WORKDIR /app + +# Copy Maven wrapper and pom.xml first to leverage Docker layer caching +# Dependencies are downloaded only when pom.xml changes +COPY mvnw pom.xml ./ +COPY .mvn .mvn +RUN chmod +x mvnw && ./mvnw dependency:go-offline -B + +# Copy source code and build the JAR (tests are skipped as they run in CI) +COPY src src +RUN ./mvnw package -DskipTests -B + +# ---- Stage 2: Run ---- +# Uses a lightweight JRE-only image for a smaller and more secure final image +FROM eclipse-temurin:25-jre +WORKDIR /app + +# Create a non-root user and group for running the application securely +RUN groupadd --system appgroup && useradd --system --gid appgroup appuser + +# Copy the built JAR from the build stage using a stable pattern so version changes do not break the image build +COPY --from=build /app/target/*.jar app.jar + +# Ensure the non-root user owns the application files +RUN chown -R appuser:appgroup /app + +# Switch to the non-root user +USER appuser + +# Document the port the application listens on +EXPOSE 8080 + +# Start the Spring Boot application +ENTRYPOINT ["java", "-jar", "app.jar"] diff --git a/README.md b/README.md index 96e6545..63aa953 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,69 @@ docker-compose up -d --- +## 🐳 Docker + +### Architecture + +The application can be fully containerized using a **multi-stage Dockerfile** and **Docker Compose**. + +The `app` container connects to the `postgres` container over the `xpeho_network` bridge network using the PostgreSQL service name as the hostname. +``` +┌─────────────────── xpeho_network (bridge) ───────────────────┐ +│ │ +│ ┌──────────┐ jdbc:postgresql:// ┌──────────┐ │ +│ │ app │ ──────── postgres:5432 ────────▶ │ postgres │ │ +│ │ :8080 │ (service name) │ :5432 │ │ +│ └──────────┘ └──────────┘ │ +│ │ +└───────────────────────────────────────────────────────────────┘ +``` + +| Service | Image / Build | Role | Exposed Port | +|------------|------------------------------|----------------------------|---------------------------| +| `postgres` | `postgres:17-alpine` | PostgreSQL database | `${POSTGRES_PORT}` → 5432 | +| `app` | Built from `Dockerfile` | Spring Boot application | 8080 → 8080 | + +### Dockerfile — Multi-stage Build + +The Dockerfile uses two stages to produce a lightweight, secure final image: + +| Stage | Image | Role | +|-------|-------|------| +| **Build** | `eclipse-temurin:25-jdk` | Compiles the JAR with Maven (full JDK) | +| **Run** | `eclipse-temurin:25-jre` | Runs the application (lightweight JRE, non-root user) | + +> **Why Eclipse Temurin?** Reference OpenJDK distribution: free, open-source, maintained by the Eclipse Foundation (Adoptium). + +> **Security:** The final image runs as a non-root user (`appuser`), without source code or build tools. + +### Compose Profiles + +The `app` service is behind a **Compose profile** to avoid interfering with the dev/CI workflow: + +```bash +# Start PostgreSQL only (dev, tests, CI) +docker compose up -d + +# Start PostgreSQL + Application (full deployment) +docker compose --profile app up -d --build +``` + +### Useful Commands + +```bash +# View application logs +docker compose logs -f app + +# Stop and remove containers +docker compose down + +# Stop and remove containers + volumes (reset DB) +docker compose down -v +``` + +--- + ## ⚙️ Configuration ### Environment Variables (.env) @@ -56,6 +119,11 @@ POSTGRES_USER=your_user POSTGRES_PASSWORD=your_password POSTGRES_DB=your_database POSTGRES_PORT=5432 + +# Liquibase (optional, defaults provided) +LB_CHANGELOG=db/changelog/db.changelog-master.yaml +LB_SCHEMA=public +SPRING_LIQUIBASE_ENABLED=true ``` ### External API Configuration diff --git a/docker-compose.yml b/docker-compose.yml index 93fac5a..efe7983 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -18,6 +18,30 @@ services: networks: - xpeho_network + app: + profiles: + - app + build: + context: . + dockerfile: Dockerfile + container_name: xpeho_app + environment: + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_DB: ${POSTGRES_DB} + POSTGRES_PORT: 5432 + SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/${POSTGRES_DB} + SPRING_LIQUIBASE_ENABLED: ${SPRING_LIQUIBASE_ENABLED:-true} + LB_CHANGELOG: ${LB_CHANGELOG:-db/changelog/db.changelog-master.yaml} + LB_SCHEMA: ${LB_SCHEMA:-public} + ports: + - "8080:8080" + depends_on: + postgres: + condition: service_healthy + networks: + - xpeho_network + volumes: postgres_data: driver: local