diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..fbbe580 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,13 @@ +.github +.mvn +.vscode +target +.codacy.yml +.gitignore +azure-pipelines.yml +CODE_OF_CONDUCT.md +codecov.yml +CONTRIBUTING.md +LICENSE +mvnw +mvnw.cmd diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 409614c..27103df 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -66,3 +66,37 @@ jobs: with: project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} force-coverage-parser: jacoco -r jacoco.xml + + container: + needs: coverage + runs-on: ubuntu-latest + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} + + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4.2.2 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3.4.0 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3.10.0 + + - name: Build and push Docker image to GitHub Container Registry + uses: docker/build-push-action@v6.17.0 + with: + context: . + push: true + platforms: linux/amd64 + provenance: false + tags: | + ghcr.io/${{ github.repository }}:latest + ghcr.io/${{ github.repository }}:sha-${{ github.sha }} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7cea3e1 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,61 @@ +# ------------------------------------------------------------------------------ +# Stage 1: Builder +# This stage builds the application and its dependencies. +# ------------------------------------------------------------------------------ +FROM maven:3.9-eclipse-temurin-21-alpine AS builder + +WORKDIR /app + +# Copy pom.xml and download dependencies. This will be cached until pom.xml +# changes, speeding up the build process. +COPY pom.xml . +RUN mvn dependency:go-offline -B + +# Copy source code and build the application, skipping tests for faster builds. +COPY src ./src +RUN mvn clean package -DskipTests + +# ------------------------------------------------------------------------------ +# Stage 2: Runtime +# This stage creates the final, minimal image to run the application. +# ------------------------------------------------------------------------------ +FROM eclipse-temurin:21-jdk-alpine AS runtime + +WORKDIR /app + +# Install curl for health check +RUN apk add --no-cache curl + +# Metadata labels for the image. These are useful for registries and inspection. +LABEL org.opencontainers.image.title="๐Ÿงช RESTful Web Service with Spring Boot" +LABEL org.opencontainers.image.description="Proof of Concept for a RESTful Web Service made with JDK 21 (LTS) and Spring Boot 3" +LABEL org.opencontainers.image.licenses="MIT" +LABEL org.opencontainers.image.source="https://github.com/nanotaboada/java.samples.spring.boot" + +# https://rules.sonarsource.com/docker/RSPEC-6504/ + +# Copy application JAR file from the builder stage +COPY --from=builder /app/target/*.jar ./app.jar + +# Copy metadata docs for container registries (e.g.: GitHub Container Registry) +COPY --chmod=444 README.md ./ +COPY --chmod=555 assets/ ./assets/ + +# Copy entrypoint and healthcheck scripts +COPY --chmod=555 scripts/entrypoint.sh ./entrypoint.sh +COPY --chmod=555 scripts/healthcheck.sh ./healthcheck.sh + +# Add system user +RUN addgroup -S spring && \ + adduser -S -G spring spring + +USER spring + +EXPOSE 9000 +EXPOSE 9001 + +HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \ + CMD ["./healthcheck.sh"] + +ENTRYPOINT ["./entrypoint.sh"] +CMD ["java", "-jar", "./app.jar"] diff --git a/README.md b/README.md index ae52f3a..10636e8 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Sample RESTful Web Service with Spring Boot +# ๐Ÿงช RESTful Web Service with Spring Boot ## Status @@ -27,6 +27,30 @@ http://localhost:9000/swagger/index.html ![API Documentation](assets/images/swagger.png) +## Container + +### Docker Compose + +This setup uses [Docker Compose](https://docs.docker.com/compose/) to build and run the app + +#### Build the image + +```bash +docker compose build +``` + +#### Start the app + +```bash +docker compose up +``` + +#### Stop the app + +```bash +docker compose down +``` + ## Credits The solution has been coded using [Visual Studio Code](https://code.visualstudio.com/) with the [Extension Pack for Java](https://marketplace.visualstudio.com/items?itemName=vscjava.vscode-java-pack) diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 0000000..2626fe3 --- /dev/null +++ b/compose.yaml @@ -0,0 +1,15 @@ +name: java-samples-spring-boot + +services: + api: + image: java-samples-spring-boot + container_name: spring-app + build: + context: . + dockerfile: Dockerfile + ports: + - "9000:9000" + - "9001:9001" + environment: + - SPRING_PROFILES_ACTIVE=production + restart: unless-stopped diff --git a/scripts/entrypoint.sh b/scripts/entrypoint.sh new file mode 100644 index 0000000..af8ed35 --- /dev/null +++ b/scripts/entrypoint.sh @@ -0,0 +1,11 @@ +#!/bin/sh +set -e + +echo "โœ” Starting Spring Boot container..." + +echo "โœ” Server Port: ${SERVER_PORT:-9000}" +echo "โœ” Management Port: ${MANAGEMENT_PORT:-9001}" +echo "โœ” Active Profile(s): ${SPRING_PROFILES_ACTIVE:-default}" + +echo "๐Ÿš€ Launching Spring Boot app..." +exec "$@" diff --git a/scripts/healthcheck.sh b/scripts/healthcheck.sh new file mode 100644 index 0000000..fd0e099 --- /dev/null +++ b/scripts/healthcheck.sh @@ -0,0 +1,8 @@ +#!/bin/sh +set -e + +# Use Actuator endpoint for health check +ACTUATOR_HEALTH_URL="http://localhost:9001/actuator/health" + +# Curl with fail-fast behavior and silent output +curl --fail --silent --show-error --connect-timeout 1 --max-time 2 "$ACTUATOR_HEALTH_URL" diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index d5d7ec3..33aede6 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,6 +1,6 @@ server.port: 9000 management.server.port: 9001 -management.server.address: 127.0.0.1 +management.server.address: 0.0.0.0 management.endpoints.web.exposure.include=health,info,mappings # http://localhost:9001/actuator/health