Cargo Chef - Speed Up Your Docker Builds & Reduce Image Size of Your Rust Project

Cargo Chef - Speed Up Your Docker Builds & Reduce Image Size of Your Rust Project

Cache the dependencies of your Rust project and speed up your Docker builds.

Rust:- A language empowering everyone to build reliable and efficient software.

  • Speed
  • Safety
  • Cargo Manager
  • Error Messages
  • Efficient C Binding
  • Threads without Data Races

this talk presented at DockerCon 2022 India Community Room slides.com/sangambiradar/dockercon2022/full..

Getting started with rust

Install Rust

➜  rust-demo git:(main) ✗ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
info: downloading installer

Welcome to Rust!

This will download and install the official compiler for the Rust
programming language, and its package manager, Cargo.

Rustup metadata and toolchains will be installed into the Rustup
home directory, located at:

  /Users/sangambiradar/.rustup

This can be modified with the RUSTUP_HOME environment variable.

The Cargo home directory is located at:

  /Users/sangambiradar/.cargo

This can be modified with the CARGO_HOME environment variable.

The cargo, rustc, rustup and other commands will be added to
Cargo's bin directory, located at:

  /Users/sangambiradar/.cargo/bin

This path will then be added to your PATH environment variable by
modifying the profile files located at:

  /Users/sangambiradar/.profile
  /Users/sangambiradar/.zshenv

You can uninstall at any time with rustup self uninstall and
these changes will be reverted.

Current installation options:


   default host triple: aarch64-apple-darwin
     default toolchain: stable (default)
               profile: default
  modify PATH variable: yes

1) Proceed with installation (default)
2) Customize installation
3) Cancel installation
>1

info: profile set to 'default'
info: default host triple is aarch64-apple-darwin
info: syncing channel updates for 'stable-aarch64-apple-darwin'
info: latest update on 2022-11-03, rust version 1.65.0 (897e37553 2022-11-02)
info: downloading component 'cargo'
info: downloading component 'clippy'
info: downloading component 'rust-docs'
 18.9 MiB /  18.9 MiB (100 %)   5.7 MiB/s in  3s ETA:  0s
info: downloading component 'rust-std'
 27.6 MiB /  27.6 MiB (100 %)   5.9 MiB/s in  4s ETA:  0s
info: downloading component 'rustc'
 52.2 MiB /  52.2 MiB (100 %)   6.1 MiB/s in  9s ETA:  0s
info: downloading component 'rustfmt'
info: installing component 'cargo'
info: installing component 'clippy'
info: installing component 'rust-docs'
 18.9 MiB /  18.9 MiB (100 %)   6.2 MiB/s in  1s ETA:  0s
info: installing component 'rust-std'
 27.6 MiB /  27.6 MiB (100 %)  18.1 MiB/s in  1s ETA:  0s
info: installing component 'rustc'
 52.2 MiB /  52.2 MiB (100 %)  20.6 MiB/s in  2s ETA:  0s
info: installing component 'rustfmt'
info: default toolchain set to 'stable-aarch64-apple-darwin'

  stable-aarch64-apple-darwin installed - rustc 1.65.0 (897e37553 2022-11-02)


Rust is installed now. Great!

To get started you may need to restart your current shell.
This would reload your PATH environment variable to include
Cargo's bin directory ($HOME/.cargo/bin).

To configure your current shell, run:
source "$HOME/.cargo/env"

Cargo Package Manager

Cargo new hello-world
Cargo build 
Cargo run

update package dependencies & versioning cargo.toml

[package]
name = "hello"
version = "0.1.0"
authors = ["sangam"]
edition = "2018"

[dependencies]
rocket = { version = "0.5.0-rc.1", default-features = false }

here we are using rocket package for simple web app

➜  src git:(main) ✗ cat main.rs
#[macro_use]
extern crate rocket;

#[get("/")]
fn index() -> &'static str {
    "डॉकरकॉन के हिंदी कम्युनिटी रूम में आपका स्वागत है "
}

#[launch]
fn rocket() -> _ {
    rocket::build().mount("/", routes![index])
}

Cargo Build

➜  rust-demo git:(main) ✗ cargo build                                                   
    Updating crates.io index
  Downloaded futures-io v0.3.15
  Downloaded futures-task v0.3.15
  Downloaded log v0.4.14
  Downloaded bitflags v1.2.1
  Downloaded httparse v1.4.1
  Downloaded itoa v0.4.7
  Downloaded getrandom v0.2.3
  Downloaded futures-util v0.3.15
  Downloaded pear_codegen v0.2.3
  Downloaded tower-service v0.3.1
  Downloaded tracing v0.1.26
  Downloaded tokio-macros v1.2.0
  Downloaded proc-macro2 v1.0.27
  Downloaded serde_derive v1.0.126
  Downloaded ref-cast-impl v1.0.6
  Downloaded memchr v2.4.0
  Downloaded rand_chacha v0.3.0
  Downloaded quote v1.0.9
  Downloaded smallvec v1.6.1
  Downloaded uncased v0.9.6
  Downloaded slab v0.4.3
  Downloaded mio v0.7.11
  Downloaded syn v1.0.72
  Downloaded unchecked-index v0.2.2
  Downloaded rocket_codegen v0.5.0-rc.1
  Downloaded rocket v0.5.0-rc.1
  Downloaded futures-sink v0.3.15
  Downloaded futures-executor v0.3.15
  Downloaded bytes v1.0.1
  Downloaded futures v0.3.15
  Downloaded const_fn v0.4.8
  Downloaded binascii v0.1.4
  Downloaded async-stream v0.3.2
  Downloaded futures-core v0.3.15
  Downloaded proc-macro2-diagnostics v0.9.1
  Downloaded once_cell v1.7.2
  Downloaded instant v0.1.9
  Downloaded async-trait v0.1.50
  Downloaded ppv-lite86 v0.2.10
  Downloaded tracing-core v0.1.18
  Downloaded standback v0.2.17
  Downloaded either v1.6.1
  Downloaded num_cpus v1.13.0
  Downloaded http-body v0.4.2
  Downloaded convert_case v0.4.0
  Downloaded httpdate v1.0.1
  Downloaded rand v0.8.3
  Downloaded atty v0.2.14
  Downloaded socket2 v0.4.0
  Downloaded time-macros-impl v0.1.1
  Downloaded http v0.2.4
  Downloaded rand_core v0.6.2
  Downloaded time v0.2.26
  Downloaded serde v1.0.126
  Downloaded indexmap v1.6.2
  Downloaded remove_dir_all v0.5.3
  Downloaded ref-cast v1.0.6
  Downloaded parking_lot v0.11.1
  Downloaded pin-project-lite v0.2.6
  Downloaded time-macros v0.1.1
  Downloaded toml v0.5.8
  Downloaded fnv v1.0.7
  Downloaded pin-utils v0.1.0
  Downloaded glob v0.3.0
  Downloaded autocfg v1.0.1
  Downloaded tokio-util v0.6.7
  Downloaded h2 v0.3.3
  Downloaded scopeguard v1.1.0
  Downloaded try-lock v0.2.3
  Downloaded rocket_http v0.5.0-rc.1
  Downloaded ubyte v0.10.1
  Downloaded yansi v0.5.0
  Downloaded want v0.3.0
  Downloaded version_check v0.9.3
  Downloaded futures-channel v0.3.15
  Downloaded proc-macro-nested v0.1.7
  Downloaded stable-pattern v0.1.0
  Downloaded tempfile v3.2.0
  Downloaded signal-hook-registry v1.4.0
  Downloaded lock_api v0.4.4
  Downloaded async-stream-impl v0.3.2
  Downloaded mime v0.3.16
  Downloaded parking_lot_core v0.8.3
  Downloaded pear v0.2.3
  Downloaded inlinable_string v0.1.14
  Downloaded derive_more v0.99.14
  Downloaded atomic v0.5.0
  Downloaded unicode-xid v0.2.2
  Downloaded twoway v0.2.2
  Downloaded lazy_static v1.4.0
  Downloaded cfg-if v1.0.0
  Downloaded percent-encoding v2.1.0
  Downloaded libc v0.2.95
  Downloaded hashbrown v0.9.1
  Downloaded proc-macro-hack v0.5.19
  Downloaded futures-macro v0.3.15
  Downloaded encoding_rs v0.8.28
  Downloaded devise v0.3.0
  Downloaded devise_core v0.3.0
  Downloaded tokio-stream v0.1.6
  Downloaded devise_codegen v0.3.0
  Downloaded state v0.5.1
  Downloaded multer v2.0.0
  Downloaded cookie v0.15.0
  Downloaded spin v0.9.0
  Downloaded figment v0.10.5
  Downloaded hyper v0.14.9
  Downloaded tokio v1.6.1
  Downloaded 108 crates (5.9 MB) in 6.59s (largest was `encoding_rs` at 1.4 MB)
   Compiling proc-macro2 v1.0.27
   Compiling unicode-xid v0.2.2
   Compiling syn v1.0.72
   Compiling version_check v0.9.3
   Compiling libc v0.2.95
   Compiling autocfg v1.0.1
   Compiling cfg-if v1.0.0
   Compiling memchr v2.4.0
   Compiling proc-macro-hack v0.5.19
   Compiling futures-core v0.3.15
   Compiling pin-project-lite v0.2.6
   Compiling serde_derive v1.0.126
   Compiling log v0.4.14
   Compiling serde v1.0.126
   Compiling yansi v0.5.0
   Compiling futures-macro v0.3.15
   Compiling proc-macro2-diagnostics v0.9.1
   Compiling futures-channel v0.3.15
   Compiling proc-macro-nested v0.1.7
   Compiling bytes v1.0.1
   Compiling futures-sink v0.3.15
   Compiling futures-task v0.3.15
   Compiling quote v1.0.9
   Compiling futures-util v0.3.15
   Compiling tokio v1.6.1
   Compiling mio v0.7.11
   Compiling num_cpus v1.13.0
   Compiling signal-hook-registry v1.4.0
   Compiling standback v0.2.17
   Compiling pin-utils v0.1.0
   Compiling once_cell v1.7.2
   Compiling futures-io v0.3.15
   Compiling slab v0.4.3
   Compiling fnv v1.0.7
   Compiling itoa v0.4.7
   Compiling indexmap v1.6.2
   Compiling http v0.2.4
   Compiling httparse v1.4.1
   Compiling bitflags v1.2.1
   Compiling const_fn v0.4.8
   Compiling lazy_static v1.4.0
   Compiling tracing-core v0.1.18
   Compiling time v0.2.26
   Compiling uncased v0.9.6
   Compiling hashbrown v0.9.1
   Compiling tracing v0.1.26
   Compiling getrandom v0.2.3
   Compiling cookie v0.15.0
   Compiling instant v0.1.9
   Compiling ref-cast v1.0.6
   Compiling inlinable_string v0.1.14
   Compiling smallvec v1.6.1
   Compiling scopeguard v1.1.0
   Compiling try-lock v0.2.3
   Compiling lock_api v0.4.4
   Compiling parking_lot_core v0.8.3
   Compiling want v0.3.0
   Compiling rand_core v0.6.2
   Compiling http-body v0.4.2
   Compiling socket2 v0.4.0
   Compiling mime v0.3.16
   Compiling tokio-macros v1.2.0
   Compiling time-macros-impl v0.1.1
   Compiling pear_codegen v0.2.3
   Compiling devise_core v0.3.0
   Compiling ref-cast-impl v1.0.6
   Compiling pear v0.2.3
   Compiling tower-service v0.3.1
   Compiling encoding_rs v0.8.28
   Compiling httpdate v1.0.1
   Compiling ppv-lite86 v0.2.10
   Compiling percent-encoding v2.1.0
   Compiling devise_codegen v0.3.0
   Compiling rand_chacha v0.3.0
   Compiling parking_lot v0.11.1
   Compiling stable-pattern v0.1.0
   Compiling atomic v0.5.0
   Compiling multer v2.0.0
   Compiling figment v0.10.5
   Compiling unchecked-index v0.2.2
   Compiling either v1.6.1
   Compiling state v0.5.1
   Compiling convert_case v0.4.0
   Compiling async-trait v0.1.50
   Compiling twoway v0.2.2
   Compiling derive_more v0.99.14
   Compiling time-macros v0.1.1
   Compiling futures-executor v0.3.15
   Compiling devise v0.3.0
   Compiling rand v0.8.3
   Compiling tokio-util v0.6.7
   Compiling async-stream-impl v0.3.2
   Compiling rocket v0.5.0-rc.1
   Compiling remove_dir_all v0.5.3
   Compiling glob v0.3.0
   Compiling spin v0.9.0
   Compiling async-stream v0.3.2
   Compiling tempfile v3.2.0
   Compiling tokio-stream v0.1.6
   Compiling futures v0.3.15
   Compiling atty v0.2.14
   Compiling binascii v0.1.4
   Compiling toml v0.5.8
   Compiling ubyte v0.10.1
   Compiling h2 v0.3.3
   Compiling hyper v0.14.9
   Compiling rocket_http v0.5.0-rc.1
   Compiling rocket_codegen v0.5.0-rc.1
   Compiling hello v0.1.0 (/Users/sangambiradar/Documents/Dockercon2022/rust-demo)
    Finished dev [unoptimized + debuginfo] target(s) in 4m 33s

Cargo run

➜  rust-demo git:(main) ✗ cargo run 
    Finished dev [unoptimized + debuginfo] target(s) in 0.11s
     Running `target/debug/hello`
🔧 Configured for debug.
   >> address: 127.0.0.1
   >> port: 8000
   >> workers: 10
   >> ident: Rocket
   >> keep-alive: 5s
   >> limits: bytes = 8KiB, data-form = 2MiB, file = 1MiB, form = 32KiB, json = 1MiB, msgpack = 1MiB, string = 8KiB
   >> tls: disabled
   >> temp dir: /var/folders/xy/3ssjv1j152x3_0ryt4yb982c0000gn/T/
   >> log level: normal
   >> cli colors: true
   >> shutdown: ctrlc = true, force = true, signals = [SIGTERM], grace = 2s, mercy = 3s
🛰  Routes:
   >> (index) GET /
📡 Fairings:
   >> Shield (liftoff, response, singleton)
🛡️ Shield:
   >> X-Content-Type-Options: nosniff
   >> Permissions-Policy: interest-cohort=()
   >> X-Frame-Options: SAMEORIGIN
🚀 Rocket has launched from http://127.0.0.1:8000

Screenshot 2022-11-26 at 12.26.07 PM.png

Dockerize Rust app

cat Dockerfile.plain 
ARG BASE_IMAGE=rust:1.52.1-slim-buster

FROM $BASE_IMAGE
WORKDIR /app
COPY . .
RUN cargo build  --release --locked
ENV ROCKET_ADDRESS=0.0.0.0
EXPOSE 8080 
CMD ["./target/release/hello"]

Docker build

➜  rust-demo git:(main) ✗ docker build -t rustplain -f Dockerfile.plain .
[+] Building 435.3s (10/10) FINISHED                                                                                                 
 => [internal] load build definition from Dockerfile.plain                                                                      0.0s
 => => transferring dockerfile: 319B                                                                                            0.0s
 => [internal] load .dockerignore                                                                                               0.0s
 => => transferring context: 2B                                                                                                 0.0s
 => [internal] load metadata for docker.io/library/rust:1.52.1-slim-buster                                                      7.4s
 => [auth] library/rust:pull token for registry-1.docker.io                                                                     0.0s
 => [internal] load build context                                                                                              12.7s
 => => transferring context: 1.65GB                                                                                            12.6s
 => [1/4] FROM docker.io/library/rust:1.52.1-slim-buster@sha256:f55f5c929fb5d76137d71284d05e456ceeb9c217c267529c9dd79b7460122  67.3s
 => => resolve docker.io/library/rust:1.52.1-slim-buster@sha256:f55f5c929fb5d76137d71284d05e456ceeb9c217c267529c9dd79b74601225  0.0s
 => => sha256:7bf4871c98008a070ed024152c9b7c9d1cb3776303b848f1430017596dcfc97f 4.85kB / 4.85kB                                  0.0s
 => => sha256:fcad0c936ea5c12e1c8c4edb81a97c0cde04ee71e7067ee3b246474cf1854d7a 25.91MB / 25.91MB                                9.5s
 => => sha256:d212d31fbd9f05f9ffa3c85a35798148f19ce9b5ecb92217026942b7f8bd9c3e 232.84MB / 232.84MB                             64.4s
 => => sha256:f55f5c929fb5d76137d71284d05e456ceeb9c217c267529c9dd79b7460122574 984B / 984B                                      0.0s
 => => sha256:a0b8f6e797fb0d0b91e2be0f8872246b1397480cd150eec312779b8966f2f418 742B / 742B                                      0.0s
 => => extracting sha256:fcad0c936ea5c12e1c8c4edb81a97c0cde04ee71e7067ee3b246474cf1854d7a                                       0.9s
 => => extracting sha256:d212d31fbd9f05f9ffa3c85a35798148f19ce9b5ecb92217026942b7f8bd9c3e                                       2.7s
 => [2/4] WORKDIR /app                                                                                                          0.6s
 => [3/4] COPY . .                                                                                                              3.9s
 => [4/4] RUN cargo build  --release --locked                                                                                 352.7s
 => exporting to image                                                                                                          3.3s 
 => => exporting layers                                                                                                         3.3s 
 => => writing image sha256:e2f3263f121c7d233dd3ca0d28ab69b9af1c06db3c0b7ccd0a1a1caee18cef19                                    0.0s 
 => => naming to docker.io/library/rustplain

lets check size of docker image

➜  rust-demo git:(main) ✗ docker images         
REPOSITORY                                                TAG                                                                          IMAGE ID       CREATED          SIZE
rustplain                                                 latest                                                                       e2f3263f121c   56 seconds ago   3.41GB

🔥 3.41GB for simple web app ! lets reduce with image size

lets build Muti-stage Dockerfile


ARG BASE_IMAGE=rust:1.52.1-slim-buster

FROM $BASE_IMAGE as builder
WORKDIR /app
COPY . .
RUN cargo build --release
CMD ["./target/release/hello"]

FROM $BASE_IMAGE
COPY --from=builder /app/target/release/hello /
CMD ["./hello"]

Docker build

➜  rust-demo git:(main) ✗ docker build -t rustmulti -f Dockerfile.multistage . 
[+] Building 289.4s (11/11) FINISHED                                                                                                 
 => [internal] load build definition from Dockerfile.multistage                                                                 0.0s
 => => transferring dockerfile: 367B                                                                                            0.0s
 => [internal] load .dockerignore                                                                                               0.0s
 => => transferring context: 2B                                                                                                 0.0s
 => [internal] load metadata for docker.io/library/rust:1.52.1-slim-buster                                                      2.7s
 => [auth] library/rust:pull token for registry-1.docker.io                                                                     0.0s
 => [internal] load build context                                                                                               0.3s
 => => transferring context: 688.29kB                                                                                           0.2s
 => CACHED [stage-1 1/2] FROM docker.io/library/rust:1.52.1-slim-buster@sha256:f55f5c929fb5d76137d71284d05e456ceeb9c217c267529  0.0s
 => CACHED [builder 2/4] WORKDIR /app                                                                                           0.0s
 => CACHED [builder 3/4] COPY . .                                                                                               0.0s
 => [builder 4/4] RUN cargo build --release                                                                                   286.3s
 => [stage-1 2/2] COPY --from=builder /app/target/release/hello /                                                               0.0s
 => exporting to image                                                                                                          0.0s
 => => exporting layers                                                                                                         0.0s
 => => writing image sha256:23dc151185880ea5cb93e3978d78c7dc247c489fba894d1fec68bc1c2768b04d                                    0.0s 
 => => naming to docker.io/library/rustmulti

Docker image size reduced from 3Gb to 797MB !

➜  rust-demo git:(main) ✗ docker images 
REPOSITORY                                                TAG                                                                          IMAGE ID       CREATED          SIZE
rustmulti                                                 latest                                                                       23dc15118588   18 minutes ago   797MB
rustplain                                                 latest

Cargo-chef come to rescue?

Cargo chef is open source utility :- github.com/LukeMathWalker/cargo-chef that speed up Rust Docker builds using Docker layer caching.

Nothing too mysterious going on here, you can examine the recipe.json file: it contains the skeleton of your project (e.g. all the Cargo.toml files with their relative path, the Cargo.lock file is available) plus a few additional pieces of information. In particular it makes sure that all libraries and binaries are explicitly declared in their respective Cargo.toml files even if they can be found at the canonical default location (src/main.rs for a binary, src/lib.rs for a library).

The recipe.json is the equivalent of the Python requirements.txt file - it is the only input required for cargo chef cook, the command that will build out our dependencies:

lets build image using cargo-chef utility

ARG BASE_IMAGE=rust:1.52.1-slim-buster

FROM $BASE_IMAGE as planner
WORKDIR /app
RUN cargo install cargo-chef --version 0.1.20 --locked
COPY . .
RUN cargo chef prepare  --recipe-path recipe.json

FROM $BASE_IMAGE as cacher
WORKDIR /app
RUN cargo install cargo-chef --version 0.1.20 --locked
COPY --from=planner /app/recipe.json recipe.json
RUN cargo chef cook --release --recipe-path recipe.json

FROM $BASE_IMAGE as builder
WORKDIR /app
COPY . .
# Copy over the cached dependencies
COPY --from=cacher /app/target target
COPY --from=cacher $CARGO_HOME $CARGO_HOME
RUN cargo build --release

FROM gcr.io/distroless/cc-debian10
COPY --from=builder /app/target/release/hello /
ENV ROCKET_ADDRESS=0.0.0.0
EXPOSE 8080 
CMD ["./hello"]

Docker build

 rust-demo git:(main) ✗ docker build -t rustdistrolesschef -f Dockerfile.distroless .
[+] Building 392.3s (20/20) FINISHED                                                                                    
 => [internal] load build definition from Dockerfile.distroless                                                    0.0s
 => => transferring dockerfile: 868B                                                                               0.0s
 => [internal] load .dockerignore                                                                                  0.0s
 => => transferring context: 2B                                                                                    0.0s
 => [internal] load metadata for gcr.io/distroless/cc-debian10:latest                                              1.3s
 => [internal] load metadata for docker.io/library/rust:1.52.1-slim-buster                                         3.0s
 => [auth] library/rust:pull token for registry-1.docker.io                                                        0.0s
 => [internal] load build context                                                                                  0.2s
 => => transferring context: 549.14kB                                                                              0.2s
 => [planner 1/5] FROM docker.io/library/rust:1.52.1-slim-buster@sha256:f55f5c929fb5d76137d71284d05e456ceeb9c217c  0.0s
 => CACHED [stage-3 1/2] FROM gcr.io/distroless/cc-debian10@sha256:ad23034798c17a50bb7f57330391dae444f0c02614ddc0  0.0s
 => CACHED [planner 2/5] WORKDIR /app                                                                              0.0s
 => [planner 3/5] RUN cargo install cargo-chef --version 0.1.20 --locked                                         322.6s
 => [builder 3/6] COPY . .                                                                                         3.5s
 => [planner 4/5] COPY . .                                                                                         2.8s 
 => [planner 5/5] RUN cargo chef prepare  --recipe-path recipe.json                                                0.3s 
 => [cacher 4/5] COPY --from=planner /app/recipe.json recipe.json                                                  0.0s 
 => [cacher 5/5] RUN cargo chef cook --release --recipe-path recipe.json                                          52.4s 
 => [builder 4/6] COPY --from=cacher /app/target target                                                            1.5s 
 => [builder 5/6] COPY --from=cacher /usr/local/cargo /usr/local/cargo                                             1.0s 
 => [builder 6/6] RUN cargo build --release                                                                        7.0s 
 => [stage-3 2/2] COPY --from=builder /app/target/release/hello /                                                  0.0s 
 => exporting to image                                                                                             0.0s 
 => => exporting layers                                                                                            0.0s 
 => => writing image sha256:0e6c2fb29bd45755d180a2d8f34d78b9c8f5845c5ae798dc98248434f7664a8d                       0.0s
 => => naming to docker.io/library/rustdistrolesschef

if you see image size of new distroless chef cargo image is just 26.2MB

REPOSITORY                                                TAG                                                                          IMAGE ID       CREATED          SIZE
rustdistrolesschef                                        latest                                                                       0e6c2fb29bd4   34 seconds ago   26.2MB
rustmulti                                                 latest                                                                       23dc15118588   3 hours ago      797MB
rustplain                                                 latest                                                                       e2f3263f121c   3 hours ago      3.41GB

3.14GB to 26.2M that's big impact !

Did you find this article valuable?

Support CloudNativeFolks Community by becoming a sponsor. Any amount is appreciated!