1. Introduction

We’re all using Docker containers these days to do all sorts of great stuff, but it’s not always obvious how best to automate the building of images and embed good repeatable processes into our projects. In this article you’ll discover one of the best approaches I’ve found to automate Docker in your project. It’s to create tasks for the building of images and running of containers within your Gradle project. 

With this approach, anyone checking out your code can immediately start a Docker container without having to read your documentation. If you’re working on a project with multiple source code repositories, having a consistent approach like this makes it a lot easier for people to get to grips with code you’ve written.

2. Overview

When it comes to Docker plugins there are two main players right now, the Palantir Docker Plugin and the Bmuschko Docker Plugin. Technically, they are best described as suites of plugins as there are separate plugins providing specific fine grained functionality.

I generally use the Palantir Docker Plugin as it’s slightly less opinionated in that it let’s you configure your own Dockerfile. This is the plugin I’ll be using in this article. The Bmuschko Docker Plugin, on the other hand, generates the Dockerfile for you or allows you to configure it’s instructions in your build.gradle.

They are both good plugins, so here’s a quick summary of what to expect from each.

Feature Description Palantir Docker Plugin Bmuschko Docker Plugin
Create Dockerfile task
Build image task
Push image task
Tag image task
Run container task
Stop container task
Remove container task
Summary More tasks with more functionality available. Less configuration required. Uses provided Dockerfile, which cannot be generated automatically Less tasks available, but you can implement one of the provided custom task types.
Configuration is more opinionated. e.g. you can build an image for a Spring Boot project, without having to define the Dockerfile.
See article bmuschko Docker Gradle plugin review for full details.

3. Step-by-step example

Let’s run through an example, where we’ll create a project that deploys an existing Java application into a Docker image and runs it.

3.1 Application to deploy

You can download this sample application jar file which runs a Spring Boot application on port 8080. Create an empty directory, put the jar file in it, then try running:

mkdir gradle-docker-example cd gradle-docker-example cp . java -jar springbootify.jar

You’ll then be able to make a request to http://localhost:8080/doit

3.2 Initialising Gradle

Now that you have a sample application in an empty directory, let’s initialise the project with whatever version of Gradle you have installed on your machine. If you don’t have Gradle yet, you can download it from gradle.org.

Tip: To run Gradle you’ll also need a Java installation and a JAVA_HOME environment variable configured. Also, don’t forget to add Gradle to your PATH environment variable.

Run the following command to setup Gradle in this project:

gradle init
<hit enter to all the questions>

You’ll now have a Gradle project setup, along with the build.gradle, which is where we’ll be putting all the configuration for building this project.

3.3. Create the Dockerfile

The Dockerfile is the text file where we’ll put the instructions to tell Docker how to build our image. There’s no file extension, so just create a file with name Dockerfile.

FROM openjdk:12-jdk-alpine
COPY springbootify.jar springbootify.jar
CMD \["java","-jar","springbootify.jar"\]

3.4. Building the Docker image with Gradle

Now it’s time for the fun stuff. Let’s configure the Palantir Docker plugin to create an image for the Dockerfile we just created, and then run the container. Open up build.gradle and add this plugin declaration to apply the plugin:

plugins {
    id "com.palantir.docker" version "0.22.1"

Add a project version as we’ll want to use this within the name of our image:

version '0.1.0'

We’ll now specify a configuration for the com.palantir.docker plugin to build the image.

docker {
     name "${project.name}:${project.version}"
     files 'springbootify.jar'

We can now try out building the image by running:

./gradlew docker

All done? Run docker images to see that your new image has been built.

3.5 Running the Docker image with Gradle

To run the image as a container, we need to add another plugin:

plugins {
    id "com.palantir.docker" version "0.22.1"
    id "com.palantir.docker-run" version "0.22.1"

Info: note how these plugins are fine grained, so you don’t need to import more functionality than you need.

We’ll also need a specific configuration for running the container.

 dockerRun {
    name "${project.name}"
    image "${project.name}:${project.version}"
    ports '8080:8080'
    clean true

Now we can run the container:

./gradlew dockerRun

Check out docker ps, which will show your running container.

The application is now available on port http://localhost:8080/doit.

To stop the application, you can run ./gradlew dockerStop.

4. Next steps: pushing your image

Now that you’ve seen how to build images and run containers, it’s worth thinking about how this fits into your full build process. Consider the scenario where you want to build the application on your continuous integration (CI) server. You’ll want to make sure to run:

./gradlew build docker

This will first build your application, which for example, may generate a jar file like we were using in the previous section). It will then build your image. This is great, but to get it deployed somewhere else, it needs to be pushed to a central location so that other Docker installations can access it.

4.1 Setting up an account on Docker Hub

Docker Hub allows you to create public or private repositories where you can store your images. There are many other options available, for example AWS’s Elastic Container Registry (ECR), but Docker Hub is the simplest way to get started.

Tip: a Docker repository is where you put different versions of a specific image (e.g. gradle-docker-example), whereas a Docker registry is where those repositories live (e..g Docker Hub)

Follow these steps to sign up and create your repository, then in the next section you’ll see how to tag and push your image to it.

  1. Go to hub.docker.com and create an account
  2. Go to Repositories and create a private repository called gradle-docker-example

Note down your repository name, e.g. /gradle-docker-example

4.1. Configuring the plugin for pushing

Docker’s mechanism to push an image to a central repository involves first tagging the image with the repository name, then pushing it. 

The Palantir Docker plugin exposes two more types of tasks for this purpose, dockerTag and dockerPush. Let’s extend the dockerConfiguration to specify the tag:

docker {
    name "${project.name}:${project.version}"
    files 'springbootify.jar'
    tag 'DockerHub', "<dockerHub-username>/gradle-docker-example:${project.version}"

Replace with your username.

Run ./gradlew tasks and you’ll see there are more tasks available now.

You can see that the DockerHub string we put in the tag configuration is used to dynamically generate the tasks dockerTagDockerHub and dockerPushDockerHub.

Run ./gradlew dockerTagDockerHub and then check the output from docker images:

Now you’ll see we now have 2 versions of the image we created before, with the same image id. The new version of the image has:

  1. An external repository pointing at your Docker Hub account
  2. A tag, normally the version of your application.

In order to push you’ll first have to authenticate with the Docker Hub:

docker login
<enter username & password>

The last step is to run ./gradlew dockerPushDockerHub. You’ll see an output similar to below.

Tip: Docker pushes are incremental. If, for example, you add a new instruction to your Dockerfile it is intelligent enough to only push changes from that point onwards.

4.2 Pulling the image from another machine

Now your image exists on a central registry you can run it from whatever server you like, with the following commands:

docker login
<enter username & password>
docker run -p 8080:8080  <dockerHub-username>/gradle-docker-example:0.1.0

Tip: If you want to try this out on the same server where you built the image, just run docker rm <image-id> before running the above command. That way, docker will not find the image locally, and instead pull it from the Docker Hub like in the output below.

5. Conclusion

Now you’ve seen how to create images, run containers, and push images using simple Gradle Docker tasks.

Due to the simplicity of running these tasks it’s very easy to integrate them into your software development lifecycle, such as during development, continuous integration, or deployment.

To get started right away check out the accompanying GitHub repository, containing all the code from this article.

Missing Gradle knowledge makes you slow

A broken build is easy to fix with a quick Google search, but a misconfigured build costs hours of time each week.

Time to build. Time to test. Time to fix.

Knowing the "right" way to build a project isn't obvious, especially with pages of hard-to-follow documentation. But for your project to scale, you must master the build script and configure it effectively.

That's now a lot easier with Gradle Build Bible.

Download this step-by-step guide designed for Java developers like you who are ready to master Gradle.

  • Uncover all the mysteries of the Gradle build script.
  • Fix your build to make development fun again.