Rust smaller docker images

Hello!

a rust rookie here, I'm trying to generate a smaller docker images for a rust projects. I want to be able to do code changes from a shared volume to make development and reload of these projects much faster or automated. I followed a couple of the guides but got errors. Is there a working guide on how to create such images?

To be clear, you’re trying to create a “development” time image, not the “ship it to production” image?

Can you share your dockerfile?

yes that's correct, I have multiple projects that I want to create containers for and manage them with a docker-compose file. This is my docker file, I'm using a demo app to make this work first:

FROM rust AS build

WORKDIR /usr/src

# Download the target for static linking.
RUN rustup target add x86_64-unknown-linux-musl

RUN USER=root cargo new my-app
WORKDIR /usr/src/my-app
COPY Cargo.toml Cargo.lock ./
RUN cargo build --release

# Copy the source and build the application.
COPY src ./src
RUN cargo install --target x86_64-unknown-linux-musl --path .

# Copy the statically-linked binary into a scratch container.
FROM scratch
COPY --from=build /usr/local/cargo/bin/my-app .
USER 1000
CMD ["./my-app"]

the result:

build failed
The command '/bin/sh -c cargo install --target x86_64-unknown-linux-musl --path .' returned a non-zero code: 101

new error now

error occurred: Failed to find tool. Is musl-gcc installed? I couldn't find this as a brew or a cask formula. any idea how to install this on a Mac?

Why on Mac? Don't you need it in the docker?

I'm using my MacBook as a dev machine. I don't have a linux box. I have docker desktop installed with Kubernetes enabled and a VMware image running for the docker machine.

I'm now trying to test creating two rust containers. Trying to figure out how the workflow of dev / build / publish to Kubernetes works!!

I can always run VMware and install docker on the linux virtual to do the development if that resolve some issues. Would that be more ideal?

I thought you want to build inside of docker, so the Mac is irrelevant... Also you are already running docker in a virtualized Linux, that's how docker on Mac and Windows works for non Mac/Windows containers...

yes that's correct this is why I'm reaching out for help. What am I missing?

The build happens inside the “build” Docker container. You likely need to install musl-gcc.

RUN apt update && apt-get install -y musl-gcc && rm -rf /var/lib/apt/lists/*

Note that the last step removes apt files that are not necessary after installation in order to reduce image size. Although, I wouldn’t worry about image size until after you have a working container.

I've been looking into static builds and docker size, you can check my experiences http://zderadicka.eu/static-build-of-rust-executables/

Few generals comments:

  • you can achieve relatively small image with dynamic binary when using alpine
  • for static builds, especially if you have dependencies on C/C++ libraries alpine is better because it has all libraries compiled with Muslim
  • cargo install command is probably additional, just install musl target with rust up and then cargo build with musl target. Or use alpine as noted above cause musl is default target there.

I added this now getting this error

E: Unable to locate package musl-gcc

The command '/bin/sh -c apt-get update && apt-get install -y musl-gcc && rm -rf /var/lib/apt/lists/*' returned a non-zero code: 100

I'll check the link thanks

According to https://packages.debian.org/stretch/amd64/musl-tools/filelist, the musl-gcc compiler is provided by the package musl-tools. You probably want to install that.

this worked but I got multiple 424 MB images

I changed the install to musl-tools and that solved it but now the original error:

error: no binaries are available for install using the selected features

The command '/bin/sh -c cargo install --target x86_64-unknown-linux-musl --path .' returned a non-zero code: 101

This command tells Cargo to look in the current directory for your own project, and install binaries from it in ~/.cargo/bin.

Do you need this? Do you actually have binaries? Do you use required-features to disable them?

I thought the project gets outputted as one binary that would need to be copied to that directory!! isn't that the case? I was following the guide I saw online from a couple of posts!

When you run cargo build (or run/test/bench), the project is built in ./target, and all results sit there.

You never need to call cargo install to build a project, unless you want to actually install the produced binary as a system-wide developer tool living in Cargo's bin directory.

If your project is a library, then cargo install doesn't make any sense and serves no purpose.

my project is an api that runs a web server

If you want to deploy the binary on the server, then copying from ./target to some other "deployment" destination OK-ish. cargo install does basically the same thing.

For slightly better deployment, if you use Debian/Ubuntu, I suggest running cargo deb or cargo deb --install, which will properly install the binary on the system.