I am following the example dockerfile of From Zero to Production
It looks like this to me:
FROM lukemathwalker/cargo-chef:0.1.67-rust-1.78.0-slim-bookworm as chef
WORKDIR /app
RUN apt update && apt install lld clang -y
FROM chef as planner
COPY . .
RUN cargo chef prepare --recipe-path recipe.json
FROM chef as builder
COPY --from=planner /app/recipe.json recipe.json
RUN cargo chef cook --release --recipe-path recipe.json
COPY . .
ENV SQLX_OFFLINE true
RUN cargo build --release --bin zero2prod
FROM debian:bookworm-slim AS runtime
WORKDIR /app
RUN apt-get update -y \
&& apt-get install -y --no-install-recommends openssl ca-certificates \
&& apt-get autoremove -y \
&& apt-get clean -y \
&& rm -rf /var/lib/apt/lists/*
COPY --from=builder /app/target/release/zero2prod zero2prod
COPY configuration configuration
ENV APP_ENVIRONMENT production
ENTRYPOINT ["./zero2prod"]
however, I want to make the image smaller by using a scratch container.
Notice, I am on an ARM (M2) Macbook and want to create docker containers with linux amd64 and aarm64 architecture
The following works:
FROM lukemathwalker/cargo-chef:0.1.67-rust-1.78.0-slim-bookworm as chef
WORKDIR /app
RUN apt update && apt install lld clang -y
FROM chef as planner
COPY . .
RUN cargo chef prepare --recipe-path recipe.json
FROM chef as builder
COPY --from=planner /app/recipe.json recipe.json
RUN cargo chef cook --release --recipe-path recipe.json
COPY . .
ENV SQLX_OFFLINE true
RUN cargo build --release --bin zero2prod
# Collect dependencies for zero2prod
RUN mkdir /dependencies && \
ldd /app/target/release/zero2prod | tr -s '[:blank:]' '\n' | grep '^/' | xargs -I '{}' cp --parents '{}' /dependencies
FROM scratch AS runtime
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /dependencies /
COPY --from=builder /app/target/release/zero2prod /zero2prod
COPY configuration /configuration
ENV APP_ENVIRONMENT production
ENV RUST_LOG="error,$BINARY_NAME=info"
ENTRYPOINT ["/zero2prod"]
by executing
docker buildx build --tag zero2prod:latest --target runtime --file Dockerfile .
docker run --rm -p 8000:8000 zero2prod:latest
but is not creating a single (statically) linked binary.
How can I create such a statically linked build? Such that
FROM scratch AS runtime
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /app/target/release/zero2prod /zero2prod
COPY configuration /configuration
ENV APP_ENVIRONMENT production
ENV RUST_LOG="error,$BINARY_NAME=info"
ENTRYPOINT ["/zero2prod"]
works?
If I understand the situation correctly I need to look into MUSL & cross-build - but am still a bit confused -especially as trying to enable static linking fails for me with:
#ENV RUSTFLAGS='-C target-feature=+crt-static'
#0.176 error: cannot produce proc-macro for `actix-macros v0.2.4` as the target `aarch64-unknown-linux-gnu` does not support these crate types
This is also the case for other compiler options and targets that I have explored so far
#ENV CC=musl-gcc \
# CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER=musl-gcc
#RUSTFLAGS='-C target-feature=-crt-static'
#ENV CC_aarch64_unknown_linux_musl=aarch64-linux-musl-gcc \
# CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER=aarch64-linux-musl-gcc \
# RUSTFLAGS="-C target-feature=+crt-static -C link-arg=-lgcc"
# Add the target for musl
#RUN rustup target add x86_64-unknown-linux-musl
# RUN cargo build --release --bin zero2prod --target x86_64-unknown-linux-gnu
Even distroless works:
FROM lukemathwalker/cargo-chef:0.1.67-rust-1.78.0-slim-bookworm as chef
WORKDIR /app
RUN apt update && apt install lld clang -y
FROM chef as planner
COPY . .
RUN cargo chef prepare --recipe-path recipe.json
FROM chef as builder
COPY --from=planner /app/recipe.json recipe.json
RUN cargo chef cook --release --recipe-path recipe.json
COPY . .
ENV SQLX_OFFLINE true
RUN cargo build --frozen --release --bin zero2prod
FROM gcr.io/distroless/cc-debian12 AS runtime
USER 1000
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /app/target/release/zero2prod /zero2prod
COPY configuration /configuration
ENV APP_ENVIRONMENT production
ENV RUST_LOG="error,$BINARY_NAME=info"
ENTRYPOINT ["/zero2prod"]
but how can I make FROM scratch AS runtime
build as well?
I know I need to add musl-tools
and also:
RUN rustup target add x86_64-unknown-linux-musl
RUN rustup target add aarch64-unknown-linux-musl
and modify
RUN cargo chef cook --release --target aarch64-unknown-linux-musl --recipe-path recipe.json
and also modify
RUN cargo build --frozen --release --target aarch64-unknown-linux-musl --bin zero2prod
to add the target.
I do not receive any error this way - however, the zero2prod binary is never built in the folder.
this is the important line line that was missing
COPY --from=builder /app/target/aarch64-unknown-linux-musl/release/zero2prod /zero2prod
I was using
COPY --from=builder /app/target/release/zero2prod /zero2prod
See https://users.rust-lang.org/t/static-linking-for-rust-without-glibc-scratch-image/112279/5 for a much refined answer and example https://github.com/marvin-hansen/mimalloc
instead