Sunday 23 September 2018

Cleaning up old Docker images in Jenkins

When using Jenkins for building our Docker images we will start suffering problems with the high disk usage because the images are kept stored and they are not deleted by default. The simplest solution would be to delete the image from disk as part of the Jenkins job/pipeline, once the image has been pushed to our Docker Registry.

The major problem of this approach is that on every build triggered, Docker will have to build the new image from scratch and it will not be able to leverage on the Docker layer caching. As you know, when building a new image each instruction within the Dockerfile is examined, and Docker looks for an existing image in its cache that it can reuse, rather than creating a new (duplicate) image. This implies that the building of new images will probably be much slower. So ideally we would like to keep on disk at least the latest version of our image, so that Docker can reuse previous created layers for building the new image quickly.

As an example, suppose we have a job/pipeline in Jenkins for building our images called "myDockerImage", using the Jenkins build number as image tags.

$ docker images
REPOSITORY                TAG                   IMAGE ID            CREATED             SIZE
myDockerImage             456                   1fdcfbbb6e95        2 days ago          1.2GB
myDockerImage             455                   e7deb97e4677        2 weeks ago         1.2GB
myDockerImage             454                   c4297a8dc099        2 weeks ago         1.2GB
myDockerImage             453                   36f69b5805d9        2 weeks ago         1.2GB
...                       ...                   ...                 ...                 ...
centos                    6.8                   d63aca2e7714        6 weeks ago         195MB
nginx                     1.15.2-alpine         36f3464a2197        8 weeks ago         18.6MB
postgres                  alpine                e6c5e6a76255        8 months ago        38.2MB
rabbitmq                  3.6.14-alpine         5a9e735a5ad8        8 months ago        38.9MB
openjdk                   8u151-jre-alpine3.7   b1bd879ca9b3        8 months ago        82MB
mysql                     5.7                   c356247174ed        2 months ago        372MB
varnish                   latest                ec0406c252f9        3 months ago        161MB
alpine                    3.7                   3fd9065eaf02        8 months ago        4.15MB
ruby                      2.3.1                 ffe8239a147c        22 months ago       730MB

As you can see, we have other images stored as well. Now, how could we delete all images of "myDockerImage" except the one we have just created? Let's take a look to the different options to the "docker images" command:

$ docker images --help

Usage: docker images [OPTIONS] [REPOSITORY[:TAG]]

List images

Options:
  -a, --all             Show all images (default hides intermediate images)
      --digests         Show digests
  -f, --filter filter   Filter output based on conditions provided
      --format string   Pretty-print images using a Go template
      --no-trunc        Don't truncate output
  -q, --quiet           Only show numeric IDs

The first step would be to list only the desired image (myDockerImage):

$ docker images myDockerImage
REPOSITORY                TAG                   IMAGE ID            CREATED             SIZE
myDockerImage             456                   1fdcfbbb6e95        2 days ago          1.2GB
myDockerImage             455                   e7deb97e4677        2 weeks ago         1.2GB
myDockerImage             454                   c4297a8dc099        2 weeks ago         1.2GB
myDockerImage             453                   36f69b5805d9        2 weeks ago         1.2GB
...                       ...                   ...                 ...                 ...

Then we could use the filter option (-f) for listing only the images that were built "before" the one we have just built:

$ docker images --filter "before=myDockerImage:456" myDockerImage
REPOSITORY                TAG                   IMAGE ID            CREATED             SIZE
myDockerImage             455                   e7deb97e4677        2 weeks ago         1.2GB
myDockerImage             454                   c4297a8dc099        2 weeks ago         1.2GB
myDockerImage             453                   36f69b5805d9        2 weeks ago         1.2GB
...                       ...                   ...                 ...                 ...

Then we will use the option -q for listing only the numeric IDs, and the command "docker rmi" for deleting the images in the list.
$ docker rmi $(docker images -q --filter "before=myDockerImage:456" myDockerImage)

Thursday 17 May 2018

How to load iframes faster

As you know, an iframe is an HTML tag that specifies an "inline frame" and is used to embed another document within the current HTML document. The two documents, the main one and the iframe, are independent and both are treated as complete documents, instead of treating one as part of the other.

Iframes are quite useful for embedding third party content, ads and widgets in our pages, because we can be sure that they are not going to interfere with our main page.

Using iframes has many advantages but also many disadvantages, such as their "special" behaviour for loading their content.

For example, let's take a look to the following simple HTML page:

<html>
<head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.5/angular.js"></script>
</head>

<body>
    <h1>Main Page: Example 1</h1>

    <iframe id="myiframe" src="https://www.ernestojpg.com" width="90%" height="90%"
            style="margin:auto;display:block">
    </iframe>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/ng2-bootstrap/1.6.2/ng2-bootstrap.umd.js"></script>

    <h1>Footer</h1>

</body>
</html>

As you can see, in the example above, we load two big javascript files in different parts of the document: angular.js in the page head, and ng2-bootstrap.umd.js at the end of the page body. We also embed an iframe with the content of this blog just before loading the second javascript file.

You can execute this example page here.

How do you think the three external components/dependencies (javascript1, iframe, and javacript2) are going to be loaded by the browser? If we load the example above using, for example, the Google Chrome's Developer Tools we will see something like this:


Surprisingly, the browser loads the two javascript files first, and when they are completely loaded, it starts loading the iframe document. Then when the iframe document is loaded, its dependencies are loaded as well.

Obviously, this is not the most efficient way of loading our page. If we have to load many resources within the main page (images, javascript, css, etc), the loading of the iframe will be delayed for a long time.

Ideally, we would like to start loading the iframe at the same time as the other page dependencies. But, how could we achieve that?

Fortunately there is a small trick that we could apply for achieving it: In general, an iframe will start loading its content if there is no other pending resource to load at that moment.

For this reason, making the following simple change in our code we could improve the page loading time dramatically:

<html>
<head>
</head>

<body>
    <h1>Main Page: Example 2</h1>

    <iframe id="myiframe" src="https://www.ernestojpg.com" width="90%" height="90%"
            style="margin:auto;display:block">
    </iframe>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.5/angular.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/ng2-bootstrap/1.6.2/ng2-bootstrap.umd.js"></script>

    <h1>Footer</h1>

</body>
</html>

As you can see in the code above, we have moved the reference to the first javascript file from the <head> section to the <body> section, just after the iframe. In that way, the iframe will be the first external resource to load within the page.

You can execute this example page here.

Let's have a look to how the page is loaded now in the browser:


As you can see, now the two javascript files and the iframe document are loaded practically in parallel!