Automatically reduce Docker container size using DockerSlim

REST Web Service example using Python/Flask

# Prerequisites to complete this example

  • Basic knowledge of Linux commands and a terminal
  • Docker Desktop is installed on your host machine.
  • A text editor or browser to view JSON files

For video instructions click here: Automatically minify container images using DockerSlim (opens new window)

Many tech stacks struggle with large Docker container sizes, and common web frameworks are no exception. Python’s Flask microframework is one of the world’s most popular open source set-ups for RESTful web services and APIs. However, common Python-Flask Docker images can weigh in at close to 1 GB in size.

Slimming an application built with Python-Flask using DockerSlim is easy, automatic, and quick. Here’s a step-by-step tutorial using DockerSlim’s build functionality.

# 1. Download and test DockerSlim

Download DockerSlim from here: https://dockersl.im/ (opens new window)

We developed this example on macOS.

Create a folder and copy the files from the DockerSlim download into the folder. We’ll create one called DockerSlim.

.
├── DockerSlim
| ├── docker-slim
| ├── docker-slim-sensor

If you plan to use DockerSlim frequently, we recommend you add your folder path to the /etc/paths file or otherwise add it to your PATH so you can run the docker-slim command from anywhere.

# /etc/paths
## some paths
/<pathtomydockerslimfolder>/DockerSlim

Restart terminal for the changes to be active.

Otherwise, you’ll need to run your docker-slim commands from the directory where you extracted them. You can type ./docker-slim help to run DockerSlim in this case. We’ll generally assume that you’ve added the the docker-slim binaries to your path.

Run DockerSlim to check if it’s working by typing docker-slim help (or ./docker-slim help if you did not add it to your PATH above).

$ docker-slim help

Output

NAME:
docker-slim - optimize and secure your Docker containers!

USAGE:
docker-slim [global options] command [command options] [arguments...]

VERSION:
darwin|Transformer|1.35.0|6eca3ad331720c31a7e398ffaf002a896dc9a70a|2021-04-15_02:48:03AM

COMMANDS:
build, b Analyzes, profiles and optimizes your container image auto-generating Seccomp and AppArmor security profiles
containerize, c Containerize the target artifacts
convert, k Convert container image
edit, e Edit container image
help, h Show help info
lint, l Analyzes container instructions in Dockerfiles
probe, prb Probe target
profile, p Collects fat image information and generates a fat container report
run, r Run one or more containers
server, s Run as an HTTP server
update, u Updates docker-slim
version, v Shows docker-slim and docker version information
xray, x Shows what's inside of your container image and reverse engineers its Dockerfile

GLOBAL OPTIONS:
--report value command report location (enabled by default; set it to "off" to disable it) (default: "slim.report.json")
--check-version check if the current version is outdated [$DSLIM_CHECK_VERSION]
--debug enable debug logs
--verbose enable info logs
--log-level value set the logging level ('debug', 'info', 'warn' (default), 'error', 'fatal', 'panic') (default: "warn")
--log value log file to store logs
--log-format value set the format used by logs ('text' (default), or 'json') (default: "text")
--tls use TLS
--tls-verify verify TLS
--tls-cert-path value path to TLS cert files
--host value Docker host address
--state-path value DockerSlim state base path
--in-container DockerSlim is running in a container
--archive-state value archive DockerSlim state to the selected Docker volume (default
 volume - docker-slim-state). By default, enabled when DockerSlim is running in a
 container (disabled otherwise). Set it to "off" to disable explicitly.
--no-color disable color output
--version, -v print the version

DockerSlim has an excellent menu-driven command line. To try that, simply run docker-slim with no additional arguments. You can get to the help menu by typing help in the command line at the interactive prompt.

# 2. Prepping a Container to Slim

We’ll use the DockerSlim build command for this exercise and pull one of the DockerSlim example containers. The container houses a simple Python/Flask service. You can pull the container from the dslimexamples repo on DockerHub.

$ docker pull dslimexamples/server-python2-flask-standard 

After the pull command completes, check to see that the image is loaded using the docker images command.

Output

Using default tag: latest
latest: Pulling from dslimexamples/server-python2-flask-standard
741437d97401: Pull complete
34d8874714d7: Pull complete
0a108aa26679: Pull complete
7f0334c36886: Pull complete
49ea0d2b5c48: Pull complete
2f697d8eb8c9: Pull complete
19e57c082018: Pull complete
62a638f5d7ed: Pull complete
5bceaf33fb03: Pull complete
72d2aabda994: Pull complete
9bb059401bdd: Pull complete
d3940358526e: Pull complete
Digest: sha256:0b1ba4d668a86479b927ff522bab220a27ece1c40a1d1dc89bc15b81a44b7f8b
Status: Downloaded newer image for dslimexamples/server-python2-flask-standard:latest
docker.io/dslimexamples/server-python2-flask-standard:latest

# 3. Verifying the output container

A vital step when minifying a container is verifying that the output container from DockerSlim functions correctly. We will use curl to perform a simple before and after functional verification on the example container.

To perform the simple validation test, we first need to run the example container. Run the following command in the terminal window.

$ docker run -it --rm -p 1300:1300 --name dslimflask dslimexamples/server-python2-flask-standard

Output

* Serving Flask app "server" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: on
 * Running on http://0.0.0.0:1300/ (Press CTRL+C to quit)

This command starts the Python/Flask container interactively (see the -it flags) and attaches the container's open port to 1300 on localhost.

Open a second terminal window and use CURL to send a request to the running container. (Note: You will need CURL installed on your local machine and available via your PATH.)

$ curl http://127.0.0.1:1300 

This curl command performs an HTTP GET on the root method of the Python/Flask REST service running in the container we just started. The Python/Flask service will respond by returning a JSON object with a “success” status. The service also logs an access record (see the terminal window in the background of the following image) to the terminal with IP Address, Date, Time, method (GET /HTTP 1.1), and 200 success status code.

This sequence (successfully) performs a basic web service test to verify that the example container functions as expected.

Press CTRL+C in the window where the Python/Flask server container runs to exit the service to stop the container.

# 4. Building the container from DockerSlim

Next, we will minify the container using the DockerSlim build command. The DockerSlim build command takes a container image reference as input. The input container image must be available on the local Docker Desktop in order for DockerSlim to be able to access it.

In the terminal window where you previously started the Python/Flask container, enter the following.

$ docker-slim build --copy-meta-artifacts . dslimexamples/server-python2-flask-standard

Here, the --copy-meta-artifacts . argument will copy several files produced by DockerSlim into the current working directory. You can change . to any path you choose.

# a. DockerSlim Build Steps and Output

As you can see here, the output from the DockerSlim build command is fairly robust.

But what’s actually happening?

The minification process includes performing a combination of static and dynamic analysis of the input container image to determine which files, libraries, executables, etc., are required for the regular operation of the container.

DockerSlim runs the input container using the Docker engine. While the container is running, DockerSlim attempts to understand how the application(s) present in the container function by using a set of built-in probes to inspect various facets of the container.

Here are some of the more pertinent stages of the minification process:

DockerSlim detects the open ports available for the input container and attempts to execute a sequence of web requests and web crawler actions intended to exercise the underlying web service running in the container. It then observes the application dependencies, files, and executables necessary for the application to function in response to probes, recording these dependencies and using this knowledge to compose the output container.

DockerSlim does not modify the original input container. The output of the build command is a new container image produced by DockerSlim (renamed by adding ‘.slim’ to the original container image name) constructed to include only the files necessary for the container's functional operation. This new container contains a subset of the input container’s files. DockerSlim does not inject any new files into the output container. However, DockerSlim flattens the output container layer structure.

Note: DockerSlim has several other built-in methods to test and stimulate a containerized application. DockerSlim also includes several advanced commands and options to perform minification for a wide variety of Linux-based containerized applications.

You can find details about DockerSlim advanced build commands here:

https://github.com/docker-slim/docker-slim#build-command-options (opens new window)

# b. Verifying our results from the slimmed container

When DockerSlim finishes, use docker images to see the new slim container produced by DockerSlim. It can be found with the .slim extension to the original container name.

# Note: The .slim image is 28MB vs 932MB (33X smaller)!

We will verify the ‘.slim’ version of the container using the same curl command we used previously. Run the .slim container by executing the following command in the terminal.

docker run -it --rm -p 1300:1300 --name dslimflask dslimexamples/server-python2-flask-standard.slim 

The Python/Flask service starts normally and produces outputs identical to that of the input container when we ran it previously.

Just like before, we can open a second terminal window to do our CURL test on the running, and now slimmed, container.

curl http://127.0.0.1:1300

The web service returns a success response and logs the access in the terminal as before.

In this case, DockerSlim has produced a much smaller, minified output container that functions equivalently to the original input container.

And just like that, you have a 28 MB container that does all the same things as your previous image.

Looking for similar examples to play around with? Check out a wide variety of different containerized applications in the DockerSlim examples Github (opens new window), and follow our Twitch Live Stream (opens new window), where we review a new container type each week.

Banner photo by Kelly Sikkema (opens new window) on Unsplash (opens new window)

Automatically reduce Docker container size using DockerSlim
Join our community
Subscribe to join our community, connect with like-minded developers, get early access to our products and
learn more about our open source projects.