1+ # ============================
2+ # 1. Build Stage (Java 25)
3+ # ============================
4+ FROM registry.access.redhat.com/ubi9/openjdk-25:latest AS build
5+ WORKDIR /app
6+
7+ # Maven Caching & Build (wie gehabt)
8+ COPY pom.xml .
9+ RUN --mount=type=cache,target=/root/.m2 mvn -q -DskipTests dependency:go-offline
10+ COPY src ./src
11+ RUN --mount=type=cache,target=/root/.m2 mvn -q -DskipTests compile spring-boot:process-aot package
12+
13+ # JAR extrahieren für Layering
14+ RUN cp target/javahttpclient-0.0.1-SNAPSHOT.jar app.jar && \
15+ java -Djarmode=tools -jar app.jar extract --layers --launcher --destination extracted
16+
17+ # ============================
18+ # 2. JRE Creation Stage (NEU)
19+ # ============================
20+
21+ # WICHTIG: Umschalten auf root, um Pakete zu installieren
22+ USER root
23+ # Installiere die JMODS, die für jlink zwingend erforderlich sind. Red Hat trennt das, um Platz zu sparen.
24+ # Wenn jlink anweist, Debug-Symbole zu entfernen (--strip-debug), sucht das Tool im Hintergrund
25+ # nach dem System-Befehl objcopy (ein Teil der binutils), was natürlich nicht als notwendig erachtet wurde.
26+ RUN microdnf install -y java-25-openjdk-jmods binutils && \
27+ microdnf clean all
28+
29+ # Wir analysieren die extrahierten Layer, um ein minimales JRE zu bauen
30+ RUN jdeps \
31+ --ignore-missing-deps \
32+ --print-module-deps \
33+ --multi-release 25 \
34+ --recursive \
35+ --class-path "extracted/dependencies/*:extracted/snapshot-dependencies/*" \
36+ extracted/application/BOOT-INF/classes > modules.txt
37+
38+ # Erzeuge das Custom JRE
39+ # --compress zip-9 ist der moderne Ersatz für --compress=2
40+ # NoClassDefFoundError: java.beans.PropertyEditorSupport -> java.desktop
41+ # jmx, metrics -> java.management
42+ # load jar files (swagger) -> jdk.zipfs
43+ # needs jlink -> java.instrument
44+ # jpa needs java.sql.Date -> java.sql
45+ # jetty jndi config -> java.naming
46+ # jdk25 warnings -> sun.misc.Unsafe, das von Objenesis/CGLIB -> jdk.unsupported
47+ RUN $JAVA_HOME/bin/jlink \
48+ --add-modules $(cat modules.txt),jdk.crypto.ec,jdk.charsets,java.desktop,java.management,jdk.zipfs,java.instrument,java.sql,java.naming,jdk.unsupported \
49+ --strip-debug \
50+ --no-man-pages \
51+ --no-header-files \
52+ --compress zip-9 \
53+ --output /custom-jre
54+
55+ # Erzeuge die Klassenliste für AppCDS
56+ RUN /custom-jre/bin/java -Djarmode=tools -jar app.jar extract --layers --destination cds-extraction
57+
58+ # Erzeugt das Basis-Archiv für das Custom JRE
59+ RUN /custom-jre/bin/java -Xshare:dump
60+
61+ # CDS Archiv generieren
62+ # 1. Liste der Klassen erstellen (Training)
63+ # WICHTIG: Wir nutzen /custom-jre/bin/java, damit das Archiv zum JRE passt!
64+ # spring.context.exit weist Spring Boot an, den Application Context aufzubauen (damit alle Klassen geladen werden),
65+ # aber die App sofort danach sauber zu beenden
66+ # Training gegen die entpackten Layer (muss identisch zum Start sein!)
67+ RUN /custom-jre/bin/java -XX:ArchiveClassesAtExit=app.jsa \
68+ -Dspring.context.exit=onRefresh \
69+ -Dspring.aot.enabled=true \
70+ -cp "extracted/dependencies/*:extracted/snapshot-dependencies/*:extracted/application/" \
71+ org.springframework.boot.loader.launch.JarLauncher || [ -f app.jsa ]
72+ # Hinweis: Das "|| true" ist wichtig, da die App beim Training mangels DB/Config evtl. abbricht,
73+ # aber das Archiv trotzdem schreibt.
74+
75+ # ============================
76+ # 3. Runtime Stage (Minimales OS)
77+ # ============================
78+ # Wir nutzen hier ubi9-minimal statt der vollen Java-Runtime
79+ FROM registry.access.redhat.com/ubi9/ubi-minimal:latest
80+
81+ LABEL org.opencontainers.image.title="Java http client with Custom JRE" \
82+ org.opencontainers.image.version="0.0.1-SNAPSHOT"
83+
84+ WORKDIR /app
85+
86+ # Kopiere das Custom JRE aus der Build-Stage
87+ COPY --from=build /custom-jre /opt/jre
88+ ENV PATH="/opt/jre/bin:$PATH"
89+
90+ # User-Setup (UBI-Minimal braucht oft manuelles ID-Handling)
91+ # curl-minial for healthcheck
92+ RUN microdnf install -y shadow-utils curl-minimal && \
93+ groupadd -r appgroup -g 185 && \
94+ useradd -r -u 185 -g appgroup -d /app -s /sbin/nologin appuser && \
95+ mkdir -p /app/config /app/data && \
96+ chown -R 185:185 /app && \
97+ microdnf clean all
98+
99+ USER 185
100+
101+ # Layer kopieren (wie gehabt)
102+ COPY --from=build --chown=185:185 /app/extracted/dependencies/ ./
103+ COPY --from=build --chown=185:185 /app/extracted/spring-boot-loader/ ./
104+ COPY --from=build --chown=185:185 /app/extracted/snapshot-dependencies/ ./
105+ COPY --from=build --chown=185:185 /app/extracted/application/ ./
106+ COPY --from=build --chown=185:185 /app/app.jsa /app/app.jsa
107+ COPY --chown=185:185 containerconfig/application.properties /app/config/application.properties
108+ COPY --chown=185:185 entrypoint.sh /app/entrypoint.sh
109+
110+ EXPOSE 8080
111+ HEALTHCHECK --interval=30s --timeout=3s \
112+ CMD curl -f http://localhost:8080/actuator/health || exit 1
113+
114+ ENTRYPOINT ["/app/entrypoint.sh"]
0 commit comments