I’ve just started looking at Micronaut and GraalVM Native. As a long time Java developer and architect, I’d love to be able to leverage my Java expertise, in a Serverless architecture. While Lambda’s do support Java, the cold start time of the JVM is pretty bad, especially if you add on a introspective framework like Spring.

So far it looks like Micronaut is a great tool for my goals, and when you compile it down to a native executable using the GraalVM tool chain, it gets even better! I haven’t gotten too far into this yet, so I have not tested any complex applications or systems yet, but so far things are looking good.

However, I have run into some issues getting this all working correctly on my Apple Silicon M1 Max based MacBookPro. So I wanted to share what I’ve needed to do to get things working correctly.

First, I’m assuming you have installed GraalVM and its tooling, and have it setup correctly in your JAVA_HOME and PATHs (I’m using aliases in my .zshrc file to quickly switch between Graal and standard JDKs).

The first real issue I ran into was a java.lang.UnsatisfiedLinkError related to JNA. I was able to get past that by upgrading the micronaut application version from 3.5.3 (as generated by Micronaut Launch) to 3.6.0 (current latest). That seems to load in a newer version of JNA that supports the M1 chipset.

Then I got “Could not initialize class com.github.dockerjava.httpclient5.UnixDomainSocket” errors while trying to run the native build. This is due to an issue in the docker-java plugin, and there’s a lot of back and forth about who’s responsible and generally seems like it won’t be fixed properly until Micronaut 4 is released. However, by adding this to the top of my build.gradle file, I was able to force in a newer version of the docker-java plugin:

buildscript {
dependencies {
classpath("com.github.docker-java:docker-java:3.2.13")
classpath("com.github.docker-java:docker-java-transport-httpclient5:3.2.13")
}
}

At this point I am able to run locally, and build Native images and zips.
 

gradle buildNativeLambda

Will build a native lambda custom runtime zip file located under the build/libs/ directory of your project.

That zip file can be uploaded to AWS for your Lambda, using “Custom runtime on Amazon Linux 2”, and setting the Handler to: “io.micronaut.function.aws.proxy.MicronautLambdaHandler”.

When you create the Lambda on the AWS side, you need to make sure you set the Architecture to “arm64”. Otherwise you’ll get an error like this: “/var/task/bootstrap: line 3: ./func: cannot execute binary file”.

 

Also, as a side-note, if you want to be able to easily run and test your code locally, make sure you pick “Micronaut Application” instead of “Function Application for Serverless” in your Micronaut project creation (cli or Launch). Serverless functions can’t be easily run locally in this case.

I hope that helps! Let me know if you have any questions or tips!

 

 

Referenced Issues:

https://github.com/testcontainers/testcontainers-java/issues/3610
https://github.com/docker-java/docker-java/issues/1779
https://github.com/bmuschko/gradle-docker-plugin/issues/1035
https://github.com/micronaut-projects/micronaut-gradle-plugin/issues/363
https://github.com/bmuschko/gradle-docker-plugin/issues/1070