#cargo-build #docker-build #cargo #docker #cargo-subcommand #cache

bin+lib cargo-chef

A cargo 子命令,用于为项目依赖项构建最优 Docker 层缓存

67 个版本

0.49.0 2022年12月10日
0.1.67 2024年5月22日
0.1.66 2024年3月10日
0.1.62 2023年7月26日
0.1.9 2020年11月29日

#55 in Cargo 插件

Download history 9703/week @ 2024-04-29 8655/week @ 2024-05-06 10040/week @ 2024-05-13 9876/week @ 2024-05-20 8916/week @ 2024-05-27 9945/week @ 2024-06-03 9913/week @ 2024-06-10 9734/week @ 2024-06-17 9788/week @ 2024-06-24 9951/week @ 2024-07-01 9519/week @ 2024-07-08 9216/week @ 2024-07-15 10732/week @ 2024-07-22 9113/week @ 2024-07-29 8338/week @ 2024-08-05 9203/week @ 2024-08-12

38,009 每月下载量

Apache-2.0 OR MIT

57KB
1K SLoC

cargo-chef

缓存 Rust 项目的依赖项并加快 Docker 构建速度。


cargo-chef 最初是为 Zero to Production In Rust 的部署章节开发的,这是一本关于使用 Rust 编程语言进行后端开发的实战入门书。

目录

  1. 如何安装
  2. 如何使用
  3. 优势与限制
  4. 许可协议

如何安装

您可以使用以下方式从 crates.io 安装 cargo-chef

cargo install cargo-chef --locked

如何使用

⚠️ cargo-chef 不应在本地上运行
其主要用途是通过在将实际源代码复制过来之前运行来加快容器构建速度。不要在现有代码库上运行它,以避免文件被覆盖。

cargo-chef 提供了两个命令:preparecook

cargo chef --help

cargo-chef

USAGE:
    cargo chef <SUBCOMMAND>

SUBCOMMANDS:
    cook       Re-hydrate the minimum project skeleton identified by `cargo chef prepare` and
               build it to cache dependencies
    prepare    Analyze the current project to determine the minimum subset of files (Cargo.lock
               and Cargo.toml manifests) required to build it and cache dependencies

prepare 检查您的项目并构建一个 配方,该配方捕获构建依赖项所需的信息集合。

cargo chef prepare --recipe-path recipe.json

这里没有太多神秘的事情,您可以检查 recipe.json 文件:它包含您的项目骨架(例如,所有带有相对路径的 Cargo.toml 文件,Cargo.lock 文件也可用)以及一些附加信息。
特别是,它确保所有库和二进制文件都在各自的 Cargo.toml 文件中明确声明,即使它们可以在规范默认位置(二进制的 src/main.rs,库的 src/lib.rs)找到。

recipe.json 文件相当于 Python 的 requirements.txt 文件 —— 它是构建依赖项所需的唯一输入,即 cargo chef cook 命令,这个命令将构建我们的依赖项

cargo chef cook --recipe-path recipe.json

如果您想以 --release 模式构建

cargo chef cook --release --recipe-path recipe.json

您还可以选择覆盖使用哪个 Rust 工具链。例如,强制使用 nightly 工具链

cargo +nightly chef cook --recipe-path recipe.json

cargo-chef 被设计成可以在 Dockerfile 中使用

FROM lukemathwalker/cargo-chef:latest-rust-1 AS chef
WORKDIR /app

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
# Build dependencies - this is the caching Docker layer!
RUN cargo chef cook --release --recipe-path recipe.json
# Build application
COPY . .
RUN cargo build --release --bin app

# We do not need the Rust toolchain to run the binary!
FROM debian:bookworm-slim AS runtime
WORKDIR /app
COPY --from=builder /app/target/release/app /usr/local/bin
ENTRYPOINT ["/usr/local/bin/app"]

我们使用三个阶段:第一阶段计算配方文件,第二阶段缓存我们的依赖项并构建二进制文件,第三阶段是我们的运行时环境。
只要您的依赖项没有更改,recipe.json 文件将保持不变,因此 cargo chef cook --release --recipe-path recipe.json 的输出将被缓存,大大加快您的构建速度(在某些商业项目中测得高达 5 倍)。

预构建镜像

我们提供 lukemathwalker/cargo-chef 作为预构建的 Docker 镜像,其中包含 Rust 和 cargo-chef

标记方案为 <cargo-chef version>-rust-<rust version>
例如,0.1.22-rust-1.56.0
您可以通过使用来选择获取 cargo-chefrust 的最新版本

  • latest-rust-1.56.0(使用特定 Rust 版本的最新 cargo-chef);
  • 0.1.22-rust-latest(使用特定 cargo-chef 版本的最新 Rust)。您可以在 Dockerhub 上的所有可用标签 中找到。

⚠️ 您必须在所有阶段使用相同的 Rust 版本
如果您在一个阶段中使用不同的 Rust 版本,缓存将无法按预期工作。

不使用预构建镜像

如果您不想使用 lukemathwalker/cargo-chef 镜像,您可以在 Dockerfile 中简单安装 CLI

FROM rust:1 AS chef 
# We only pay the installation cost once, 
# it will be cached from the second build onwards
RUN cargo install cargo-chef 
WORKDIR app

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
# Build dependencies - this is the caching Docker layer!
RUN cargo chef cook --release --recipe-path recipe.json
# Build application
COPY . .
RUN cargo build --release --bin app

# We do not need the Rust toolchain to run the binary!
FROM debian:bookworm-slim AS runtime
WORKDIR app
COPY --from=builder /app/target/release/app /usr/local/bin
ENTRYPOINT ["/usr/local/bin/app"]

在 Alpine 上运行二进制文件

如果您想使用 alpine 发行版运行应用程序,您需要创建一个完全静态的二进制文件。
推荐的方法是使用 muslrust 构建 x86_64-unknown-linux-musl 目标。
cargo-chef 支持 x86_64-unknown-linux-musl,但我们正在进行 交叉编译 —— 目标工具链必须明确指定。

一个示例 Dockerfile 看起来像这样

# Using the `rust-musl-builder` as base image, instead of 
# the official Rust toolchain
FROM clux/muslrust:stable AS chef
USER root
RUN cargo install cargo-chef
WORKDIR /app

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
# Notice that we are specifying the --target flag!
RUN cargo chef cook --release --target x86_64-unknown-linux-musl --recipe-path recipe.json
COPY . .
RUN cargo build --release --target x86_64-unknown-linux-musl --bin app

FROM alpine AS runtime
RUN addgroup -S myuser && adduser -S myuser -G myuser
COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/app /usr/local/bin/
USER myuser
CMD ["/usr/local/bin/app"]

优势与限制

cargo-chef已在一些开源项目和部分商业项目中进行了测试,但我们的测试显然还没有穷尽关于cargo build定制化的可能性范围,我们确信还有一些需要平滑处理的地方边缘 - 请在GitHub上提交问题。

cargo-chef的好处

一个常见的替代方法是加载一个最小化的main.rs到一个包含Cargo.tomlCargo.lock的容器中,以构建一个只包含您的依赖项的Docker层(更多信息请参见这里)。与cargo-chef相比,这更加脆弱,因为cargo-chef

  • 自动获取工作空间中的所有crate(以及随着添加的新crate)
  • 在文件或crate移动时继续工作,而手动方法需要手动编辑Dockerfile
  • 生成更少的中间Docker层(对于工作空间)

局限性和注意事项

  • cargo chef cookcargo build必须从相同的工目录中执行。如果您使用cat检查您的一个项目下的*.d文件,您会发现它们包含指向项目target目录的绝对路径。如果移动,cargo将不会利用它们作为缓存的依赖项;
  • cargo build将从零开始构建本地依赖项(当前项目之外的),即使它们未更改,这也归因于其指纹逻辑对时间戳的依赖(请参见cargo仓库中的这个问题);

许可协议

根据您选择,受Apache License,Version 2.0或MIT许可协议的许可。除非您明确声明,否则您提交的任何旨在包含在您提交的此crate中的贡献,根据Apache-2.0许可协议定义,应如上所述双重许可,不得附加任何额外条款或条件。

依赖项

~7–17MB
~205K SLoC