Build docker images with gradle

In this article, we will understand how to create docker images from gradle build for a java application with example.
Before moving further, make sure that you have following installed on your system

  • Gradle
  • Docker

For this article, we will use a simple java application having a single file with main class as shown below

public class Main {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}

Gradle changes
Gradle does not offer an inbuilt functionality to create a docker image.
For this, we need to use a plugin and the plugin to build a docker image with gradle, use the Jib plugin. This plugin provides a simple way to build and publish container images using gradle.

Following are the steps for configuring your gradle application to create docker image
1. Add jib gradle plugin.

plugins {
    id 'com.google.cloud.tools.jib' version '3.3.1'
}

2. Add jib task to create an image from your application and push it to the registry as shown below

jib {
    from {
        image = 'openjdk:11-jdk-slim'
    }
    to {
        image = 'demo-app'
    }
}

In this example, we’re using the openjdk:11-jdk-slim base image and tagging our image as demo-app.

When you execute this task with gradle with command

gradle jib

When the above command is executed, below is the workflow that is executed step by step
1. Gradle compiles the java code and generates the class files.
2. Jib creates a docker image with the details specified in jib task such as which base image to use and the name of the image.
3. Jib copies the compiled class files into the docker image, along with any dependencies specified in your gradle build.
4. Jib configures the entry point for the docker image to be the java command, along with the appropriate classpath and command line arguments to run the application.
5. Jib then builds the docker image and publishes it to the registry.

In this case, since there is a single java file, jib sets it to be the main class.
If there are multiple files, then you can specify the main class with container property under jib task as shown below.

jib {
    from {
        image = 'openjdk:11-jdk-slim'
    }
    to {
        image = 'demo-app'
    }
    container {
        mainClass = 'com.codippa.Main'
        jvmFlags = ['-Xms512m', '-Xmx1024m']
    }
}

You can also create a jar from the application file by supplying the jar task and providing it the Main-Class attribute.

Below is a complete gradle file, which specifies the plugins, dependencies and also contains jar and jib tasks.

plugins {
    id 'java'
    id 'com.google.cloud.tools.jib' version '3.3.1'
}

group 'com.codippa'
version '1.0-SNAPSHOT'

repositories {
    mavenCentral()
}

dependencies {
  implementation 'org.springframework.boot:
                  spring-boot-starter-web:2.7.5'
}

jar {
  manifest {
    attributes 'Main-Class': 'com.codippa.Main'
  }
}

jib {
  from {
    image = 'openjdk:11-jdk-slim'
  }
  to {
    image = 'demo-app'
  }
  container {
    mainClass = 'com.codippa.Main'
        jvmFlags = ['-Xms512m', '-Xmx1024m']
    }
}

Default tag of the image will be latest. If you want another tag for the image, then specify it after the image name as below

jib {
  from { 
    image = 'openjdk:11-jdk-slim'
  } 
  to { 
    image = 'demo-app:1.0-SNAPSHOT' 
  } 
}

or with tags option

jib {
  from { 
    image = 'openjdk:11-jdk-slim' 
  } 
  to { 
    image = 'demo-app'
    tags = ['1.0-SNAPSHOT'] 
  }
}

If you do not specify any registry, then by default it will publish to docker hub. If you want to push the image to another registry with, then specify the registry URL along with the image name as shown below

plugins {
    id 'com.google.cloud.tools.jib' version '3.3.1'
}

jib {
    to {
        image = 'docker.io/<repository>/demo-app:1.0-SNAPSHOT'
        auth {
            username = "<username>"
            password = "<password>"
        }
    }
}

The auth block provides the username and password for authenticating with the container registry.

Custom docker file
Till now, we looked at a simple image that is built on jdk image and contains the application.
It might happen that your application image has additional instructions such as copying files, downloading libraries, exposing ports etc.
You can use an external Dockerfile to build application images with buildDocker configuration option inside jib task.
buildDocker provides a lot of customization options for image creation process such as the name and tag of the image, the Dockerfile location, and the options to use for the docker build command.

Below are the options that can be configured with buildDocker configuration
1. dockerBuildDirectory: The directory containing the Dockerfile to use for the build.
This can be an absolute path or a relative path from the project root directory.
2. dockerBuildContext: The context directory to use for the docker build.
This can be an absolute path or a relative path from the project root directory.
3. dockerfile: The name of the dockerfile to use for the build.
This can be an absolute path or a relative path from the dockerBuildDirectory.
4. labels: Additional labels to add to the docker image.
5. env: Additional environment variables to set during the docker build.
6. volumes: Additional volumes to mount during the docker build.
7. entrypoint: The entry point to use for the docker image.
8. user: The user to use for the docker image.
9. ports: The ports to expose on the docker image.
10. buildArgs: Additional build arguments to pass to the docker build.

An example of these options is given ahead

jib {
    from {
        image = 'openjdk:11-jre-slim'
    }
    to {
        image = 'demo-registry/demo-image:1.0-SNAPSHOT'
    }
    container {
        mainClass = 'com.codippa.Main'
    }
    buildDocker {
        dockerBuildDirectory = 'app'
        dockerfile = 'app-dockerfile'
        labels = ['version': '1.0']
        ports = ['8080', '8443']
    }
}

Creating local images
jib task is used to create and push images to docker registry.
If you want to create an image and run it locally with docker, then jib plugin provides another task jibDockerBuild for this.
Simply execute

gradle jibDockerBuild

and it will create a docker image with default configuration and publish it to local docker daemon.

jibDockerBuild task can be customized as per requirement, such as specifying a custom base image or Dockerfile, by adding configuration options to your build.gradle file.
Example,

plugins {
    id 'com.google.cloud.tools.jib' version '3.3.1'
}

jib {
    from {
        image = 'openjdk:11-jre-slim'
    }
    to {
        image = 'app-registry/demo-app:1.0-SNAPSHOT'
    }
    container {
        mainClass = 'com.codippa.Main'
    }
}

jibDockerBuild {
    dockerBuildDirectory = 'app'
    dockerfile = 'appdockerfile'
    labels = ['version': '1.0']
    ports = ['8080', '8443']
}

Once the jibDockerBuild task succeeds, you can run the image locally with docker run command as below

docker run demo-app:1.0-SNAPSHOT

Remember that jibDockerBuild task requires docker installed locally on your system.
If you try to run the jibDockerBuild task without a local Docker daemon, you’ll see an error message as shown below

> Task :jib FAILED
No installed Docker daemon detected. Please install Docker and try again.

So make sure you have docker installed and running before using the jibDockerBuild task.

That is all for this article on creating docker images from gradle build. Hope it was informative.