A Docker Multistage Build reduces the complexity of a production image to a minimum. Since it is very easy to build jars from Clojure applications, we can just use an JRE to bring it to production.
Let's create a sample project using the app
template with Leiningen:
lein new app foo
cd foo
Now, inside the project, we can create a Dockerfile
build the project and
create a production image, which comes with an JRE instead of a whole JDK:
FROM clojure:openjdk-11-lein-slim-buster AS BUILD
COPY . /code
WORKDIR /code
RUN lein uberjar
FROM openjdk:11-jre-slim
WORKDIR /app
COPY --from=BUILD /code/target/uberjar/*-standalone.jar ./app.jar
CMD ["java", "-jar", "app.jar"]
Build the image:
docker build -t foo .
And now let's start the application:
docker run --rm foo
and see "Hello, World!" on the terminal's output.
The size of the resulting image is "only" 209 MB big, which is kind of okayish, because we have a whole JRE packed inside. If we'd chosen a normal JDK build without Multistage, the resulting image would be around 431 MB big. This saves us some disk space and reduces potential attack vectors, because there are fewer possible binaries, which could be exploited. And also it is satisfactory to see the Megabytes get reduced by solely choosing a different base-image and copy the compiled files into this image.