building efficient docker images for rust
lets build the smallest, most effficient docker image for our rust apps.
toc
create a simple rust app
create your own rust program or follow this guide for creating and deploying a minimal axum app with loco
speed up build with cargo-chef
cargo-chef
is a tool designed for Rust projects that aims to optimize the build process in Docker environments. It helps in reducing build times by caching dependencies more effectively. It works by first analyzing your Rust project to generate a recipe that contains all the dependencies. This recipe is then used to pre-compile these dependencies, creating a reusable Docker layer. Subsequent builds can use this cached layer to skip re-compiling unchanged dependencies, significantly speeding up the build process. This tool is particularly useful in continuous integration (CI) pipelines where build times are critical.
cargo install cargo-chef --locked
our scaffolded dockerfile from our loco app looks like this:
FROM rust:1.74-slim as builder
WORKDIR /usr/src/
COPY . .
RUN cargo build --release
FROM debian:bookworm-slim
WORKDIR /usr/app
COPY --from=builder /usr/src/config /usr/app/configCOPY --from=builder /usr/src/target/release/hello_loco-cli /usr/app/hello_loco-cli
ENTRYPOINT ["/usr/app/hello_loco-cli"]
if we build an image based on the above dockerfile we will get an image of 123mb:
➜ docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEhello_loco latest 0d5bc1271eeb 0 minites ago 123MB
lets use cargo-chef in dockerfile:
FROM lukemathwalker/cargo-chef:latest-rust-1 AS chefWORKDIR /app
FROM chef AS plannerCOPY . .RUN cargo chef prepare --recipe-path recipe.json
FROM chef AS builderCOPY --from=planner /app/recipe.json recipe.json# Build dependencies - this is the caching Docker layer!RUN cargo chef cook --release --recipe-path recipe.json# Build applicationCOPY . .RUN cargo build --release --bin app
# We do not need the Rust toolchain to run the binary!FROM debian:bookworm-slim AS runtimeWORKDIR /appCOPY --from=builder /app/target/release/app /usr/local/binENTRYPOINT ["/usr/local/bin/app"]