Creating a minimal Rust Docker image

I'm interested in the smallest possible Docker image that has an operating system, Rust, and Cargo and (to a certain extent) nothing else. Here's where I'm at currently (on GitHub also):

FROM frolvlad/alpine-glibc:alpine-3.4

ENV RUST_VERSION 1.12.0
ENV RUST_PACKAGE_NAME rust-$RUST_VERSION-x86_64-unknown-linux-gnu
ENV RUST_PACKAGE_SHA256 3a9647123f1f056571d6603e40f21a96162702e1ae4725ee8c2bc9452a87cf5d
ENV RUST_DOWNLOAD_URL https://static.rust-lang.org/dist/$RUST_PACKAGE_NAME.tar.gz

RUN set -ex \
  && apk add --no-cache \
    curl \
  && curl -O $RUST_DOWNLOAD_URL \
  && echo "$RUST_PACKAGE_SHA256  $RUST_PACKAGE_NAME.tar.gz" | sha256sum -c - \
  && tar -xf $RUST_PACKAGE_NAME.tar.gz \
  && ./$RUST_PACKAGE_NAME/install.sh --without=rust-docs \
  && rm -rf \
    $RUST_PACKAGE_NAME \
    $RUST_PACKAGE_NAME.tar.gz

WORKDIR /source

I pushed this to Docker Hub and it indicates it's 119 MB compressed (not sure what compression they do). Is there any way to reduce this further? @eddyb mentioned in IRC that there might be a way to reduce duplication between /usr/local/lib/ and /usr/local/lib/rustlib/x86_64-unknown-linux-gnu/lib/, though I'm not sure how one would do this. It looks like after a fresh install, /usr/local/lib is 228.6 megabytes.

Thanks!

4 Likes

Isn't set -ex useless here? Isn't it normally used to stop a script from running as soon as there's an error and here you already have that effect since you are calling all commands with '&&'?

From my experience with Docker, 119MB already sounds pretty small to me.

With that said, the next thread I'd pull on is figuring out how to build a Docker image that is FROM scratch.

1 Like

@frewsxcv: Did you forget to include the gcc package? The container doesn't seem usable for me without 'cc'.

docker run --rm -e "USER=test" corey/test-rust-alpine sh -c "cargo new --bin t && cd t && cargo run"

     Created binary (application) `t` project
   Compiling t v0.1.0 (file:///source/t)
error: could not exec the linker `cc`: No such file or directory (os error 2)
  |
  = note: "cc" "-Wl,--as-needed" "-Wl,-z,noexecstack" "-m64" "-L" "/usr/local/lib/rustlib/x86_64-unknown-linux-gnu/lib" "/source/t/target/debug/t.0.o" "-o" "/source/t/target/debug/t" "-Wl,--gc-sections" "-pie" "-nodefaultlibs" "-L" "/source/t/target/debug/deps" "-L" "/usr/local/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-Wl,-Bstatic" "-Wl,-Bdynamic" "/usr/local/lib/rustlib/x86_64-unknown-linux-gnu/lib/libstd-40393716.rlib" "/usr/local/lib/rustlib/x86_64-unknown-linux-gnu/lib/libpanic_unwind-40393716.rlib" "/usr/local/lib/rustlib/x86_64-unknown-linux-gnu/lib/libunwind-40393716.rlib" "/usr/local/lib/rustlib/x86_64-unknown-linux-gnu/lib/librand-40393716.rlib" "/usr/local/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcollections-40393716.rlib" "/usr/local/lib/rustlib/x86_64-unknown-linux-gnu/lib/librustc_unicode-40393716.rlib" "/usr/local/lib/rustlib/x86_64-unknown-linux-gnu/lib/liballoc-40393716.rlib" "/usr/local/lib/rustlib/x86_64-unknown-linux-gnu/lib/liballoc_jemalloc-40393716.rlib" "/usr/local/lib/rustlib/x86_64-unknown-linux-gnu/lib/liblibc-40393716.rlib" "/usr/local/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcore-40393716.rlib" "-l" "dl" "-l" "pthread" "-l" "gcc_s" "-l" "pthread" "-l" "c" "-l" "m" "-l" "rt" "-l" "util" "-l" "compiler-rt"

error: aborting due to previous error

error: Could not compile `t`.

To learn more, run the command again with --verbose.

Possibly, I was thinking the same thing. I did it because other Docker images do it. Didn't think too hard about it.

119MB is indeed smaller than most other images, especially Ubuntu and Debian based images which already have a 100MB-200MB base. The base Alpine container is 5MB, so I imagine moving to scratch won't result in many storage victories.

About 'gcc', I'm not sure if it's the right package. With it I have 'cannot find Scrt1.o: No such file or directory'. 'Scrt1.o' is from musl-dev and with it I get: 'undefined reference to `__rawmemchr'' but maybe cc is not using glibc.

Which create are you trying to build? Indeed, there is no gcc or g++ installed, as I was going for absolute minimal. I understand that this is not ideal for most Rust project, but at least initially when I wrote the image, I was going for a no-batteries-included image.

I tried to build a new empty project with: docker run --rm -e "USER=test" corey/test-rust-alpine sh -c "cargo new --bin t && cd t && cargo run"

That does indeed seem problematic. Will look into this now.

Changed the strategy a bit as my previous one turned out to be fallacious:

https://github.com/frewsxcv/docker-rust-alpine/pull/1/files

Let me know how it goes

Oh, my latest changes don't include cargo. I'll need to add that.

Fixed in Include Cargo. by frewsxcv · Pull Request #2 · frewsxcv/docker-rust-alpine · GitHub

I can build an empty project now. :slight_smile:

Do the container still needs curl? Does cargo depend on it?

Probably not, I'll try removing it. Good catch!

FYI, if anyone is using this image, it now lives at Docker Hub instead of corey/test-rust-alpine

1 Like