Creating a Docker image of a Java application usually comes with the hustle of having a correct Dockerfile, a running docker daemon for a local build or delegate it to a CI tool like Jenkins, Travis CI, etc. That is, it was! With Jib, it's a straightforward process only bound to Maven or Gradle.

What's Jib ?

The Google blog post introducing Jib offers a really good definition:

An open-source Java containerizer from Google that lets Java developers build containers using the Java tools they know. Jib is a fast and simple container image builder that handles all the steps of packaging your application into a container image. It does not require you to write a Dockerfile or have docker installed, and it is directly integrated into Maven and Gradle—just add the plugin to your build and you'll have your Java application containerized in no time.

Basically, it's as stated. Jib helps us build a docker container without following the usual process and that only with a Maven or Gradle plugin. Here follows images to help you visualise the difference between the usual docker build flow against the jib build flow.

Docker build flow:

docker_build_flowrc1o.max-600x600

Jib build flow:

jib_build_flowb135.max-600x600

We can go from our code to an image pushed in our registry in one shot, without even having a running local docker daemon, that's awesome right ?! And yes, you don't actually need a running local docker daemon to push your image to a registry. Jib natively supports pushing to common registries such as Docker Hub, Google Container Registry (GCR), Amazon Elastic Container Registry (ECR) and Azure Container Registry (ACR) and for a custom one, just prefix the url to your image name.

Usage

Take a trip to the Jib Maven Plugin Quickstart or if you are using Gradle, go here instead. Read it in order to setup things like authentication to your registry. I will take this sample Spring Boot project available on Github as an example. After added this to the pom:

<plugins>
    ...
    <plugin>
        <groupId>com.google.cloud.tools</groupId>
        <artifactId>jib-maven-plugin</artifactId>
        <version>${jib.version}</version>
        <configuration>
            <from>
                <image>openjdk:8u222-jre</image>
            </from>
            <to>
                <image>docker.io/mlniang/jib-example:${project.version}</image>
            </to>
            <container>
                <entrypoint>
                    <shell>sh</shell>
                    <option>-c</option>
                    <arg>chmod +x /entrypoint.sh &amp;&amp; sync &amp;&amp; /entrypoint.sh</arg>
                </entrypoint>
                <ports>
                    <port>8080</port>
                </ports>
                <environment>
                    <SPRING_OUTPUT_ANSI_ENABLED>ALWAYS</SPRING_OUTPUT_ANSI_ENABLED>
                </environment>
                <creationTime>USE_CURRENT_TIMESTAMP</creationTime>
            </container>
        </configuration>
    </plugin>
    ...
</plugins>

The command mvn compile jib:build built and pushed my image to docker hub. Assuming by now, you have read the documentation, here is a quick explanation of this configuration:

  • Create and push the image mlniang/jib-example:<pom_project_version> to docker hub using openjdk:8u222-jre as a base image.
  • In any container from our image, as the entrypoint, execute the script located in src/main/jib/entrypoint.sh which contains the java command for running our Spring Boot app in standalone mode. Jib will automatically add to the image any files in the src/main/jib directory which will be available as the root of our containers.
  • Expose the port 8080 of any future container for access to our Spring Boot app.
  • Set the environment variable SPRING_OUTPUT_ANSI_ENABLED of any container .
  • Set the creation time of the container to be the current timestamp (it will use the UNIX EPOCH by default).

Note that your code need to be compiled before the jib:build goal and the first run may take some time but next builds will be more faster due to the layer caching.
If you want to just build your image and not push it, you can use the jib:dockerBuild goal instead. However, for this one, you must have a running docker daemon.

You can bind the execution of jib:build or jib:dockerBuild to an existing Maven lifecycle. If we want the jib:build goal to execute when we package our app, we will add to the plugin configuration:

...
<plugin>
    <groupId>com.google.cloud.tools</groupId>
    <artifactId>jib-maven-plugin</artifactId>
    <version>${jib.version}</version>
    <configuration>
            ...
    </configuration>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>build</goal>
            </goals>
        </execution>
    </executions>
</plugin>
...

Now, mvn clean package is enough to get the job done.

One last thing: you can use command line parameters to override your configuration. One use case is when we want the current tag version of our image to be the latest. After my usual package command, I can go for:

$ mvn clean package -Djib.to.image=docker.io/mlniang/jib-example:latest

Without even modifying my pom.

There are plenty more to do with Jib like build tar archives, advanced configuration, etc. Have fun with the documentation and I hope this tool will help you in your daily life.