Lead Image © Maggie Molloy, 123RF.com

Lead Image © Maggie Molloy, 123RF.com

Minifying container images with DockerSlim

Dieting

Article from ADMIN 58/2020
By
DockerSlim minifies your Docker container images up to 30x and adds security, too.

Although not always the first choice, Docker Engine will be used extensively as the default container runtime in Kubernetes and OpenShift on cloud-native infrastructure for some time to come. So, what would you say if I told you that you could automatically reduce the size of your Docker images 30x? Why are smaller container images a good thing? When it comes to containers, less is more: Your containers start quicker, they contain fewer security threats and bugs, they take up less storage, and they take less time to download and upgrade. In other words, it's a really good idea to keep your container images trim.

A sophisticated piece of open source software allows you to do just that; it's called DockerSlim [1]. The clever tool also makes a welcome foray into further securing your container images. The DockerSlim docs declare: "Don't change anything in your Docker container image and minify it by up to 30x (and for compiled languages even more) making it secure too!"

As well as clear and useful docs in the GitHub page README file, you'll also appreciate the slick introduction on a single-page website [2] that has a simple and effective example (before pointing you back to the GitHub repository again). There, you'll also find a useful video that runs through some command-line options to get you started.

In this article, I look at how to automate the slimming of your container images and how to start tweaking the security profiles of your containers, as well.

Trimming the Fat

Before looking at how to get DockerSlim working, I'll show you a few examples on how trim you can expect certain container images to become. The GitHub page has some eye-watering benchmarks that you might expect to achieve with a number of languages. The Python examples in Listing 1 from the README file [1] are impressive, although Node.js, Ruby, Golang, Java, PHP, and other languages can be expected to achieve similar results.

Listing 1

Python Minify Results

from ubuntu:14.04 - 438MB => 16.8MB (minified by 25.99X)
from python:2.7-alpine - 84.3MB => 23.1MB (minified by 3.65X)
from python:2.7.15 - 916MB => 27.5MB (minified by 33.29X)
from centos:7 - 647MB => 23MB (minified by 28.57X)
from centos/python-27-centos7 - 700MB => 24MB (minified by 29.01X)
from python2.7:distroless - 60.7MB => 18.3MB (minified by 3.32X)

Teeny Tiny

Although you can install the binaries required to run DockerSlim directly, for obvious reasons (because you're dealing with container images), you should focus on using the dslim/docker-slim Docker image that's been made available instead.

I'm using Linux Mint 19 (which uses Ubuntu 18.04 LTS under the bonnet), so to install Docker I'll use the command:

$ apt install docker.io

I would recommend, for anything other than testing, to use Docker CE [3], so you get the latest stable release with the latest features.

The super-slight DockerSlim provides lots of options, but to get started, I'll pull down one of the most popular images locally and try and minify it:

$ docker pull dslim/docker-slim
[snip...]
Status: Downloaded newer image for dslim/docker-slim:latest

Now that it is available locally, I'll pull down the nginx image, one of the most popular images in use today. In Listing 2, you can see the two images I pulled from Docker Hub and their disk sizes. In this case, nginx is coming in at an unwieldy 126MB.

Listing 2

Beginning Image Sizes

REPOSITORY         TAG          IMAGE ID          CREATED       SIZE
dslim/docker-slim  latest       2622a843b5f5      3 weeks ago   21.3MB
nginx              latest       231d40e811cd      4 weeks ago   126MB
KE:

Having clicked the link for the latest version of the Nginx Dockerfile [4] on the Docker Hub website [5], I now have the ability to test DockerSlim.

By reputation, Nginx is not only a super-lightweight champion in the world of web servers, but because of the sheer number of downloads it experiences from Docker Hub (and therefore the scrutiny it is under), I would expect it to be extremely trim already. As a result, running it past DockerSlim's watchful eye is a big challenge.

The Dockerfile is too long (103 lines) to display here; I chose the "Raw" option for the Dockerfile on GitHub, copying its contents and then saving it locally to a file called "Dockerfile." From within the same directory as the downloaded Dockerfile (as root user, and not a less-privileged user, out of laziness mostly), the next command should do something interesting (Listing 3):

$ docker run -it --rm -v /var/run/docker.sock:/var/run/docker.sock dslim/docker-slim build nginx

Listing 3

Putting DockerSlim to the Test

docker-slim[build]: info=http.probe message='using default probe'
docker-slim[build]: state=started
docker-slim[build]: info=params target=nginx continue.mode=probe
docker-slim[build]: state=image.inspection.start
docker-slim[build]: info=image id=sha256:231d40e811cd970168fb0c4770f2161aa30b9ba6fe8e68527504df69643aa145 size.bytes=126323486 size.human=126 MB
docker-slim[build]: info=image.stack index=0 name='nginx:latest' id='sha256:231d40e811cd970168fb0c4770f2161aa30b9ba6fe8e68527504df69643aa145'
docker-slim[build]: info=image.exposed_ports list='80'
docker-slim[build]: state=image.inspection.done
docker-slim[build]: state=container.inspection.start
docker-slim[build]: info=container status=created name=dockerslimk_1_20191223125148 id=d87e572be9182325c7d0af5cd672648a3ea13938013fd31c8627cf948d66015b
docker-slim[build]: info=cmd.startmonitor status=sent
docker-slim[build]: info=event.startmonitor.done status=received
docker-slim[build]: info=container name=dockerslimk_1_20191223125148 id=d87e572be9182325c7d0af5cd672648a3ea13938013fd31c8627cf948d66015b target.port.list=[32770] target.port.info=[80/tcp => 0.0.0.0:32770] message='YOU CAN USE THESE PORTS TO INTERACT WITH THE CONTAINER'
docker-slim[build]: state=http.probe.starting message='WAIT FOR HTTP PROBE TO FINISH'
docker-slim[build]: info=continue.after mode=probe message='no input required, execution will resume when HTTP probing is completed'
docker-slim[build]: info=prompt message='waiting for the HTTP probe to finish'
docker-slim[build]: state=http.probe.running
docker-slim[build]: info=http.probe.ports count=1 targets='80'
docker-slim[build]: info=http.probe.commands count=1 commands='GET /'
docker-slim[build]: info=http.probe.call status=200 method=GET target=http://172.17.0.3:80/ attempt=1  time=2020-11-11T12:52:01Z
docker-slim[build]: info=http.probe.summary total=1 failures=0 successful=1
docker-slim[build]: state=http.probe.done
docker-slim[build]: info=event message='HTTP probe is done'
docker-slim[build]: state=container.inspection.finishing
docker-slim[build]: state=container.inspection.artifact.processing
docker-slim[build]: state=container.inspection.done
docker-slim[build]: state=building message='building minified image'
docker-slim[build]: state=completed
docker-slim[build]: info=results status='MINIFIED BY 16.16X [126323486 (126 MB) => 7817018 (7.8 MB)]'
docker-slim[build]: info=results  image.name=nginx.slim image.size='7.8 MB' data=true
docker-slim[build]: info=results  artifacts.location='/bin/.docker-slim-state/images/231d40e811cd970168fb0c4770f2161aa30b9ba6fe8e68527504df69643aa145/artifacts'
docker-slim[build]: info=results  artifacts.report=creport.json
docker-slim[build]: info=results  artifacts.dockerfile.original=Dockerfile.fat
docker-slim[build]: info=results  artifacts.dockerfile.new=Dockerfile
docker-slim[build]: info=results  artifacts.seccomp=nginx-seccomp.json
docker-slim[build]: info=results  artifacts.apparmor=nginx-apparmor-profile
docker-slim[build]: state=done
docker-slim[build]: info=report file='slim.report.json'

Lo and behold, according to Listing 3, the line reporting

MINIFIED BY 16.16X [126323486 (126 MB) => 7817018 (7.8 MB)]

would be a remarkable achievement – if true. As shown in Listing 4, I check the Docker Engine output, which shows further confirmation of the DockerSlim report.

Listing 4

Output of docker images

REPOSITORY         TAG       IMAGE ID         CREATED          SIZE
nginx.slim         latest    3557e77e8e91     5 minutes ago    7.82MB
dslim/docker-slim  latest    2622a843b5f5     3 weeks ago      21.3MB
nginx              latest    231d40e811cd     4 weeks ago      126MB

The remarkable 7.82MB nginx.slim image needs testing now, so I'll fire up a container with that image and see what happens (hit Ctrl+C to kill the container):

$ docker run -it -p80:80 nginx.slim

By pointing my browser at TCP port 80 on localhost, I can see the very welcome and familiar result of running an Nginx instance (Figure 1). I think anybody would agree that the reduction from 126MB to less than 8MB (as seen in Listing 4) is a sight to behold.

Figure 1: The minuscule, super-fit version of Nginx on display uses an image weighing in at a teeny, tiny 7.82MB!

You should test your container images carefully before deploying them into critical environments. The README file talks about debugging and checking the process, so you have some support from the documentation if an image ever misbehaves.

DockerSlim needs to talk to Docker Engine in a specific way; usually using the Unix socket is easiest. Therefore, the command I used to minify my Nginx image contained the -v /var/run/docker.sock:/var/run/docker.sock option. The documentation says:

If you are using the docker-slim container make sure you run it configured with the Docker IPC information, so it can communicate with the Docker daemon. The most common way to do it is by mounting the Docker Unix socket to the docker-slim container.

It goes on to note that some services like GitLab won't expose the socket in the same way, so you can use environment variables such as DOCKER_HOST to get around this, if required. I'm paraphrasing, so check out the docs for more details.

Super Lightweight Champion

Now that you're suitably impressed, I'll look at some of the (many) features that DockerSlim brings to the table, beginning with the DockerSlim output (see Listing 3 again). You'll note that DockerSlim has quite rightly spotted that TCP port 80 is exposed within the Dockerfile – because Nginx is a web server. There are further details on how to interact with DockerSlim when it runs in the docs, because you can connect to a temporary container that's used while DockerSlim weaves its magic trimming down an image to check that things are working as expected.

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy ADMIN Magazine

SINGLE ISSUES
 
SUBSCRIPTIONS
 
TABLET & SMARTPHONE APPS
Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

comments powered by Disqus