Statically linked Java images are a superb idea; what's not to love about super small, super fast images that have a low attack surface. However, the reality is far from it. This post is describing some of the fun frustration I've had trying to get something rather basic working: namely Cloud Logging.
UnknownHostException
The first problem is statically linked Graal Native Images cannot do some things without libc
set, in this case they can't perform a DNS lookup which results in a java.net.UnknownHostException
. I'm not going to dwell on how stupid this is and just move on. There are rough instructions available on the Graal site to use musl
for this but a quick Google came up with something from, erm, Google Cloud. I bodged this into Helidon and... it didn't build:
#11 274.5 [WARNING] Error: Detected a direct/mapped ByteBuffer in the image heap. A direct ByteBuffer has a pointer to unmanaged C memory, and C memory from the image generator is not available at image runtime.A mapped ByteBuffer references a file descriptor, which is no longer open and mapped at run time. To see how this object got instantiated use --trace-object-instantiation=java.nio.DirectByteBuffer. The object was probably created by a class initializer and is reachable from a static field. You can request class initialization at image runtime by using the option --initialize-at-run-time=<class-name>. Or you can write your own initialization methods and call them explicitly from your main entry point.
#11 274.5 [WARNING] Trace: Object was reached by
#11 274.5 [WARNING] reading field io.grpc.netty.shaded.io.netty.buffer.PoolChunk.memory of
#11 274.5 [WARNING] constant io.grpc.netty.shaded.io.netty.buffer.PoolChunk@42d125b0 reached by
...
Googling
The build problem was due to the embedded Netty within the gRPC libraries. Fortunately the fix for this was pretty easy: Google have their own library for it:
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-graalvm-support</artifactId>
<version>0.3.0</version>
</dependency>
With this added it built and ran, but now it couldn't find any GCP config:
java.lang.IllegalArgumentException: A project ID is required for this service but could not be determined from the builder or the environment. Please set a project ID using the builder.
at com.google.common.base.Preconditions.checkArgument(Preconditions.java:142)
at com.google.cloud.ServiceOptions.<init>(ServiceOptions.java:304)
Confuddling
Normally, I would have just mounted the volume into the users home, but it appears Graal doesn't have value for user.home
. I've not digged into this to confirm it, so thought I'd just work around it:
docker run --rm -it -p 8080:8080 `
-e CLOUDSDK_CONFIG=/gcloud `
-v $Env:APPDATA/gcloud/:/gcloud:ro `
helidon-quickstart-mp:native
Booom! It ran up without any errors.
Alas, there was one final hiccup which was answered by microsoft/WSL#5324. In a nutshell, if you let Windows go to sleep then the clock in WSL2 drifts. That meant I couldn't find any of my log messages as they were timestamped incorrectly. With WSL restarted we got some logs in Cloud Logging. Hurrah!
The resulting image is a somewhat portly 116MB which is 24MB larger than last time and only 11MB off Alpine JLink. I don't think could be explained by the extra libraries but something to worry about another day. However, it starts in around 160ms with -m 32m
set on the docker container which can't be sniffed at. Regardless of how compelling Graal is, there are lots of bear traps to be aware of. I hope this improves soon.
Now for metrics and tracing...
Top comments (0)