First look at Jib

July 11, 2018

As soon as Google’s blog post “Introducing Jib — build Java Docker images better” was online, all my channels went crazy about Jib. That was a bit surprising as Jib was started over one year ago but with this blog post this project rockets with more than 1000 new GitHub stars within one day. Crazy.

I got a lot asked yesterday how Jib compares to fabric8io/docker-maven-plugin (d-m-p) or fabric8io/fabric8-maven-plugin which includes d-m-p.

Let me try to shed some light on the differences and pro and cons of both approaches.

How Jib works

Let’s first have a quick look what Jib offers today and what makes it unique.

Looking at the Jib Maven plugin it currently supports three goals:

  • jib:dockerBuild for assembling an image and loading it to a Docker daemon.
  • jib:build for assembling the image and pushing it to a Docker registry.
  • jib:exportDockerContext for creating a Dockerfile along with all the files required for performing a build against a Docker daemon.

The unique asset of Jib is that it does all of this without consulting a Docker daemon by creating all image layers and metadata locally, conforming to either the Docker or OCI format. And that all directly with plain Java code.

Also, Jib assumes a very opinionated project setup with a so-called flat classpath app with main class, dependencies and resources all in different artefacts. Compare this to fat jars popularised by Spring Boot, where the application code, resources and dependencies are all stored in the same jar file. There are some drawbacks of flat classpath app, but one benefit is, that you can organise the various files into several layers in your image, putting the one which is changing less (like dependencies) at the bottom of the layer stack. That’s what Jib does: It puts all dependency jars into one layer, all resource files (like property files to be loaded from the classpath) in another and the application classes into a third layer. All of these layers get aggressively cached locally. That way, you can be much faster when recreating images than with fat jars which apparently can be stored only in one single layer.

But let’s have a look how Jib works in detail. The steps performed by a jib:dockerBuild or jib:build are:

  • Fetch base image’s layers and cache them locally. By default the base image is gcr.io/distroless/java, but this can be configured.
  • Create three application image layers for

    • Dependencies
    • Resources
    • Classes

    Since these layers are cached, if any of them doesn’t change (which is likely for dependencies), then the layer is not recreated.

  • Create the application image locally. The two previous steps were performed asynchronously; this step continues when both previous steps have been finished. The ENTRYPOINT of this image is fixed set to:

        java <jvm-flags> -cp dep_dir/*:resource_dir/:class_dir/ <main-class>
    

    where <jvm-flags> can optionally be configured and <main-class> is the mandatory main class to specify. The information leading to the classpath comes from the underlying Maven or Gradle project information. Also, any Java arguments configured to become the CMD of the image, as well the exposed ports (EXPOSE) can be added, too.

  • Finally the local image layers along with its meta-data is tarred up, and either loaded into a Docker daemon or pushed to a Docker registry.

How does Jib compare to d-m-p ?

Jib is impressive for the use case it supports and brings a fresh spin to the way how Java apps can be packaged into Docker images.

The most significant benefits of Jib are:

  • Fast for incremental builds when you have a flat classpath application, resulting in three different layers for dependencies, resources and your application classes.
  • No Docker daemon required, which reduces the build requirements and increases security because the image creation happens without root permissions.
  • Produces reproducible images by wiping out file owner and modifications date. However, not sure whether e.g. generated timestamps in resource files like properties are wiped out, too.
  • It supports both, Maven and Gradle.

However, there are also some limitations. Some of them might be tackled in the future, but other might not be changed due to the unique way how Jib works:

  • Can be only used for simple flat classpath Java applications. There is currently no support for fat jars (i.e. Spring Boot fat jars) nor other packaging formats like WAR file.
  • Simplistic startup of the application with a plain java call instead of using a full features startup script like run-java-sh.
  • No additional files like configuration files outside the classpath or agents like jmx_exporter agent can be added (but there is a PR pending for agents).
  • Fixed classpath order, e.g. doesn’t allow for overwritten resources in dependencies as dependencies are always first on the classpath.
  • Jib uses a custom XML configuration syntax instead of plain Dockerfile syntax (which I often heard as a major critique about d-m-p which also supports a custom XML configuration, but as alternative to Dockerfiles).

d-m-p provides some additional features which are not supported by Jib, like

  • Running containers for integration testing (that’s very likely the most prominent difference)
  • Dockerfile support
  • docker-compose.yml support
  • Enhanced authentication support OpenShift and Amazon ECR support
  • Support for watching code change and then automatically triggering a rebuild of images and restart of containers
  • Support for arbitrary assembly and base images, including Spring Boot fat jar and JavaEE containers.
  • Healthchecks

And if you jump to fabric8-maven-plugin, which includes d-m-p for its image building business, you have even more high level features like a zero config mode which analyses your pom.xml and selects opinionated defaults like base images and handcrafted startup scripts, depending on the type of tech stack you are using (like Spring Boot, Thorntail, Vert.x, Tomcat, …)

Next steps …

This overview is only a quick glance on Jib. In one of the next posts, I plan to show some real-life examples and also measuring the performance gain by using Jib.

Also, there a plans for d-m-p to add support for Jib backend and other daemonless build systems like img, buildah or kaniko. The mid to longterm plan is to enhance the build abstraction within d-m-p and offer, based on the Java project given, different ways to build images.

BTW, if you are interested in what’s going on in Docker image building business these days, you probably might find this KubeCon presentation as useful as I did. Daemonless FTW :)

Psst, d-m-p also likes GitHub ;-)