Looking for solutions to my archive extraction problem brought me to look at the Rocker project from Grammarly. I’d looked at it before but not in any great detail. In a nutshell, it aims to extend the Dockerfile syntax to overcome some of its current limitations. Although not an option for me (because I can’t depend anything beyond the standard Docker toolset) the Rocker solution to my earlier problem would be as simple as follows:
1 2 3 4 5 |
FROM ubuntu:16.04 RUN groupadd bar && useradd foo -g bar -m USER foo MOUNT .:/context RUN tar xzf /context/example.tar.gz |
The extra syntax here is the MOUNT
command which follows the same syntax as the --volume
flag on docker run
. As the Grammarly team point out, there are trade-offs here which help to explain why the Docker maintainers are reluctant to add volume mounts to docker build
. Here, changes to the contents of the mounted directories do not result in the cache being busted.
Anyway, this post is meant to be about a different problem: building Docker images where the chosen language requires compilation e.g. Java. One approach (that taken by OpenShift’s source-to-image) is to add the source to an image that contains all of the necessary pieces to build, package and run it. As shown in Jamie’s WASdev post, for Liberty that might mean putting Maven and a full JDK in to the image. I’m not a fan off this approach: I prefer to end up with an image that only contains what is needed to run the application.
The following shows how this might look using Rocker:
1 2 3 4 5 6 7 8 9 10 |
FROM maven RUN git clone https://github.com/microprofile/microprofile-samples.git /root/microprofile-samples WORKDIR /root/microprofile-samples MOUNT /root/.m2 RUN mvn clean install EXPORT /root/microprofile-samples/microprofile-sample-canonical/target/microprofile-sample-canonical.war FROM websphere-liberty:microProfile IMPORT microprofile-sample-canonical.war /config/dropins TAG sample:latest |
Here we’re building one of the Micro Profile samples (which uses Maven) and then creating an image with the resulting WAR and the new WebSphere Liberty Micro Profile image. You’ll note that there are two FROM
statements in the file. First we build on the maven
image to create the WAR file. We then use the Rocker EXPORT
command to make the WAR file available to subsequent build steps. The second part of the build then starts with the websphere-liberty:microProfile
image, imports the WAR and tags it. Building, running and then calling the application is then as simple as follows:
1 2 3 |
rocker build . docker run -p 9080 -d sample curl $(docker port sample 9080)/microprofile-sample-canonical |
The other thing of note is that we’ve used the MOUNT
command to create a volume for the Maven cache. The volume is tied to this Rockerfile so, if you run rocker build --no-cache .
you’ll see that it rebuilds the images from scratch but Maven does not need to download the dependencies again.
The MOUNT
is also a great way to overcome the long-running issue of how to provide credentials required to access resources at build time without them ending up in the final image. Other nice features include the ATTACH
command which effectively allows you to set a breakpoint in your Rockerfile, and the fact that the Rockerfile is pre-processed as a golang template allowing much more advanced substitutions than with Docker’s build arguments.
[…] copying files between them. This brings to regular Docker build a capability that I’ve previously talked about in the context of Rocker, and something that’s of particular use in a compiled language like […]