Hilla-Anwendung als Native Image für Deployment bauen
Eine Hilla-Anwendung, die über die Hilla-CLI erstellt wurde, kann via Maven (oder Gradle) für das Deployment als Native Image gebaut werden. Dazu ist ein JDK mit GraalVM-Unterstützung erforderlich. Eine Hilla-Anwendung, die als Native Image gebaut wurde, ist eine eigenständige ausführbare Datei, die ohne eine Java-Laufzeitumgebung ausgeführt werden kann. Im Vergleich zu einer Hilla-Anwendung, die als JAR-Datei gebaut wurde und ausgeführt wird, zeichnet sich eine Hilla-Anwendung, die als Native Image gebaut wurde und ausgeführt wird, i.d.R. durch eine geringere Startzeit und eine geringere Speichernutzung aus. Eine Hilla-Anwendung kann mit Unterstützung von GraalVM wie folgt als Native Image gebaut werden:
./mvnw clean package -Pproduction -Pnative native:compile
Das erstellte Native Image enthält alle erforderlichen Klassen, Abhängigkeiten und das Frontend-Bundle. Da eine Hilla-Anwendung ein klassisches Spring Boot-Projekt ist, enthält das erstellte Native Image auch einen integrierten Server, der die Hilla-Anwendung bereitstellt, sobald das Native Image ausgeführt wird.
Weitere Informationen zur Unterstützung von Native Images durch Spring Boot sind hier zu finden.
Hilla-Anwendung als Native Image ausführen
Die gebaute Hilla-Anwendung my-app
kann als Native Image folgendermaßen ausgeführt werden:
./target/my-app
Spring Boot-spezifische Konfigurationen können hierbei bei Bedarf als Umgebungsvariablen gesetzt bzw. übersteuert werden. So kann bspw. das Log-Level beim Start der Anwendung folgendermaßen angepasst werden:
export LOGGING_LEVEL_COM_VAADIN=DEBUG && ./target/my-app
Docker-Image für Hilla-Anwendung erstellen
Das Deployment von Anwendungen findet häufig auf Basis von Containern statt. Daher bietet es sich an für die Hilla-Anwendung ein Dockerfile
zu erstellen. Das Dockerfile
sollte im Basis-Verzeichnis der Hilla-Anwendung liegen und folgenden Inhalt haben:
FROM debian:bookworm-slim
WORKDIR /app
COPY target/*.so /app/
COPY target/my-app /app/my-app
EXPOSE 8080
CMD ["/app/my-app"]
Das Dockerfile
basiert auf einem abgespeckten Basis-Image, hier debian:bookworm-slim
. Das Native Image der Hilla-Anwendung wird zusammen mit den beim Bau erstellten Bibliotheken (.so
Dateien) in das Docker-Image kopiert. Die Bibliotheken stellen Abhängigkeiten bereit, die nicht Bestandteil des Native Image sind, aber von der Anwendung während der Ausführung benötigt werden. Des weiteren enthält das Dockerfile
die Konfiguration für den Port, über den die Anwendung Anfragen erhalten kann. Über CMD
wird festgelegt, dass das Native Image im Docker-Image beim Start des Containers ausgeführt werden soll.
Docker-Image lokal erstellen
Das Docker-Image kann lokal mit Docker oder Podman erstellt werden:
docker|podman build --tag my-app .
Der Befehl wird im Basis-Verzeichnis der Hilla-Anwendung ausgeführt. Der .
zeigt dabei auf das selbige Verzeichnis und das darin befindliche Dockerfile
.
Das Docker-Image ist anschließend lokal verfügbar und verwendbar.
Docker-Container lokal starten
Auf Basis des erstellten Docker-Images kann ein Docker-Container mit der Hilla-Anwendung gestartet werden:
docker|podman run -it --rm --publish 8080:8080 my-app
Dockerfile um Build-Stage erweitern
Das Bauen der Hilla-Anwendung als Native Image kann auch Bestandteil des Dockerfile
sein. Dafür wird das Dockerfile
in zwei Stages aufgeteilt:
# First stage: JDK with GraalVM and native image support
FROM ghcr.io/graalvm/native-image-community:21.0.2 AS build
WORKDIR /usr/src/app
# Copy project files
COPY . .
# Create native image
RUN ./mvnw clean package -Pproduction -Pnative native:compile
# Second stage: Lightweight debian-slim image
FROM debian:bookworm-slim
WORKDIR /app
# Copy the produced library files from the build stage
COPY --from=build /usr/src/app/target/*.so /app/
# Copy the native image from the build stage
COPY --from=build /usr/src/app/target/my-app /app/my-app
EXPOSE 8080
# Run the application
CMD ["/app/my-app"]
In der ersten Stage des Dockerfile
wird das Native Image erzeugt. Dazu werden die erforderlichen Dateien und Abhängigkeiten in das Docker-Image kopiert und anschließend die Hilla-Anwendung als Native Image gebaut. In der zweiten Stage wird das Docker-Image mit dem gebauten Native Image erstellt.
Hilla-Anwendung über fly.io deployen
Mit Hilfe der erstellen Dockerfile
kann eine Hilla-Anwendung sehr einfach über einen Dienst wie fly.io deployed werden. Die Registrierung bei fly.io ist kostenlos, erfordert jedoch die Angabe von Kreditkarten-Daten. Fly.io bietet einen kostenlosen Hobby-Plan, über den man kostenlos Anwendungen mit eingeschränkten Ressourcen deployen kann. Die Anwendung ist anschließend über https://<unique-app-identifier>.fly.dev
erreichbar. Die erforderliche Konfiguration von IP-Adressen, DNS, SSL-Zertifikat usw. übernimmt dabei fly.io.
Der Weg zum Deployment ist in https://fly.io/docs/hands-on/ ausführlich beschrieben.
Zunächst wird die flyctl
installiert. Unter macOS kann dies bspw. mit brew
erfolgen:
brew install flyctl
Anschließend wird ein neues Konto bei fly.io erstellt oder ein bestehendes Konto verknüpft:
fly auth signup
Vor dem ersten Deployment wird eine Deployment-Konfiguration für fly.io erstellt. Dies erfolgt über den Befehl:
fly launch --dockerfile Dockerfile
Die vorgeschlagenen Einstellungen können übernommen werden. Eine .dockerignore
sollte zunächst nicht erstellt werden, da dies ansonsten das Kopieren des Native Image und der Bibliotheken vorerst verhindern würde.
Die Deployment-Konfiguration legt die flyctl
in der Datei fly.toml
ab. Diese Datei kann bei Bedarf editiert werden.
app = '<unique-app-identifier>'
primary_region = 'ams'
[build]
dockerfile = 'Dockerfile'
[http_service]
internal_port = 8080
force_https = true
auto_stop_machines = true
auto_start_machines = true
min_machines_running = 0
processes = ['app']
[[vm]]
size = 'shared-cpu-1x'
memory = '256mb'
Das eigentliche Deployment der Hilla-Anwendung erfolgt dann mittels:
fly deploy
Das Dockerfile
, das Native Image und die Bibliotheken (.so
Dateien) werden auf einen Build-Server von fly.io hochgeladen. Das Erstellen des Docker-Images passiert bei fly.io. Fly.io legt das erstellte Docker-Image in einer eigenen Registry ab und startet anschließend die Hilla-Anwendung als Docker-Container. Nach erfolgreichem Start kann die Anwendung im Browser über https://<unique-app-identifier>.fly.dev
aufgerufen werden.
Wenn man das oben gezeigte Dockerfile
mit den zwei Stages verwenden möchte, muss man beachten, dass dafür die verfügbaren Ressourcen des Hobby-Plans voraussichtlich nicht ausreichend sind und der Bau-Vorgang des Docker-Images bei fly.io deshalb fehlschlagen kann.
Hilla-Anwendung aktualisieren
Ergeben sich Änderungen am Code der Hilla-Anwendung muss diese erneut gebaut und anschließend erneut deployed werden:
./mvnw clean package -Pproduction -Pnative native:compile
fly deploy
Erfolgt das Bauen des Native Image über eine eigene Stage im Dockerfile
ist nur die Ausführung von fly deploy
erforderlich.
Fazit
Eine Hilla-Anwendung kann mit Unterstützung von GraalVM sehr einfach für das Deployment als Native Image gebaut werden. Mit Hilfe eines passenden Dockerfile
steht dem Deployment über einen Dienst wie fly.io nichts im Weg. Die offizielle Dokumentation von Hilla bietet weitere Anleitungen für das Deployment von Hilla-Anwendungen, z.B. zu AWS, GCP, Azure oder Heroku.
Im Hilla-Blog findet sich darüber hinaus auch ein Blog-Post von Marcus Hellberg zu diesem Thema: Deploying a Spring Boot app as a native GraalVM image with Docker.
Eine Beispiel-Hilla-Anwendung, die als Native Image gebaut und über fly.io deployed wurde, kann über https://vaadin-create-countdown.fly.dev/ aufgerufen werden. Den zugehörigen Quellcode findet man bei GitHub.