Docker image sizes

The way I test that is by using time in run.sh. So for the Python test runner, I’d do:

- python3 bin/run.py "$@"
+ time python3 bin/run.py "$@"

And then use ./bin/run-in-docker on a Python solution.

VB test runner also shrunk to about 33% Reduce image size by ErikSchierboom · Pull Request #35 · exercism/vbnet-test-runner · GitHub

BTW If you’re on a *Nix system, you can use the following command from within the root of your tooling repo to build the Docker image and then print its size:

TAG=exercism/$(basename $PWD) docker build --quiet --tag $TAG . &> /dev/null && docker 
images $TAG --format '{{.Size}}'
1 Like

People might also want to try dive, which shows where the image size comes from.

4 Likes

To be able to properly compare measurements, what was the command you used to check image size?

This command seems as if changes decreased the size a lot, even though I am not really a friend of using the alpine based erlang image. On the other hand side, the most common situation in which it causes problems is FFI, which again we do not use on exercism.

I think I will create a PR with my current state.

$ docker image inspect erlang_test_runner:common_test | jq '.[].Size' | numfmt --to-unit=Mi --format='%.2f MiB'
56.60 MiB

I originally ran:

docker image ls --format "{{.Repository}}:{{.Tag}} {{.Size}}" | awk '{if ($2~/GB/) print substr($2, 1, length($2)-2) * 1000 "MB - " $1 ; else print $2 " - " $1 }' | sed '/^0/d' | sort -n | sed -E -e 's/:latest//'

However, docker images $TAG --format '{{.Size}} should work fine locally.

1 Like

Thats formatting with a thousand as base for the prefixes… So ± rounding errors I am in the same range as that command.

Having a look at the Rust image, it contains two toolchains, stable and nightly. Since nightly is needed anyway for json test output, we might get rid of the stable toolchain. That should bring it down from 2GB to around 1.5 - it’s a start. I’ll open an issue for myself on the repo.

Thanks @ee7-1282 for the tip, dive is a handy tool!

I would suggest taking a look at the libraries. At the moment the rust docker image has like 600-700 MB of just libraries. Many of them are not useful for solving exercises. I suggest making a whitelist of libraries instead of downloading libraries from a github repo that lists development crates (which has things like git, XML parsing, json parsing, etc…)

1 Like

I agree with the suggestion by @Meatball. Let’s try to come up with a whitelist of crates.

1 Like

Thanks, I noted it in my issue as well.

I think a good idea may be to make a forum post and take suggestions and just checking that the libary may resably be used within an exercise and then add it to the list.

There already is a whitelist: https://github.com/exercism/rust-test-runner/blob/d74d3a58667f37bdf9d63691d61a4f5110daff85/local-registry/supported_crates

Yeah but I mean the storage uses of these libraries are quite big, and they were just taken by taking the most popular crates on a website.

The list includes template engines, uuid libraries, database libraries, git libraries, libraries for parsing images and working with them, benchmarking tools, and other various types of “development” libaries.

My point is that someone makes a curated list of libraries that actually makes sense.

1 Like

Yeah, at a glance, things like rusqlite could probably go and save us some $$$ :slight_smile:

In the PR that introduced that file it is reported that the top 100 packages together make for 17 MB; the file specifies 232 packages, so assuming the trend continues that would make for only 40 MB.

It seems implausible that the length of that file is behind the Rust image being so big.

Alright did some further digging, I actually counted in the add nightly into the 700 MB, so the rust libraries stand for 90 MB.

image
(top one without and bottom one with).

The reason why I suspected why it used more was that it took around 5 minutes to compile, but that is actually the library that installed the other libraries. No idea why it has so many dependencies. Any way continuing… that tool had similar dependencies to that file. And I seem to have removed that nightly line under testing somewhere.

It is the nightly part which is 600 MB.

An easy win would be switching to slim version which reduces the image by 600, if nightly could go away that is another 600 MB.

Nightly cannot go away, but stable probably can, which is the same win in size reduction.

I tried doing rustup toolchain uninstall stable but that did nothing but that may be the wrong way?

Turns out if the version is pinned, it is not called stable. The correct command would’ve been rustup toolchain uninstall 1.71.0 (or whatever the pinned version is).

However, images don’t shrink with later layers, they only grow. FROM rust:1.70.0 AS test is a layer that already contains the stable toolchain, later layers cannot reduce the image size by removing it.

The only way to not bloat the image with two toolchains is therefore to only install nightly in the first place. There are base images for nightly out there. But they don’t allow to pin the exact version, since that would create a new base image every day.

An approach I’ve tried and which worked is to copy the source of the official rust images and replace the (stable) version with a nightly version pinned to a specific date. It seems a little barbaric… but I think the official sources should be trustworthy from a security perspective, since we have been depending on them anyway with FROM rust:1.70.0 AS test. And the maintenance burden isn’t any higher either, there is still only one place to update the rust version (well marked with a comment). That being said, I’d like some feedback if there is something bad about this approach I haven’t considered.

Combined with switching to alpine, that reduces the image size to 947 MB. I believe there’s not much more to gain here, it’s almost only stuff we need:

size thing
664 MB nightly toolchain
93 MB local registry of preselected dependencies
85 MB gcc stuff
~100 MB rest, including misc. binaries we mostly need

PR is here, but CI is not yet passing…