Create Fast and Easy Docker Images With Jib

In this post, we are going to take a look at Jib, a tool from Google in order to create Docker images in an easy and fast way. No need to create a Docker file, no need to install a Docker daemon, Jib just runs out-of-the-box.

1. Introduction

Up until now, we have been using the dockerfile-maven-plugin from Spotify in order to build and push our Docker images. This requires us to write a Docker file, according to best practices, to install a Docker daemon and to add the plugin to our build process. Jib will provide us a more easy way to create our Docker images. We only need to add and configure the Maven plugin and that is about it. Of course, we only believe this when we have tried it ourselves, and that is exactly what we are going to do.

We will create a simple Spring Boot application, containerize it with Jib Maven plugin and push it to Docker Hub. Next, we will pull the image and run the Docker container. The sources are available at GitHub.

We are using:

More information about Jib can be found at the Google Cloud Platform Blog and at GitHub.

You might also want to read: Publish Docker Images on a Private Nexus Repository Using Jib Maven Plugin

2. Create the Application

First, we will create a simple Spring Boot application. We add the Spring Actuator and Spring Web MVC dependencies to our pom. Spring Actuator will provide us the means to add health checks.

XML
 




xxxxxxxxxx
1


 
1
<dependency>
2
  <groupId>org.springframework.boot</groupId>
3
  <artifactId>spring-boot-starter-actuator</artifactId>
4
</dependency>
5
<dependency>
6
  <groupId>org.springframework.boot</groupId>
7
  <artifactId>spring-boot-starter-web</artifactId>
8
</dependency>



Our application consists out of a Rest controller which returns a hello message and the address of the machine.

Java
 




xxxxxxxxxx
1
16


 
1
@RestController
2
public class HelloController {
3
 
4
    @RequestMapping("/hello")
5
    public String hello() {
6
        StringBuilder message = new StringBuilder("Hello Jib Maven Plugin!");
7
        try {
8
            InetAddress ip = InetAddress.getLocalHost();
9
            message.append(" From host: " + ip);
10
        } catch (UnknownHostException e) {
11
            e.printStackTrace();
12
        }
13
        return message.toString();
14
    }
15
 
16
}



Run the application locally:

Shell
 




xxxxxxxxxx
1


 
1
$ mvn spring-boot:run



After successful startup, we invoke the URL http://localhost:8080/hello which returns us the following message:

Shell
 




xxxxxxxxxx
1


 
1
Hello Jib Maven Plugin! From host: gunter-Latitude-5590/127.0.1.1



3. Setup Jib and Docker Hub

In this section, we will add the Jib Maven plugin and ensure that we have a successful connection to Docker Hub Registry. It has been quite a struggle to get this working properly, mainly due to a lack of documentation. The official Jib documentation is quite vague about secure authentication methods. Most of the examples consist of adding plain text credentials to the pom or to the Maven settings file. But that is not what we want. We want a secure way of connecting to Docker Hub by means of a Docker Credential Helper.

In order to test the connection, we add the Jib Maven plugin to our pom and configure it in order to retrieve a base image and to push this image to Docker Hub.

XML
 




xxxxxxxxxx
1
15


 
1
<plugin>
2
  <groupId>com.google.cloud.tools</groupId>
3
  <artifactId>jib-maven-plugin</artifactId>
4
  <version>1.8.0</version>
5
  <configuration>
6
    <!-- openjdk:11.0.5-jre -->
7
    <from>
8
      <image>openjdk:11.0.5-jre</image>
9
    </from>
10
    <to>
11
      <image>docker.io/${docker.image.prefix}/${project.artifactId}</image>
12
      <credHelper>pass</credHelper>
13
    </to>
14
  </configuration>
15
</plugin>



The from tag contains our base image, just like the FROM statement in a Docker file. The to tag contains the image we want to push. The ${docker.image.prefix} is set to mydeveloperplanet (our Docker Hub account), you will need to change this to your own account. The ${project.artifactId} contains the version 1.0-SNAPSHOT. In order to make use of a Credential Helper, we set the tag credHelper to pass.

Before starting, we need to set up a GPG key if you do not already have one, see also the Ubuntu help pages.

Shell
 




xxxxxxxxxx
1


 
1
$ gpg --gen-key



For ease of use, you can add the generated key to your profile as an environment variable. Add the following line to your .profile where you replace Your_GPG_Key with your key.

Shell
 




xxxxxxxxxx
1


 
1
export GPGKEY=Your_GPG_Key



Source your .profile in order to make the environment variable available.

Shell
 




xxxxxxxxxx
1


 
1
$ source .profile



You can also choose to send your key to the Ubuntu keyserver, but it is not necessary in order to execute the next steps.

Shell
 




xxxxxxxxxx
1


 
1
$ gpg --send-keys --keyserver keyserver.ubuntu.com $GPGKEY



Install pass and initialize a password store with your GPG key.

Shell
 




xxxxxxxxxx
1


 
1
$ sudo apt install pass
2
$ pass init Your_GPG_Key
3
 
4
mkdir: created directory '/home/gunter/.password-store/'
5
Password store initialized for My Password Storage Key



Next thing to do, is to download and unpack the Docker Credential Helper and make the file executable.

Shell
 




xxxxxxxxxx
1


 
1
$ wget https://github.com/docker/docker-credential-helpers/releases/download/v0.6.3/docker-credential-pass-v0.6.3-amd64.tar.gz
2
$ tar xvzf docker-credential-pass-v0.6.3-amd64.tar.gz
3
$ mv docker-credential-pass /usr/bin
4
$ chmod +x docker-credential-pass



The Docker Credential Helper needs to be configured correctly and this is where documentation falls short.

Create a config.json file with the following content. In the documentation it is stated to add the contents { "credStore": "pass" }, but with this configuration, Jib will not be able to connect to the Docker Hub Registry. We found the following issue where the use of credStore is not supported anymore for the Google Cloud Registry.

JSON
 




xxxxxxxxxx
1


 
1
"credHelpers": {
2
"https://index.docker.io/v1": "pass"
3
}



Initialize the Docker Credential Helper. Enter the password pass is initialized when being asked for a password.

Shell
 




xxxxxxxxxx
1


 
1
$ pass insert docker-credential-helpers/docker-pass-initialized-check
2
mkdir: created directory '/home/gunter/.password-store/docker-credential-helpers'
3
Enter password for docker-credential-helpers/docker-pass-initialized-check:
4
Retype password for docker-credential-helpers/docker-pass-initialized-check:



Check whether the password is correctly set:

Shell
 




xxxxxxxxxx
1


 
1
$ pass show docker-credential-helpers/docker-pass-initialized-check
2
pass is initialized



Login with your Docker credentials. A warning is raised saying that your password is stored unencrypted in the file config.json. We could not figure out why this warning is being raised, because the credentials are stored encrypted in the config.json file.

Shell
 




xxxxxxxxxx
1


 
1
$ docker login
2
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
3
Username: your_user_name
4
Password: 
5
WARNING! Your password will be stored unencrypted in /home/gunter/.docker/config.json.
6
Configure a credential helper to remove this warning. See
7
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
8
 
9
Login Succeeded



From now on, it is possible to execute docker login without the need for entering your credentials.

Shell
 




xxxxxxxxxx
1


 
1
$ docker login
2
Authenticating with existing credentials...
3
WARNING! Your password will be stored unencrypted in /home/gunter/.docker/config.json.
4
Configure a credential helper to remove this warning. See
5
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
6
 
7
Login Succeeded



You can logout again with docker logout:

Shell
 




xxxxxxxxxx
1


 
1
$ docker logout
2
Removing login credentials for https://index.docker.io/v1/



Ensure that you are logged in again and run the Maven Jib build command:

Shell
 




xxxxxxxxxx
1


 
1
$ mvn compile jib:build



The image is successfully built and pushed to Docker Hub. Two warnings are raised during the build:

Base image 'openjdk:11.0.5-jre' does not use a specific image digest - build may not be reproducible
This can be easily be solved by replacing openjdk:11.0.5-jre with the sha256 key for the base image openjdk@sha256:b3e19d27caa8249aad6f90c6e987943d03e915bbf3a66bc1b7f994a4fed668f6

The credential helper (docker-credential-pass) has nothing for server URL: https://index.docker.io/v1
This is a strange warning because the credentials for this URL are resolved and used for pushing the image.

4. Configure Jib for Our Application

Now that we have configured the authentication in a secure way, we can continue with configuring the Jib Maven plugin for our application. We add a tag to our image and specifiy the main class.

XML
 




xxxxxxxxxx
1
10


 
1
<to>
2
  <image>docker.io/${docker.image.prefix}/${project.artifactId}</image>
3
  <credHelper>pass</credHelper>
4
  <tags>
5
    <tag>${project.version}</tag>
6
  </tags>
7
</to>
8
<container>
9
  <mainClass>com.mydeveloperplanet.myjibplanet.MyJibPlanetApplication</mainClass>
10
</container>



Do not add the tag format with value OCI to your container configuration. Docker Hub does not support yet OCI completely and an error message will be shown ‘An error occurred while loading the tags. Try reloading the page’.

Build the image again and pull the Docker image:

Shell
 




xxxxxxxxxx
1
15


 
1
$ docker pull mydeveloperplanet/myjibplanet
2
Using default tag: latest
3
latest: Pulling from mydeveloperplanet/myjibplanet
4
844c33c7e6ea: Pull complete 
5
ada5d61ae65d: Pull complete 
6
f8427fdf4292: Pull complete 
7
a5217f27a28f: Pull complete 
8
176e83ebae4f: Pull complete 
9
800204250483: Pull complete 
10
492e142ab90b: Pull complete 
11
7c8e6198cd4b: Pull complete 
12
c49bb7f02774: Pull complete 
13
Digest: sha256:b7144bfdf6ee47d6b38914a84789ef9f7e2117320080b28ce39c385ee399a0c8
14
Status: Downloaded newer image for mydeveloperplanet/myjibplanet:latest
15
docker.io/mydeveloperplanet/myjibplanet:latest



Run the image and map it to port 8080:

Shell
 




xxxxxxxxxx
1


 
1
$ docker run -p 127.0.0.1:8080:8080/tcp mydeveloperplanet/myjibplanet
2
...
3
2019-12-25 09:57:13.196 INFO 1 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
4
2019-12-25 09:57:13.205 INFO 1 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 9 ms



List the Docker containers:

Shell
 




xxxxxxxxxx
1


 
1
$ docker ps
2
CONTAINER ID    IMAGE                           COMMAND                  CREATED                 STATUS           PORTS                      NAMES
3
c05e431b0bd1    mydeveloperplanet/myjibplanet   "java -cp /app/resou…"   13 seconds ago    Up 12 seconds    127.0.0.1:8080->8080/tcp   recursing_meninsky



We only need to retrieve the IP address of our Docker container:

JSON
 




xxxxxxxxxx
1


 
1
$ docker inspect c05e431b0bd1
2
...
3
  "NetworkSettings": {
4
    ...
5
    "IPAddress": "172.17.0.2",
6
  ...
7
  }
8
...



The URL of our application can now be invoked with http://172.17.0.2:8080/hello.

This returns us the welcome message:

Shell
 




xxxxxxxxxx
1


 
1
Hello Jib Maven Plugin! From host: c05e431b0bd1/172.17.0.2



We have one more issue to solve: our application runs as root in the Docker container. This is not something we want because of security. First, we will check which users are available in the Docker container:

Shell
 




xxxxxxxxxx
1


 
1
$ docker exec -it -u root c05e431b0bd1 cat /etc/passwd
2
...
3
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
4
...



The Docker container contains a user nobody, which is the one we can use for running our application. Add the user tag to the pom:

XML
 




xxxxxxxxxx
1


 
1
<container>
2
  <mainClass>com.mydeveloperplanet.myjibplanet.MyJibPlanetApplication</mainClass>
3
  <user>nobody</user>
4
</container>



Build the image, pull it and run it. Verify with docker inspect whether nobody is used as user.

JSON
 




xxxxxxxxxx
1


 
1
...
2
  "Config": {
3
    "Hostname": "76b3afaca3af",
4
    "Domainname": "",
5
    "User": "nobody",
6
    ...
7
  }
8
...



In our pom, we also added Spring Actuator. There is no option to add a Docker healthcheck via Jib. This must be resolved with liveness probe and readiness probe in the Kubernetes configuration, see also this issue.

5. Conclusion

We experimented with Jib Maven plugin in order to create our Docker images. Configuring the credentials for Docker Hub Registry was a real struggle, but once this was set up, the plugin was really easy to use. Besides that, no Docker daemon is needed and you do not need to write a separate Docker file. Last but not least, it is really fast. We will definitely be using this plugin in the near future.

 

 

 

 

Top