Modern Java in the Cloud, Part 4: Let’s Go!
In the previous parts (1, 2, 3), we guided you through how to run Kotlin with your favourite Jakarta EE runtime. Luckily, the exact same procedure goes for MicroProfile — and, as I mentioned in the first post in the series, most runtimes implement both Jakarta EE and MicroProfile.
We’ll now glue that together with the other learnings from this series, so we’ll set up a Dockerfile, a build process and eventually run it serverless with Quarkus native.
Your first step would be to create a skeleton with the Quarkus starter, and then start writing your Kotlin code. Quarkus supports Kotlin out of the box, so you might not need to touch your POM file at all. Run mvnd quarkus:dev and see that your application starts without issues.
Then you’re ready to containerize it. Start off by using the Dockerfile provided with the starter. If you are going to run in JVM mode, and build your code with for instance GitHub Actions, then you probably won’t have to change anything here.
If you intend to run Quarkus native though, my experience is that the provided Quarkus native Dockerfile is not suitable for our use case. I want a Dockerfile that uses the layer mechanism and that ends up with a minimal container out in production.
For that, I’ve ended up with a Dockerfile which starts off from a Maven base image, adds the POM file and downloads my dependencies. Then I add my application code and compile it, and in the next step, I switch over to a GraalVM based base image and compile my code into a native binary. Finally, I switch to a minimal base image and then start my binary.
The official Quarkus documentation has a great example of how to do this, at https://quarkus.io/guides/building-native-image#multistage-docker.
Over at https://github.com/madsop/quarkus-multistage/blob/main/Dockerfile, you can see an example of how I’ve done this myself in production.
Now that you have your container and all, let’s look at how to make it reach the public. I’ll show you how to do this with Google Cloud Build, however the principles and concepts are applicable for your cloud of choice.
Google Cloud Build uses cloud build files for you to specify how the build should be done. These are conceptually comparable to Jenkinsfiles or GitHub Actions YAML files. What you need to do here is to make sure your code is being built on a sufficiently powerful build node, as native build is resource heavy. What the exact numbers you need here are depends on the size of your application and other variables — my experience is that the default build node is ok for trivial-sized applications.
If you as part of your build process also want to deploy your container somewhere, this is also where you’ll do that. I’ve chosen Google Cloud Run as my somewhere here, so I’ll specify the destination details. As you might expect, Google Cloud Build and Cloud Run play nicely together and the integration is pretty straight forward. The same concepts go for Amazon Lambda or any other KNative based platform, though.
With that, you’re ready to go! Enjoy!