Skip to content

Commit e1255fc

Browse files
authored
Merge pull request #41 from graalvm/25-upgrade
Upgrade Spring and Micronaut webserver workshops to Java 25 and Oracle GraalVM 25
2 parents 6d35ff2 + 7eeaf64 commit e1255fc

25 files changed

+224
-49
lines changed

native-image/micronaut-webserver/Dockerfile.distroless-base.mostly

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ RUN ./mvnw --no-transfer-progress clean package -Dpackaging=native-image -Pmostl
88
# RUN ./mvnw --no-transfer-progress clean package -Dpackaging=native-image -DbuildArgs="--static-nolibc,-Os,-o target/webserver.mostly-static"
99

1010
# Distroless Base - provides glibc
11-
FROM gcr.io/distroless/base-debian12
11+
FROM gcr.io/distroless/base-debian13
1212
COPY --from=nativebuild /webserver/target/webserver.mostly-static /
1313
EXPOSE 8000
1414
ENTRYPOINT ["/webserver.mostly-static", "-b", "0.0.0.0", "-d", "/web"]

native-image/micronaut-webserver/Dockerfile.distroless-java-base-jar

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ WORKDIR /webserver
44
RUN ./mvnw --no-transfer-progress clean package
55

66
# Distoless Java 21 (Debian)
7-
FROM gcr.io/distroless/java21-debian12
7+
FROM gcr.io/distroless/java25-debian13
88
COPY --from=build /webserver/target/webserver-0.1.jar webserver-0.1.jar
99
EXPOSE 8080
1010
ENTRYPOINT ["java", "-jar", "webserver-0.1.jar"]

native-image/micronaut-webserver/Dockerfile.distroless-java-base.dynamic

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ WORKDIR /webserver
55
RUN ./mvnw --no-transfer-progress clean package -Dpackaging=native-image
66

77
# Distroless Java Base - provides glibc and other libraries needed by the JDK
8-
FROM gcr.io/distroless/java-base-debian12
8+
FROM gcr.io/distroless/java-base-debian13
99
COPY --from=nativebuild /webserver/target/webserver /
1010
EXPOSE 8000
1111
ENTRYPOINT ["/webserver", "-b", "0.0.0.0", "-d", "/web"]

native-image/micronaut-webserver/Dockerfile.distroless-java-base.dynamic-optimized

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ RUN ./mvnw --no-transfer-progress clean package -Dpackaging=native-image -Pdynam
88
# RUN ./mvnw --no-transfer-progress clean package -Dpackaging=native-image -DbuildArgs="-Os,-o target/webserver.dynamic-optimized"
99

1010
# Distroless Java Base-provides glibc and other libraries needed by the JDK
11-
FROM gcr.io/distroless/java-base-debian12
11+
FROM gcr.io/distroless/java-base-debian13
1212
COPY --from=nativebuild /webserver/target/webserver.dynamic-optimized /
1313
EXPOSE 8000
1414
ENTRYPOINT ["/webserver.dynamic-optimized", "-b", "0.0.0.0", "-d", "/web"]
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
FROM container-registry.oracle.com/graalvm/native-image:25 AS nativebuild
2+
COPY . /webserver
3+
WORKDIR /webserver
4+
# Build a dynamically linked native image with optimization for size
5+
RUN ./mvnw --no-transfer-progress clean package -Dpackaging=native-image -Pdynamic-skipflow-optimized
6+
7+
# Alternative way to pass Native Image build options with `-DbuildArgs` without using Maven profiles:
8+
# RUN ./mvnw --no-transfer-progress clean package -Dpackaging=native-image -DbuildArgs="-Os,-H:+UnlockExperimentalVMOptions,-H:+TrackPrimitiveValues,-H:+UsePredicates,-o target/webserver.dynamic-skipflow"
9+
10+
# Distroless Java Base-provides glibc and other libraries needed by the JDK
11+
FROM gcr.io/distroless/java-base-debian13
12+
COPY --from=nativebuild /webserver/target/webserver.dynamic-skipflow /
13+
EXPOSE 8000
14+
ENTRYPOINT ["/webserver.dynamic-skipflow", "-b", "0.0.0.0", "-d", "/web"]

native-image/micronaut-webserver/Dockerfile.distroless-java-base.jlink

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ RUN CP=$(cat cp.txt) && \
1818
--output jlink-jre
1919

2020
# Distroless Java Base-provides glibc and other libraries needed by the JDK
21-
FROM gcr.io/distroless/java-base-debian12
21+
FROM gcr.io/distroless/java-base-debian13
2222
COPY --from=build /webserver/target/webserver-0.1.jar webserver-0.1.jar
2323
COPY --from=build /webserver/jlink-jre jlink-jre
2424
EXPOSE 8080

native-image/micronaut-webserver/Dockerfile.eclipse-temurin-jar renamed to native-image/micronaut-webserver/Dockerfile.eclispe-temurin-jar

File renamed without changes.

native-image/micronaut-webserver/README.md

Lines changed: 91 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ In this workshop you will:
2828
* x86 Linux
2929
* `musl` toolchain
3030
* Container runtime such as [Docker](https://www.docker.com/gettingstarted/), or [Rancher Desktop](https://docs.rancherdesktop.io/getting-started/installation/) installed and running.
31-
* [GraalVM 25](https://www.graalvm.org/downloads/). We recommend using [SDKMAN!](https://sdkman.io/). (For other download options, see [GraalVM Downloads](https://www.graalvm.org/downloads/).)
31+
* [Oracle GraalVM 25](https://www.graalvm.org/downloads/). We recommend using [SDKMAN!](https://sdkman.io/). (For other download options, see [GraalVM Downloads](https://www.graalvm.org/downloads/).)
3232
```bash
3333
sdk install java 25-graal
3434
```
@@ -53,7 +53,7 @@ In this workshop you will:
5353
./build-jar-eclipse-temurin.sh
5454
```
5555
Once the script finishes, a container image _eclipse-temurin-jar_ should be available.
56-
Check its size. It should be **472MB**.
56+
Check its size. It should be **472MB**.
5757
```bash
5858
docker images
5959
```
@@ -67,7 +67,7 @@ It requires a container image with a JDK and runtime libraries.
6767

6868
### Explanation
6969

70-
The Dockerfile provided for this step pulls [container-registry.oracle.com/graalvm/jdk:24](https://docs.oracle.com/en/graalvm/jdk/24/docs/getting-started/container-images/) for the builder, and then `gcr.io/distroless/java21-debian12` for the runtime.
70+
The Dockerfile provided for this step pulls [container-registry.oracle.com/graalvm/jdk:25](https://docs.oracle.com/en/graalvm/jdk/25/docs/getting-started/container-images/) for the builder, and then `gcr.io/distroless/java25-debian13` for the runtime.
7171
The entrypoint for this image is equivalent to `java -jar`, so only a path to a JAR file is specified in `CMD`.
7272

7373
### Action
@@ -112,7 +112,7 @@ See how much reduction in size you can gain.
112112
Introduced in Java 11, it provides a way to make applications more space efficient and cloud-friendly.
113113

114114
The script _build-jlink.sh_ that runs `docker build` using the _Dockerfile.distroless-java-base.jlink_.
115-
The Dockerfile contains two stages: first it generates a `jlink` custom runtime on a full JDK (`container-registry.oracle.com/graalvm/jdk:24`); then copies the runtime image folder along with static assets into a distroless Java base image, and sets the entrypoint.
115+
The Dockerfile contains two stages: first it generates a `jlink` custom runtime on a full JDK (`container-registry.oracle.com/graalvm/jdk:25`); then copies the runtime image folder along with static assets into a distroless Java base image, and sets the entrypoint.
116116
Distroless Java base image provides `glibc` and other libraries needed by the JDK, **but not a full-blown JDK**.
117117

118118
The application does not have to be modular, but you need to figure out which modules the application depends on to be able to `jlink` it.
@@ -238,7 +238,7 @@ In this step, you will build a fully dynamically linked native image **with the
238238
239239
GraalVM Native Image provides the option `-Os` which optimizes the resulting native image for file size.
240240
`-Os` enables `-O2` optimizations except those that can increase code or executable size significantly.
241-
Learn more about different optimization levels in the [Native Image documentation](https://www.graalvm.org/latest/reference-manual/native-image/optimizations-and-performance/#optimization-levels).
241+
Learn more about different optimization levels in the [Native Image documentation](https://www.graalvm.org/jdk25/reference-manual/native-image/optimizations-and-performance/#optimization-levels).
242242
243243
To configure the Native Image build and have more manual control over the process, GraalVM provides the [Native Build Tools](https://graalvm.github.io/native-build-tools/latest/index.html): Maven and Gradle plugins for building native images.
244244
@@ -315,7 +315,85 @@ No Java Runtime Environment (JRE) is required.
315315
The size of the container came down from **132MB** to **102MB**.
316316
The executable size decreased by **24MB** (from 86MB to 62MB) just by applying the file size optimization - with no change in behavior or startup time!
317317
318-
## **STEP 5**: Build a Size-Optimized Mostly Static Native Image and Run Inside a Container
318+
319+
## **STEP 5**: (Optional) Build a Size-Optimized Native Image with SkipFlow and Run Inside a Container
320+
321+
In this step, you will build another fully dynamically linked native image but with the **SkipFlow** and **file size** optimizations on. Then you run it inside a container.
322+
323+
### Explanation
324+
325+
As of Oracle GraalVM 25, more performance improvements are enabled by default.
326+
One of which is [SkipFlow](https://www.graalvm.org/release-notes/JDK_25/#native-image)-an extension to the Native Image static analysis that tracks primitive values and evaluates branching conditions dynamically during the process.
327+
328+
Note: The feature is enabled by default. With the previous releases, it could be controlled using these host options: `-H:+TrackPrimitiveValues` and `-H:+UsePredicates`.
329+
330+
For this, we have added a separate Maven profile with a different name for the generated native executable:
331+
```xml
332+
<profile>
333+
<id>dynamic-skipflow-optimized</id>
334+
<build>
335+
<plugins>
336+
<plugin>
337+
<groupId>org.graalvm.buildtools</groupId>
338+
<artifactId>native-maven-plugin</artifactId>
339+
<configuration>
340+
<imageName>webserver.dynamic-skipflow</imageName>
341+
<buildArgs>
342+
<buildArg>-Os</buildArg>
343+
<buildArg>-H:+UnlockExperimentalVMOptions</buildArg>
344+
<buildArg>-H:+TrackPrimitiveValues</buildArg>
345+
<buildArg>-H:+UsePredicates</buildArg>
346+
</buildArgs>
347+
</configuration>
348+
</plugin>
349+
</plugins>
350+
</build>
351+
</profile>
352+
```
353+
354+
> An alternative way to pass build options to Native Image, without creating Maven profiles, is by using `-DbuildArgs`:
355+
```bash
356+
./mvnw package -Dpackaging=native-image -DbuildArgs="-Os,-H:+UnlockExperimentalVMOptions,-H:+TrackPrimitiveValues,-H:+UsePredicates,-o target/webserver.dynamic-skipflow"
357+
```
358+
359+
The Dockerfile for this step, _Dockerfile.distroless-java-base.dynamic-skipflow_, is pretty much the same as before: running a native image build inside the builder container, and then copying it over to a distroless base container with just enough to run the application.
360+
No Java Runtime Environment (JRE) is required.
361+
362+
### Action
363+
364+
1. Run the script to build a size-optimized native executable and package it into a container:
365+
```bash
366+
./build-dynamic-image-skipflow.sh
367+
```
368+
369+
2. Once the build completes, a container image _distroless-java-base.dynamic-optimized_ should be available. Run it, mapping the ports:
370+
```bash
371+
docker run --rm -p8080:8080 webserver:distroless-java-base.dynamic-skipflow
372+
```
373+
The application is running from the native image inside a container.
374+
The startup time has not changed.
375+
376+
3. Open a browser and navigate to [localhost:8080/](http://localhost:8080/). You see the GraalVM documentation pages served.
377+
378+
4. Return to the terminal and stop the running container by clicking CTRL+C.
379+
380+
5. Check the size of this container image:
381+
```bash
382+
docker images
383+
```
384+
The expected output is:
385+
```bash
386+
REPOSITORY TAG IMAGE ID CREATED SIZE
387+
webserver distroless-java-base.dynamic-skipflow 6caada87f616 8 minutes ago 101MB
388+
webserver distroless-java-base.dynamic-optimized 5e16a58b1649 10 minutes ago 102MB
389+
webserver distroless-java-base.dynamic d7c449b9373d 12 minutes ago 132MB
390+
webserver distroless-java-base.jlink dde1eb772aa5 15 minutes ago 167MB
391+
webserver distroless-java-base.jar e285476a8266 32 minutes ago 216MB
392+
webserver eclispe-temurin-jar f6eef8d2aa40 33 minutes ago 472MB
393+
```
394+
The gain is tiny: the container size reduced only by 1MB, but depending on the application, **SkipFlow can provide up to a 4% reduction in binary size without any additional impact on build time**.
395+
396+
## **STEP 6**: Build a Size-Optimized Mostly Static Native Image and Run Inside a Container
319397
320398
In this step, you will build a **mostly static** native image, with the file size optimization on, and then package it into a container image that provides `glibc`, and run.
321399
@@ -555,12 +633,13 @@ Note that the website static pages add 44MB to the container images size. Static
555633
556634
| Container | Size of a build artefact <br> (JAR, Jlink runtime, native executable) | Base image | Container |
557635
|----------------------------------------|-----------------------------------------------------------------------|------------|-----------|
558-
| eclipse-temurin-jar | webserver-0.1.jar **24MB** | eclipse-temurin:25 201MB | 472MB |
559-
| distroless-java-base.jar | webserver-0.1.jar **24MB** | java21-debian12 192MB | 216MB |
560-
| distroless-java-base.jlink | jlink-jre custom runtime **68MB** | java-base-debian12 128MB | 167MB |
561-
| distroless-java-base.dynamic | webserver.dynamic **86MB** | java-base-debian12 128MB | 132MB |
562-
| distroless-java-base.dynamic-optimized | webserver.dynamic-optimized **62MB** | java-base-debian12 128MB | 102MB |
563-
| distroless-base.mostly-static | webserver.mostly-static **62MB** | base-debian12 48.3MB | 89.7MB |
636+
| eclispe-temurin-jar | webserver-0.1.jar **24MB** | eclipse-temurin:25 201MB | 472MB |
637+
| distroless-java-base.jar | webserver-0.1.jar **24MB** | java25-debian13 192MB | 216MB |
638+
| distroless-java-base.jlink | jlink-jre custom runtime **68MB** | java-base-debian13 128MB | 167MB |
639+
| distroless-java-base.dynamic | webserver.dynamic **86MB** | java-base-debian13 128MB | 132MB |
640+
| distroless-java-base.dynamic-optimized | webserver.dynamic-optimized **62MB** | java-base-debian13 128MB | 102MB |
641+
| distroless-java-base.dynamic-skipflow | webserver.dynamic-skipflow **61MB** | java-base-debian13 128MB | 101MB |
642+
| distroless-base.mostly-static | webserver.mostly-static **62MB** | base-debian13 48.3MB | 89.7MB |
564643
| scratch.static | webserver.static **62MB** | scratch 2MB | 69.2MB |
565644
| scratch.static-upx | webserver.scratch.static-upx **20MB** | scratch 2MB | 22.3MB |
566645

native-image/micronaut-webserver/build-all.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/bin/sh
22

3-
./build-jar-eclipse-temurin.sh
43
./build-jar-java-base.sh
4+
./build-jar-eclipse-temurin.sh
55
./build-jlink.sh
66
./build-dynamic-image.sh
77
./build-dynamic-image-optimized.sh
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
#!/bin/sh
22

3-
# Eclipse Temurin Java 25
4-
docker build --no-cache . -f Dockerfile.eclipse-temurin-jar -t webserver:eclipse-temurin-jar
3+
# Eclipse-temurin:25
4+
docker build --no-cache . -f Dockerfile.eclispe-temurin-jar -t webserver:eclipse-temurin-jar

0 commit comments

Comments
 (0)