Home > Net >  Rust docker image with small binary
Rust docker image with small binary

Time:07-05

I am trying to dockerise a simple server written in Rust using Rocket. I managed to get a simple image working but found the image size to be massive. When trying to shrink it I have tried two different things, both seems to result in the same issue. docker exec <name> exited with code 126 It seems like the arbdata.exe file is not being copied across properly.

working Dockerfile (big image)

FROM rust

COPY . /app

WORKDIR /app

RUN cargo build --release

CMD ["./target/release/arb_data"]

first attempt at smaller image

FROM rust as builder
WORKDIR /app
COPY ./ /app
RUN cargo build --release
# gcr.io/distroless/cc-debian11
# rust:slim-buster
# FROM gcr.io/distroless/cc
FROM gcr.io/distroless/cc
COPY --from=builder /app/target/release/arb_data .
CMD ["./arb_data"]

second attempt at a smaller image

ARG BINARY_NAME_DEFAULT=arbdata

FROM clux/muslrust:stable as builder
RUN groupadd -g 10001 -r dockergrp && useradd -r -g dockergrp -u 10001 dockeruser
ARG BINARY_NAME_DEFAULT
ENV BINARY_NAME=$BINARY_NAME_DEFAULT

# Build the project with target x86_64-unknown-linux-musl

# Build dummy main with the project's Cargo lock and toml
# This is a docker trick in order to avoid downloading and building 
# dependencies when lock and toml not is modified.
COPY Cargo.lock .
COPY Cargo.toml .
RUN mkdir src \
    && echo "fn main() {print!(\"Dummy main\");} // dummy file" > src/main.rs
RUN set -x && cargo build --target x86_64-unknown-linux-musl --release
RUN ["/bin/bash", "-c", "set -x && rm target/x86_64-unknown-linux-musl/release/deps/${BINARY_NAME//-/_}*"]

# Now add the rest of the project and build the real main
COPY src ./src
RUN set -x && cargo build --target x86_64-unknown-linux-musl --release
RUN mkdir -p /build-out
RUN set -x && cp target/x86_64-unknown-linux-musl/release/$BINARY_NAME /build-out/

# Create a minimal docker image 
FROM scratch

COPY --from=0 /etc/passwd /etc/passwd
USER dockeruser

ARG BINARY_NAME_DEFAULT
ENV BINARY_NAME=$BINARY_NAME_DEFAULT

ENV RUST_LOG="error,$BINARY_NAME=info"
COPY --from=builder /build-out/$BINARY_NAME /

# Start with an execution list (there is no sh in a scratch image)
# No shell => no variable expansion, |, <, >, etc 
# Hard coded start command
CMD ["/arbdata"]

here is a link to the github repo with the working api.

CodePudding user response:

Rust docker image on ubuntu, you can't run executable from ubuntu on alpine. Try:

FROM rust
COPY . /app
WORKDIR /app
RUN cargo build --release
FROM glibc
COPY --from=builder /app/target/release/arb_data /
CMD ["/arb_data"]

Alternatively, compile and built on musl, we have alpine rust image https://registry.hub.docker.com/_/rust/ :

FROM rust:alpine
COPY . /app
WORKDIR /app
RUN cargo build --release
FROM alpine
COPY --from=builder /app/target/release/arb_data /
CMD ["/arb_data"]

You may want to read https://en.wikipedia.org/wiki/Glibc, https://en.wikipedia.org/wiki/Musl, Why can't I run a C program built on alpine on ubuntu?, Why can't I execute binary copied into a container?, https://en.wikipedia.org/wiki/Dynamic_linker, What is the role of libc(glibc) in our linux app? etc.

You could also statically link C runtime, in which case, you can scratch. How to generate statically linked executables? .

CodePudding user response:

When you want to use a docker image without any libc available, you'll need to statically link your application. This can easily be done like this:

FROM rust:slim AS builder

ENV TARGET x86_64-unknown-linux-musl
RUN rustup target add "$TARGET"

# copy all your source files ...

RUN cargo build --release --locked --target "$TARGET"

# and then copy it to an empty docker image
FROM scratch
COPY --from=builder /path/to/binary/arb_data /bin/arb_data
RUN ["/bin/arb_data"]

You can find a full example here: https://github.com/msrd0/docker-element/blob/main/Dockerfile

I recommend you do not use rust:alpine image. It contains a broken version of the rust compiler that refuses dynamic linking not just for the final version but during the entire build process, resulting in the inability to use procedural macros (like serde_derive).

  • Related