In this post, I will outline a technique to make local docker compose builds faster by pre-fetching dependencies.
For Spinnaker, we provide a docker compose configuration.
Recently, I wanted to add the ability to build and run images locally by providing a developer configuration. This would allow someone to test a change that spans multiple services by baking an image locally.
One of the issues I ran into was that when the Dockerfile is ran, it downloaded all of my Spring Boot dependencies along with the gradle wrapper, adding significant time to my builds.
If you are unfamiliar with docker-compose, the build command basically takes the dockerfile defined for your project and creates an image from it. There isn’t really a straightforward way to mount a volume at build time for me to point to an existing .gradle directory.
To rectify this, I changed the line I used to build in my Dockerfile file from
RUN ./gradlew buildDeb -x test
to
RUN GRADLE_USER_HOME=cache ./gradlew buildDeb -x test
This simply tells gradle to download all my dependencies to the cache directory in my source code.
Since my Docker build copies the contents of the build directory into the image first, I can then cache all my dependencies in the folder ( outside of docker compose ):
GRADLE_USER_HOME=cache ./gradlew compileGroovy clean -x test --refresh-dependencies
Now, all the libraries and wrapper files my build needs is in the cache directory. The next time I try to build my image with docker compose, it will find that all the jar files and gradle wrapper exists and will not download them.
In the CI system, since the cache directory is not checked in, it will download it fresh, which is the behaviour we want.