KubeAcademy by VMware
Building Images for Java Applications with JIB
Next Lesson

Jib is a tool for building images from Java applications without using Docker or Dockerfiles. In this lesson we learn how to use Jib's Maven and Gradle build plugins to configure and build images.

Cora Iberkleid

Part Developer Advocate, part Advisory Solutions Engineer at VMware

Cora Iberkleid is part Developer Advocate, part Advisory Solutions Engineer at VMware, helping developers and enterprises navigate and adopt modern practices and technologies including Spring, Cloud Foundry, Kubernetes, VMware Tanzu, and modern CI/CD.

View Profile

In this lesson, we'll learn about Jib. Jib is an open source tool that was introduced by Google in 2018 for turning Java applications into container images. We'll cover the benefits of Jib, how to use and configurate, and we'll explain some of the features that it offers.

Jib makes it easy to containerize Java applications. For one thing, it doesn't require or use a Dockerfile, which eliminates the challenge of learning about and maintaining Dockerfiles. It can also be used without a Docker daemon or the Docker CLI, which may be useful in some cases, particularly in build or continuous integration tool chains. It's provided as a plugin for either Maven or Gradle. For all of these reasons, it's very easy to incorporate Jib into an existing Maven or Gradle workflow. Jib also applies optimizations to achieve faster builds and faster application startup times. Finally, Jib guarantees reproducible builds. Let's take a closer look, so that we can get a better understanding for what all of this means. We'll be using Maven for our examples, but you can find the equivalent syntax for Gradle in the Jib documentation on GitHub.

The quickest way to try out Jib is by adding the plugin directly in your Maven command. You can use the build goal to build an image, and upload it directly to a registry. In this case, you need to provide a name for your image that includes the registry address, and the repository name. If you do work with Docker, and want to save the image to your local Docker daemon, you can use the Docker build goal. You can set an image name and tag, or you can accept the defaults, which are the artifact ID and version in the POM file. Finally, you can publish the image as a TAR file, and then use Docker load to load it into your daemon. The loaded image name will be the artifact ID and version.

As with any Maven plugin, you can also add the plugin information to your POM file, and simplify the Maven command. Or you can go further and bind the goal of your choice to the build lifecycle, so that a simple Maven package will produce an image. It really is very simple to use. Now, let's move on to some of the features of Jib.

We mentioned that Jib introduces some optimizations for build speed and application startup. Jib doesn't use a Dockerfile, so this example is merely illustrative, but it helps to understand the optimizations. The first optimization is that Jib places the application files in the image in an unpacked format, rather than as a JAR file. Simply unpacking a JAR file can improve application startup times by about 30%.

Secondly, Jib copies over the files in layers. Now, let's take a step back, and review how Docker handles image transfers over the network. When Docker pushes or pulls an image it transfers only the layers that have changed, so if your Java application comprises a single layer, either because it's a JAR file, or because the unpacked files were all copied as a single layer then any change in the application would cause the entire application layer to be transferred over the network. This can be especially significant when pushing changes to say hundreds or thousands of edge nodes. It's much more efficient to transfer a few kilobytes of a changed class file than say a hundred megabytes of unchanged dependency files. Jib takes advantage of this fact and organizes the unpacked application files into sub-directories based on how frequently they're likely to change. It then copies the files in the sub-directories to the proper location in the image in order committing each set of files as a separate layer. This is illustrated in the copy statements in this mock Dockerfile.

Finally, Jib uses the main class name in the launch command. You can set the class name using the variable shown or let Jib infer it. While not as impactful as unpacking a JAR, specifying the name of the main class also improve start-up.

Now, these are optimizations that you could manually incorporate into your Dockerfiles, but Jib is doing them automatically for all Java applications. We mentioned that one of Jib's benefits is reproducible builds, but what does this mean? Images can be uniquely identified by their hash or digest. By default, given the same inputs, Jib guarantees that it will always build an image with the same digest. Notice here we have three images. All three were built using Jib, and no code or configuration changes were made between builds. To make the example more clear, each image was built with a different Jib goal as indicated by the tag. Notice that the image IDs are all the same, so despite the fact that these images were built at different times and that they're tagged differently, any person or system can use the IDs to confirm that these images are actually identical.

You might also notice the created column that claims that the images are built 50 years ago. In order to ensure that two builds produce the same digest certain data has to be standardized. One example is setting all timestamps to the same value. This includes creation and modification times of files and directories, ownership of files and directories, and the image creation timestamp. Jib provides a way to override the default behavior for the creation timestamp, but you can't have your cake and eat it too. That would be at the expense of having reproducible builds.

The base image that Jib uses by default is a distro list Java, which means that it contains only the necessary components to run a Java application, and no more. It doesn't have package managers, shells, et cetera. If you simply want to add a shell for troubleshooting purposes you can use the same base image that Jib uses by default with a debug tag to get a variant with a shell. If you need to install system packages you need to supply your own base image. Now, keep in mind that we're talking only about the runtime image. There's no concept of a separate build image with Jib, as we saw in the multi-stage Dockerfile example earlier in this course. The application compilation is done by Maven on your local machine, and then the runtime image is assembled by Jib.

In addition to the configuration tags for the application image name, and the base image name, Jib provides container configuration tags for a variety of other parameters. For example, by default the runtime user will be root, but you can change it as shown here. You can specify an arbitrary ID or take advantage of a default non-root user that's pre-configured on the distro list base image for precisely this purpose. You also may have noticed in the mock Dockerfile that we looked at earlier that Jib provides a way to copy arbitrary files to the image. It copies anything in the source main Jib directory to a non-class path location on the image. You can use this feature, for example, to add a Java agent and then use the tags for JVM Flags to enable it.

That concludes our lesson on Jib. We saw how easy it is to use, how it optimizes for performance and predictability, and that it provides plenty of configuration hooks. We can also now understand some of the limitations. It has a simple model with the local file system providing the build environment, and configuration is baked into each application's POM file, which makes it hard to standardize, reuse, and to manage configuration across applications. Nevertheless, it's been a very useful tool since its inception a couple of years ago, and it's enabled many Java developers to easily create containers without having to deal with the challenges of creating and maintaining Dockerfiles.

Give Feedback

Help us improve by sharing your thoughts.

Share