Squashed commit of the following:

commit 0d624137c2557c6eeb87020749e4977b821c2b5c
Author: Adrien <adrien.cesaro@proton.me>
Date:   Thu Apr 9 11:55:22 2026 +0200

    backend native image setup
This commit is contained in:
Adrien
2026-04-09 12:05:02 +02:00
parent aee6a9dfba
commit d8bcdce879
17 changed files with 1285 additions and 6 deletions
+24
View File
@@ -0,0 +1,24 @@
# Java build artifacts
target/
*.class
*.jar
# Git
.git/
.gitignore
# Editor
.vscode/
.idea/
*.iml
# OS
.DS_Store
Thumbs.db
# Logs
*.log
# Environment
.env
.env.*
+26
View File
@@ -0,0 +1,26 @@
# ---- Pull Maven from its official image (avoids microdnf under QEMU) ----
FROM maven:3.9.9-eclipse-temurin-21 AS maven-dist
# ---- Build stage: GraalVM 25 + Maven ----
ARG TARGETPLATFORM=linux/arm64
FROM --platform=$TARGETPLATFORM ghcr.io/graalvm/native-image-community:25 AS build
# Copy Maven from the official Maven image — no package installation needed
COPY --from=maven-dist /usr/share/maven /opt/maven
ENV PATH="/opt/maven/bin:$PATH"
WORKDIR /app
# Cache dependency resolution separately from source compilation
COPY pom.xml .
RUN mvn -Pnative dependency:resolve dependency:resolve-plugins -q
# Build native executable
COPY src ./src
RUN mvn -Pnative package -DskipTests
# ---- Runtime stage: minimal ARM64 distroless ----
FROM --platform=$TARGETPLATFORM gcr.io/distroless/base-nossl-debian12
COPY --from=build /app/target/ai-teacher-backend /app/ai-teacher-backend
EXPOSE 8080
ENTRYPOINT ["/app/ai-teacher-backend"]
+85
View File
@@ -143,12 +143,97 @@
<build>
<plugins>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!-- Jib — package native executable (or fat-jar) into Docker image -->
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>3.4.5</version>
<configuration>
<from>
<!-- distroless glibc base — matches GraalVM native binary ABI on Linux x86_64 -->
<image>gcr.io/distroless/base-nossl-debian12</image>
</from>
<to>
<!-- override at build time: -Djib.to.image=registry/org/image:tag -->
<image>ai-teacher-backend</image>
</to>
<container>
<ports>
<port>8080</port>
</ports>
</container>
<pluginExtensions>
<pluginExtension>
<implementation>
com.google.cloud.tools.jib.maven.extension.nativeimage.JibNativeImageExtension
</implementation>
</pluginExtension>
</pluginExtensions>
</configuration>
<dependencies>
<dependency>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-native-image-extension-maven</artifactId>
<version>0.1.0</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>native</id>
<build>
<plugins>
<!-- GraalVM native-image compilation -->
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<version>1.0.0</version>
<executions>
<execution>
<id>add-reachability-metadata</id>
<goals>
<goal>add-reachability-metadata</goal>
</goals>
</execution>
<execution>
<id>compile</id>
<goals>
<goal>compile-no-fork</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
<configuration>
<imageName>ai-teacher-backend</imageName>
<buildArgs>
<buildArg>--initialize-at-build-time=org.slf4j,ch.qos.logback</buildArg>
<buildArg>-H:+ReportExceptionStackTraces</buildArg>
<buildArg>--gc=serial</buildArg>
<buildArg>-Os</buildArg>
<buildArg>-H:+RemoveUnusedSymbols</buildArg>
<buildArg>-H:-EnableLoggingFeature</buildArg>
<buildArg>-R:MaxHeapSize=128m</buildArg>
<buildArg>-R:MinHeapSize=32m</buildArg>
</buildArgs>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
@@ -1,11 +1,15 @@
package com.aiteacher;
import org.springframework.context.annotation.ImportRuntimeHints;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
import com.aiteacher.config.NativeHintsConfig;
@SpringBootApplication
@EnableAsync
@ImportRuntimeHints(NativeHintsConfig.class)
public class AiTeacherApplication {
public static void main(String[] args) {
@@ -0,0 +1,76 @@
package com.aiteacher.config;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.aot.hint.TypeReference;
/**
* GraalVM native-image runtime hints for third-party libraries that use reflection
* or classpath resource scanning not covered by Spring Boot's AOT processor.
*
* Registered via @ImportRuntimeHints on AiTeacherApplication.
*/
public class NativeHintsConfig implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
// PDFBox — font and encoding resources loaded via classpath scanning at runtime
hints.resources().registerPattern("org/apache/pdfbox/resources/*");
hints.resources().registerPattern("org/apache/pdfbox/resources/afm/*");
hints.resources().registerPattern("org/apache/pdfbox/resources/cmap/*");
hints.resources().registerPattern("org/apache/pdfbox/resources/glyphlist/*");
hints.resources().registerPattern("org/apache/pdfbox/resources/icc/*");
hints.resources().registerPattern("org/apache/pdfbox/resources/ttf/*");
hints.resources().registerPattern("org/apache/pdfbox/resources/version.properties");
// PDFBox — font encoding classes instantiated via reflection
hints.reflection().registerType(
org.apache.pdfbox.pdmodel.font.encoding.GlyphList.class,
MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,
MemberCategory.INVOKE_PUBLIC_METHODS
);
hints.reflection().registerType(
org.apache.pdfbox.pdmodel.font.encoding.WinAnsiEncoding.class,
MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS
);
hints.reflection().registerType(
org.apache.pdfbox.pdmodel.font.encoding.MacRomanEncoding.class,
MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS
);
hints.reflection().registerType(
org.apache.pdfbox.pdmodel.font.encoding.MacExpertEncoding.class,
MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS
);
hints.reflection().registerType(
org.apache.pdfbox.pdmodel.font.encoding.StandardEncoding.class,
MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS
);
// JPA / Hibernate — array types used in entity mappings
hints.reflection().registerType(java.util.UUID[].class, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS);
// JBoss Logging — message logger implementations generated by annotation processor.
// JBoss Logging uses reflection to look up the generated *_$logger class by name.
registerJBossLogger(hints, "org.hibernate.jpa.internal.JpaLogger_$logger");
registerJBossLogger(hints, "org.hibernate.internal.CoreMessageLogger_$logger");
registerJBossLogger(hints, "org.hibernate.internal.EntityManagerMessageLogger_$logger");
// AWS SDK v2 — HTTP client and SdkPojo serialization
hints.resources().registerPattern("software/amazon/awssdk/global/handlers/execution.interceptors");
hints.resources().registerPattern("software/amazon/awssdk/services/s3/execution.interceptors");
hints.resources().registerPattern("codegen-resources/s3/*");
hints.reflection().registerType(
software.amazon.awssdk.services.s3.S3Client.class,
MemberCategory.INVOKE_PUBLIC_METHODS
);
}
private void registerJBossLogger(RuntimeHints hints, String className) {
hints.reflection().registerType(
TypeReference.of(className),
MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,
MemberCategory.INVOKE_PUBLIC_METHODS
);
}
}
+3 -3
View File
@@ -27,7 +27,7 @@ spring:
index-type: HNSW
initialize-schema: false
openai:
api-key: ${OPENAI_API_KEY}
api-key: ${OPENAI_API_KEY:}
chat:
options:
model: gpt-4o-mini
@@ -62,8 +62,8 @@ app:
endpoint: https://s3.immich-ad.ovh
region: garage
bucket: ${S3_BUCKET:aiteacher}
access-key-id: ${S3_ACCESS_KEY_ID}
secret-access-key: ${S3_SECRET_ACCESS_KEY}
access-key-id: ${S3_ACCESS_KEY_ID:}
secret-access-key: ${S3_SECRET_ACCESS_KEY:}
min-image-size-px: 100
embedding:
batch-size: 20