Topic navigation

Blog Articles

Optimizing Red Hat Fuse 7 Spring Boot container images

Working with Red Hat Fuse 7 on Spring Boot is straightforward. In my field experience, I have seen many development (a.k.a. integrator) teams moving to Fuse 7 on Spring Boot for their new integration platforms on Red Hat OpenShift Container Platform (well aligned with agile integration).

Lately, however, I have also seen some teams worried about the size of the final images and the deployment pipeline. In most cases, they had developed a set of common libraries or frameworks to extend or to homogenize the final integration projects. All the cases have the same result:

Several dependencies copied in each integration project

Always replacing the container images with the latest fat JAR (including the same dependencies) in each build pipeline

Spring Boot is usually packaged as “fat JARS” that contain all runtime dependencies. Although this is quite convenient, because you only need a JRE and a single JAR to run the application, in a container environment such as Red Hat OpenShift, you have to build a full container image to deploy your application.

Single application layer vs. multiple application layer

A typical container image with Fuse 7 on Spring Boot application has the following structure:

Basically, the image has only two big layers:

Fuse 7 on Spring Boot Application: Fat JAR created by Fuse 7 with Spring Boot and all its dependencies

Java Runtime: Base image providing the JRE and other libraries, tools, and so on.

Every time the application is built with this structure, we are wasting the storage of that layer. Basically, the container image build process will not reuse the cache of that layer because we are adding a new fat JAR, which may be very similar to the previous one because normally we are doing small changes in our application.

One of the best practices of Dockerfiles is reducing the number of layers; however, in our case, we should also minimize the size of the layers we are changing between each build. Applications developed with Fuse 7 on Spring Boot will change a few things (Camel Routes, Beans, etc.); however, the dependencies used are basically the same for each build process.

To optimize the storage and increase the build process and the deployment phase, we need to change the default structure. The new structure should be similar to:

This new structure is based on three layers:

Application: This only has the final application. The components could be changed several times, but the layer is small because it only includes the items needed for the application (Apache Camel context basically).

Spring Boot and dependencies: Any dependency or library needed by the application will be managed in this layer. It is bigger than the previous one, but this layer will change only if we apply changes in the dependency tree of the application.

Java Runtime: Base image providing the JRE and other libraries, tools, and so on.

To achieve this, there are other strategies, such as Docker multistage build; however, this article is based in Maven and its life cycle.

The main steps are:

To not package the application as Spring Boot does (i.e., not use spring-boot-maven-plugin)

Use Maven plugins to copy any runtime dependency needed by the application in one place

Use Maven plugins to build a simple jar file with the classes provided by the application. This new application will include a MANIFEST.MF file with:

Main class name

Class-Path entry to locate any dependency needed by the application.

Build container image using a Dockerfile aligned with the new layer structure:

Download and learn more about Red Hat JBoss Fuse, an innovative modular, cloud-ready architecture, powerful management and automation, and world class developer productivity. It is Java™ EE 7 certified and features powerful, enterprise-grade features such as high availability clustering, distributed caching, messaging, transactions, and a full web services stack.