Creating a minimal Rust Docker image


#1

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!


#2

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 ‘&&’?


#3

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.


#4

@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.

#5

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


#6

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.


#7

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.


#8

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.


#9

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"


#10

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


#11

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

Let me know how it goes


#12

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


#14

Fixed in https://github.com/frewsxcv/docker-rust-alpine/pull/2


#15

I can build an empty project now. :slight_smile:

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


#16

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


#17

FYI, if anyone is using this image, it now lives at https://hub.docker.com/r/corey/rust-alpine/ instead of corey/test-rust-alpine