Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
# .dockerignore
target
.git
*.log
*.class
.idea
.vscode
119 changes: 119 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
name: CI (self-hosted-safe)

on:
push:
branches:
- 'feature/*'
tags:
- 'v*'
pull_request:
branches:
- 'feature/*'

permissions:
contents: read

env:
DOCKER_REPO: brunoe/javahello
LOCAL_REPO_PREFIX: javahello

jobs:
validate:
name: Validate (Maven, GH-hosted)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up JDK 21
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 21

- name: Cache Maven repo
uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-m2-

- name: Validate
run: ./mvnw -DskipTests validate

build-docker:
name: Build & smoke-test images (self-hosted, matrix)
needs: validate
runs-on: [self-hosted, Linux, X64]
if: >
github.event_name == 'push'
|| (github.event_name == 'pull_request'
&& github.event.pull_request.head.repo.full_name == github.repository)

strategy:
matrix:
image:
- { tag: "01", name: "mavenimage", file: "Dockerfile.01.mavenimage" }
- { tag: "02", name: "mavenimagestage", file: "Dockerfile.02.mavenimagestage" }
- { tag: "03", name: "dockercache", file: "Dockerfile.03.dockercache" }
- { tag: "05", name: "manual", file: "Dockerfile.05.manual" }
- { tag: "06", name: "jlink", file: "Dockerfile.06.jlink" }
- { tag: "06b", name: "jlink-alpine", file: "Dockerfile.06b.jlink-alpine" }
- { tag: "07", name: "graalvm", file: "Dockerfile.07.graalVM" }

steps:
- uses: actions/checkout@v4

- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Cache Maven repo
uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}

- name: Build image
run: |
docker buildx build \
--progress=plain \
--load \
-t $LOCAL_REPO_PREFIX:${{ matrix.image.tag }} \
-t $LOCAL_REPO_PREFIX:${{ matrix.image.name }} \
-f ${{ matrix.image.file }} .

- name: Smoke test
run: |
NAME=${{ matrix.image.name }}

# skip smoke-run for heavy native image (graalvm) in CI matrix
if [ "$NAME" = "graalvm" ]; then
echo "Skipping smoke test for graalvm native image"
exit 0
fi

docker run --rm $LOCAL_REPO_PREFIX:$NAME

publish:
name: Publish images (tags only)
runs-on: [self-hosted, Linux, X64]
needs: build-docker
if: startsWith(github.ref, 'refs/tags/')

steps:
- uses: actions/checkout@v4

- name: Login to registry
uses: docker/login-action@v2
with:
registry: docker.io
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Push images
run: |
docker tag $LOCAL_REPO_PREFIX:jlink-alpine $DOCKER_REPO:jlink-alpine-${{ github.sha }}
docker push $DOCKER_REPO:jlink-alpine-${{ github.sha }}

docker tag $LOCAL_REPO_PREFIX:graalvm $DOCKER_REPO:graalvm-${{ github.sha }}
docker push $DOCKER_REPO:graalvm-${{ github.sha }}
20 changes: 2 additions & 18 deletions .mvn/wrapper/maven-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,19 +1,3 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
wrapperVersion=3.3.2
wrapperVersion=3.3.4
distributionType=only-script
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.12/apache-maven-3.9.12-bin.zip
56 changes: 38 additions & 18 deletions Dockerfile.01.mavenimage
Original file line number Diff line number Diff line change
@@ -1,30 +1,50 @@
# Base image with Maven and Java 21
FROM maven:3.9.6-eclipse-temurin-21-jammy
# ------------------------------------------------------------
# Stage: build (single-stage)
# Purpose: compile the application using Maven and package the artifact
# Base image: maven:3.9.12-eclipse-temurin-21-noble
# Maven profile: -Pprod
# Artifact: target/hello-world-0.0.1-SNAPSHOT.jar
# Notes: single-stage image includes build tools; consider multi-stage to reduce final image size
# ------------------------------------------------------------
FROM maven:3.9.12-eclipse-temurin-21-noble

# Set working directory
WORKDIR /app

# Add metadata
LABEL maintainer="Emmanuel Bruno <emmanuel.bruno@univ-tln.fr>"
LABEL description="Java Hello World Application - Full Maven image"
LABEL version="0.1.0-SNAPSHOT"
LABEL license="MIT"

WORKDIR /app

# Copy POM first to leverage Docker cache for dependencies
# Copy wrapper
COPY mvnw ./
COPY .mvn .mvn
RUN chmod +x mvnw

# Copy POM first to leverage Docker cache
COPY pom.xml ./
RUN mvn dependency:resolve
RUN ./mvnw --batch-mode dependency:resolve

# Copy source code and build the application
COPY src ./src
RUN mvn clean verify
RUN ./mvnw --batch-mode -Pprod -DskipTests clean package

# Copy entrypoint script and make it executable
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh || true

# Prepare a canonical runtime artifact path (/app/app.jar) if build produced a jar
# Use wildcard to match the built jar (SNAPSHOT or versioned)
RUN cp target/*.jar /app/app.jar 2>/dev/null || true
# Copy runtime dependency jars (produced by -Pprod) so manifest Class-Path 'libs/' resolves
RUN cp -r target/libs /app/libs 2>/dev/null || true

# Configure Java options for container environment
ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0"
# Create non-root user (only for running)
RUN groupadd -r appuser && useradd -r -g appuser -m appuser \
&& chown -R appuser:appuser /app

# Choose one of the following run methods:
# 1. Using Maven exec plugin (development)
CMD ["mvn", "--quiet", "exec:java", "-Dexec.mainClass=fr.univtln.bruno.demos.docker.App"]
USER appuser
ENV HOME=/home/appuser

# 2. Using JAR file directly (production recommended)
# CMD java $JAVA_OPTS -jar target/*-jar-with-dependencies.jar
ENV JAVA_OPTS="-XX:MaxRAMPercentage=75"

# 3. Using classpath (alternative)
# CMD java $JAVA_OPTS -cp target/*-jar-with-dependencies.jar fr.univtln.bruno.demos.docker.App
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
63 changes: 48 additions & 15 deletions Dockerfile.02.mavenimagestage
Original file line number Diff line number Diff line change
@@ -1,31 +1,64 @@
# Multi-stage build for Java application
# Stage 1: Build environment
FROM maven:3.9.6-eclipse-temurin-21-jammy AS stage-build
# ------------------------------------------------------------
# Stage: build
# Purpose: compile the application using Maven in the build stage
# Base image: maven:3.9.12-eclipse-temurin-21-noble
# Maven profile: -Pprod
# Artifact: target/hello-world-*-SNAPSHOT.jar
# Notes: use cache mounts for /root/.m2 to speed up dependency resolution
# ------------------------------------------------------------
FROM maven:3.9.12-eclipse-temurin-21-noble AS stage-build

WORKDIR /app

LABEL maintainer="Emmanuel Bruno <emmanuel.bruno@univ-tln.fr>"
LABEL description="Java Hello World Application - multi-stage build"
LABEL version="0.1.0-SNAPSHOT"
LABEL license="MIT"

# Copy wrapper
COPY mvnw ./
COPY .mvn .mvn
RUN chmod +x mvnw

# Copy POM first to leverage Docker cache for dependencies
COPY pom.xml ./
RUN mvn dependency:resolve
RUN ./mvnw --batch-mode dependency:resolve

# Copy source code and build the application
COPY src ./src
RUN mvn -P uberjar clean verify
RUN ./mvnw --batch-mode -Pprod -DskipTests clean package
# ------------------------------------------------------------
# Stage: runtime
# Purpose: provide a minimal runtime image with Temurin JRE
# Base image: eclipse-temurin:21.0.9_10-jre-noble
# Copies: /app/app.jar (from build stage) and /app/libs if provided
# Notes: run the application as a non-root user; keep runtime image minimal
# ------------------------------------------------------------
FROM eclipse-temurin:21.0.9_10-jre-noble

# Stage 2: Runtime environment
FROM eclipse-temurin:21-jre-jammy
LABEL maintainer="Emmanuel Bruno <emmanuel.bruno@univ-tln.fr>"
LABEL description="Java Hello World Application - multi-stage"
LABEL description="Java Hello World Application - multi-stage runtime"

WORKDIR /app

# Copy only the built jar from the build stage
COPY --from=stage-build /app/target/*-jar-with-dependencies.jar /myapp.jar
COPY --from=stage-build /app/target/hello-world-*-SNAPSHOT.jar /app/app.jar
# Copy the libs directory (containing dependencies) if needed
COPY --from=stage-build /app/target/libs /app/libs

# Install entrypoint script (exec form) and make it executable
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh || true

# Create a non-root user for security and ensure app files are owned by that user
RUN groupadd -r appuser && useradd -r -u 1001 -g appuser -m appuser \
&& chown -R appuser:appuser /app

# Create a non-root user for security
RUN useradd -r -u 1001 -g root appuser
USER appuser
ENV HOME=/home/appuser

# Configure Java options for container environment
# Configure Java options for container environment (default, overridable)
ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0"

# Run the application
ENTRYPOINT ["java", "-jar"]
CMD ["/myapp.jar"]
# Use exec-form entrypoint script to preserve signals and expand JAVA_OPTS
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
29 changes: 0 additions & 29 deletions Dockerfile.03.cache

This file was deleted.

64 changes: 64 additions & 0 deletions Dockerfile.03.dockercache
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# ------------------------------------------------------------
# Stage: build
# Purpose: compile the application using Maven with cache mounts enabled
# Base image: maven:3.9.12-eclipse-temurin-21-noble
# Maven profile: -Pprod
# Artifact: target/hello-world-*-SNAPSHOT.jar
# Notes: uses --mount=type=cache for /root/.m2 to speed up builds; cache is not persisted in final image
# ------------------------------------------------------------
FROM maven:3.9.12-eclipse-temurin-21-noble AS stage-build

WORKDIR /app

LABEL maintainer="Emmanuel Bruno <emmanuel.bruno@univ-tln.fr>"
LABEL description="Java Hello World Application - multi-stage build"
LABEL version="0.1.0-SNAPSHOT"
LABEL license="MIT"

# Copy wrapper
COPY mvnw ./
COPY .mvn .mvn
RUN chmod +x mvnw

# Copy POM first to leverage Docker cache for dependencies
COPY pom.xml ./
RUN --mount=type=cache,target=/root/.m2 ./mvnw --batch-mode dependency:resolve

# Copy source code and build the application
COPY src ./src
RUN --mount=type=cache,target=/root/.m2 ./mvnw --batch-mode -Pprod -DskipTests clean package

# ------------------------------------------------------------
# Stage: runtime
# Purpose: minimal runtime with Temurin JRE containing the packaged application
# Base image: eclipse-temurin:21.0.9_10-jre-noble
# Copies: /app/app.jar and /app/libs from build stage
# Notes: create a non-root user for security and set JAVA_OPTS appropriately
# ------------------------------------------------------------
FROM eclipse-temurin:21.0.9_10-jre-noble

LABEL maintainer="Emmanuel Bruno <emmanuel.bruno@univ-tln.fr>"
LABEL description="Java Hello World Application - multi-stage runtime"

WORKDIR /app

# Copy only the built jar from the build stage
COPY --from=stage-build /app/target/hello-world-*-SNAPSHOT.jar /app/app.jar
# Copy the libs directory (containing dependencies) if needed
COPY --from=stage-build /app/target/libs /app/libs

# Create a non-root user for security and ensure app files are owned by that user
RUN groupadd -r appuser && useradd -r -u 1001 -g appuser -m appuser \
&& chown -R appuser:appuser /app

USER appuser
ENV HOME=/home/appuser

# Configure Java options for container environment
ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0"
# Install generic entrypoint script
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh || true

# Use generic exec-form entrypoint
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
Loading