Need help building a slim container for my rust project

Hi

I think I need to call on the wisdom of both rust and container building with dockerfiles, as I'm doing something wrong and have probably stared at it for too long to see what that is.

The idea is to use a build container to build my code, and the copy over the resulting artifact in a slim alpine container.

Trying to be smart I in addition to that tried to make the build into two stages, first building a newly built project from cargo init with all my dependencies, and then copy over my real code and build again, thus using the docker build cache to only rebuild if I change my code, and the time consuming build of dependencies is thus cached. But, it doesn't work.

It always result in my container producing the familiar "Hello World" output from a fresh "cargo init" and not the more complex source I tried to insert. Any helpful suggestions appreciated. I tried asking some docker people and they were seriously confused by the rust parts, so I'm trying this forum instead.

This is my dockerfile, which I build with the usual "docker build ."

####################################################################################################
## Builder
####################################################################################################
FROM rust:latest AS builder

RUN rustup target add x86_64-unknown-linux-musl
RUN apt-get update && apt-get install -y musl-tools musl-dev
RUN update-ca-certificates

# Create appuser
ENV USER=axum
ENV UID=10001

RUN adduser \
    --disabled-password \
    --gecos "" \
    --home "/nonexistent" \
    --shell "/sbin/nologin" \
    --no-create-home \
    --uid "${UID}" \
    "${USER}"

RUN cargo new --bin --verbose axum-app

WORKDIR /axum-app

COPY ./Cargo.lock ./Cargo.lock
COPY ./Cargo.toml ./Cargo.toml

# This will build just the deps from Cargo.toml
RUN cargo build --target x86_64-unknown-linux-musl --release
RUN rm /axum-app/src/*.rs
ADD ./src/ /axum-app/src/
RUN rm /axum-app/target/x86_64-unknown-linux-musl/release/axum-app

RUN cargo build --target x86_64-unknown-linux-musl --release --verbose --manifest-path /axum-app/Cargo.toml

####################################################################################################
## Final image
####################################################################################################
FROM alpine:latest

ENV USER=axum
ENV UID=10001

# Import from builder.
COPY --from=builder /etc/passwd /etc/passwd
COPY --from=builder /etc/group /etc/group

WORKDIR /axum-app

# Copy our build
COPY --from=builder /axum-app/target/x86_64-unknown-linux-musl/release/axum-app ./

# Use an unprivileged user.
USER axum:axum

CMD ["/axum-app/axum-app"]

I think it’s this:

The essential problem is that your real sources are older than the generated Hello World, and Cargo uses the modification time to determine if it needs rebuilt. To fix it, add this like right after copying the file in:

RUN find /axum-app/src -type f -exec touch ‘{}’

I could not get that to work, as find complained about lacking arguments to -exec. I guess there's something about quoting going on. But, just running touch on the files in that folder worked!

I thought there was something about caching or rebuilding, but thought I had that eliminated by removing files left, right and centre. I figured something cargo I hadn't figured out was involved. I thought I had covered it, but apparently not.

Thanks!

I've thought more on this, and find it very curious that my rm does not suffice. How can cargo rebuild the code that has been removed? Anyway, touch makes it work, so maybe that will have to make do.

It doesn't rebuild because your newly copied files have an older timestamp than the old main.rs. So it decides to not rebuild but instead just run the executable in the target folder.

Yes, I got that point, but considering I rm both the source and the resulting artifact, how does that leave it anything?

Intermediate files are still kept.

Thanks! I now had time to read Whitfin's Blog: Optimizing Docker Images for Rust Projects and it mentions target/release/deps which I realize will hold files for the intermediate state. Thanks for clarifying that part. I'm learning a lot more about cargo from this, which is good stuff!

All feedback and input in this thread is very much appreciated.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.