In this lab you will get familiar with building the Micronaut application and using Docker to build the native image.
GraalVM Native Image allows you to ahead-of-time compile Java code to a standalone executable, called a native image. This executable includes the application, the libraries, the JDK and does not run on the Java VM, but includes necessary components like memory management and thread scheduling from a different virtual machine, called “Substrate VM”. Substrate VM is the name for the runtime components (like the deoptimizer, garbage collector, thread scheduling etc.). The resulting program has faster startup time and lower runtime memory overhead compared to a Java VM.
For more information you can refer to the official documentations https://www.graalvm.org/docs/reference-manual/aot-compilation/
Let's build the Micronaut application by going to the project directory (i.e. where the gradlew
files are)
$ ./gradlew assemble
The builds the ./build/libs/complete-0.1.jar
. As a standlone Micronaut application, you can run the application by using HotSpot JVM.
$ java -jar ./build/libs/complete-0.1.jar
Since our example includes Ruby
scripts, we need to modify the default Dockerfile
(generated by Micronaut) to include the Ruby engine by GraalVM.
Edit the Dockerfile
with your text editor (e.g. vim).
FROM oracle/graalvm-ce:19.0.0 as graalvm
COPY . /home/app/complete
WORKDIR /home/app/complete
RUN gu install native-image
#Install the GraalVM ruby engine
RUN gu install ruby
#specify the guest language
RUN native-image --no-server --language:ruby -cp build/libs/complete-*.jar
FROM frolvlad/alpine-glibc
EXPOSE 8080
COPY --from=graalvm /home/app/complete .
ENTRYPOINT ["./complete"]
As we are accessing the compute instance with SSH, we will run the docker build as a back-end process and pipe the output into a log file. We can then tail
the log file to check the progress.
$ sudo ./docker-build.sh >> log.out &
[1] 26493
$ sudo tail -f log.out
The building process will takes some time as it will process all the classes and dependencies of the application, including those from the JDK. It statically analyses these classes to determine which classes and methods are reachable and used during application execution. Then it passes all this reachable code as the input to the GraalVM compiler which ahead-of-time compiles it to the native binary.
Note: In case the SSH connection got disconnected due to whatever reason, you can safely reconnect and check the progress again by doing a tail
on the log file.
After the docker image is successfully built, it should show up in the list of Docker images locally.
$ sudo docker images -a
We can now run the application by running the Docker container.
$ sudo docker run -p 8080:8080 complete
After the server is started, you can open another terminal and access the service.
$ curl localhost:8080/meetup/random
{"name":"Autonomous Data Warehouse"}
$ curl localhost:8080/abs/java/-99
running abs in Java -> 99
$ curl localhost:8080/abs/ruby/-99
running abs in Ruby -> 99
Open another terminal and stop the running container.
$ sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
31f17c304311 complete "./complete" 1 hours ago Up 1 hours 0.0.0.0:8080->8080/tcp zealous_saha
$ sudo docker stop 31f17c304311
31f17c304311