1. Story
- That is a common way to package our applications as a Docker image by using Dockerfile as explained here. But as a Java developer we use Maven or Gradle for building and packaging our applications. And so we want to make building of Docker image fits into our workflow.
- Another story is that as a Java developer I do not prefer to build Docker images without a Docker daemon.
- And also another story is that I have no deep mastery of Docker best-practices to build docker images and so that would be cool if a tool can keep care of building optimized Docker images for us.
2. Solution by using Jib
Jib is an open source tool that supports above need and builds optimized Docker and OCI images for your Java application without a docker Daemon. It is available as a Maven and Gradle and as a Java library. In common we can say that Jib is a compiler for containers.
In addition to the above advantages Jib also provides these facilities:
- Jib separates your application into multiple layers, but how? : by separating dependencies from classes and not needing to rebuild entire Docker image. So Jib speed up your build process very effectively by rebuilding only the layers that changed.
- Jib can also automate the process of pushing Docker images into your desired registry.
- Not need to care about Docker best practices and following them across the team.
- A docker daemon is not needed by using Jib, but optionally you can configure it to use a Docker daemon.
3. Sample java project
You can recruit any Java-Maven project that you prefer to carry out with this tutorial, but a recommendation would be using this Github repository as a very simple hello world Spring-Boot project.
4. pom.xml
The only thing that we must do is adding and configuring Jib Maven plugin In pom.xml file in our project. So at the begining below listing would be a proper point to make your hand wet:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
... <properties> <java.version>11</java.version> <docker.name>amirkeshavarz/hellomavendocker</docker.name> <docker.REGISTRY_USERNAME>your-dockerhub-username</docker.REGISTRY_USERNAME> <docker.REGISTRY_PASSWORD>your-dockerhub-password</docker.REGISTRY_PASSWORD> </properties> ... <build> <plugins> ... <plugin> <groupId>com.google.cloud.tools</groupId> <artifactId>jib-maven-plugin</artifactId> <version>2.8.0</version> <configuration> <from> <image>openjdk:17-jdk-alpine</image> <auth> <username>${docker.REGISTRY_USERNAME}</username> <password>${docker.REGISTRY_PASSWORD}</password> </auth> </from> <to> <image>${docker.name}</image> <auth> <username>${docker.REGISTRY_USERNAME}</username> <password>${docker.REGISTRY_PASSWORD}</password> </auth> </to> <container> <environment></environment> <ports> <port>8080</port> </ports> <creationTime>USE_CURRENT_TIMESTAMP</creationTime> </container> </configuration> <executions> <execution> <phase>package</phase> <goals> <goal>build</goal> </goals> </execution> </executions> </plugin> </plugins> </build> ... |
Above listing is self-descriptive as well, and you can focus on its content for learning the concept but please take into these conciderations:
- Replace your Docker hub credentials at line 4, 5, 6
- <from> tag determines the base image which our final Docker image would be built from it
- <to> tag is mandatory and determines name of our targeted Docker image
- in <execution> part determined that running "mvn package" command will run also "mvn jib:build" goal. And so we now do have building our desired Docker image in our packaging workflow as an automatic operation.
5. jib:build vs jib:dockerBuild
As mentioned above we used jib:build in our pom.xml. But we should know that there is another goal named jib:dockerBuild which uses the Docker daemon in the hosting machine instead of ignoring it. This is important because in some situations we need this option. For example in next sections of this article you would see that we want have our built Docker image on our Docker daemon to see its history.
So we can also use dockerBuild goal instead of build goal in line 43 of above pom.xml.
6. Build Docker image
Now everything is done and running each of below commands would cause Docker image to be built and even pushed into Docker hub:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#### # Each of these three commands do build and push project Docker image ### # simplest goal sudo mvn jib:build # using Docker daemon sudo mvn jib:dockerBuild # package phase based on above pom.xml execution phrase sudo mvn package ### |
7. Docker image history
If we use jib:dockerBuild then we can see the Docker image in our daemon. Listing below shows running this purpose:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#### # cd to our project root cd ~/myprojectpath # build docker image sudo mvn jib:dockerBuild --- ... running output removed from here ... --- # list images of our Docker daemon sudo docker image ls -a --- REPOSITORY TAG IMAGE ID CREATED SIZE amirkeshavarz/hellomavendocker latest 8efc0437c190 About a minute ago 343MB ---- ### |
Now lets see what happend about Docker image layers as an optimization in building Docker image. For this pupose we ran below command:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#### # show history of our Docker image sudo docker image history amirkeshavarz/hellomavendocker --- IMAGE CREATED CREATED BY SIZE COMMENT 8efc0437c190 4 minutes ago jib-maven-plugin:2.8.0 1.38kB classes <missing> 4 minutes ago jib-maven-plugin:2.8.0 17B resources <missing> 4 minutes ago jib-maven-plugin:2.8.0 16.9MB dependencies <missing> 8 days ago /bin/sh -c #(nop) CMD ["jshell"] 0B <missing> 8 days ago /bin/sh -c set -eux; arch="$(apk --print-a… 318MB <missing> 8 days ago /bin/sh -c #(nop) ENV JAVA_VERSION=17-ea+14 0B <missing> 8 days ago /bin/sh -c #(nop) ENV PATH=/opt/openjdk-17/… 0B <missing> 8 days ago /bin/sh -c #(nop) ENV JAVA_HOME=/opt/openjd… 0B <missing> 8 days ago /bin/sh -c apk add --no-cache java-cacerts 2.38MB <missing> 8 days ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B <missing> 8 days ago /bin/sh -c #(nop) ADD file:7119167b56ff1228b… 5.61MB ---- ### |
That's great that we can see there are three images CREATED BY jib-maven-plugin and those COMMENTs shows us that there are three different layes for classes, resources, and dependencies. Now suppose that you change a line of your source code and rebuild your project, in this situation you would see that the build process will run very speedy, because the only layer that would be changed is the classes layer. So it will be the same if you only change in your resources or dependencies. And this is a great optimization without needing our team to be aware about docker image layers.
8. Next
In next article we will learn how to build smaller Docker images for our Java projects.