Rust 开发加速神器:用cargo-chef打造极速 Docker 构建流程

文摘   2024-07-01 00:32   江苏  

Rust 开发加速神器:用cargo-chef打造极速 Docker 构建流程

缓存您的 Rust 项目的依赖,并加速您的 Docker 构建。


cargo-chef最初是为Zero to Production In Rust[1]的部署章节开发的,这是一个使用 Rust 编程语言进行后端开发的实践介绍。

unsetunset如何安装unsetunset

您可以从crates.io[2]使用以下命令安装cargo-chef

cargo install cargo-chef --locked

unsetunset如何使用unsetunset

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

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

cargo chef --help

cargo-chef

用法:
cargo chef <子命令>

子命令:
cook 根据`cargo chef prepare`识别的最小项目骨架重新构建并构建以缓存依赖
prepare 分析当前项目以确定构建它并缓存依赖所需的最小文件子集(Cargo.lock和Cargo.toml清单)

prepare检查您的项目并构建一个recipe,它捕获了构建依赖所需的信息集。

cargo chef prepare --recipe-path recipe.json

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

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
# 构建依赖 - 这是缓存的Docker层!
RUN cargo chef cook --release --recipe-path recipe.json
# 构建应用程序
COPY . .
RUN cargo build --release --bin app

# 我们不需要Rust工具链来运行二进制文件!
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[3]上找到所有可用的标签[4]

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

不使用预构建的镜像

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

FROM rust:1 AS chef
# 我们只支付一次安装成本,
# 从第二次构建开始将被缓存
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
# 构建依赖 - 这是缓存的Docker层!
RUN cargo chef cook --release --recipe-path recipe.json
# 构建应用程序
COPY . .
RUN cargo build --release --bin app

# 我们不需要Rust工具链来运行二进制文件!
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`[5]x86_64-unknown-linux-musl目标构建。cargo-chef适用于x86_64-unknown-linux-musl,但我们是交叉编译 - 必须明确指定目标工具链。

一个示例 Dockerfile 如下所示:

# 使用`rust-musl-builder`作为基础镜像,而不是
# 官方的Rust工具链
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
# 注意我们指定了--target标志!
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"]

unsetunset优势与限制unsetunset

cargo-chef已经在一些开源项目和一些商业项目上进行了测试,但我们的测试肯定没有穷尽cargo build定制的可能性范围,我们确信还有一些粗糙的边缘需要平滑 - 请在GitHub[6]上提交问题。

cargo-chef的优势:

一种常见的替代方法是将最小的main.rs加载到带有Cargo.tomlCargo.lock的容器中,以构建一个只包含您的依赖的 Docker 层(更多信息在这里[7])。与cargo-chef相比,这是脆弱的,cargo-chef将:

  • 自动拾取工作空间中的所有 crates(以及添加的新 crates)
  • 当文件或 crates 移动时仍然工作,这将需要手动编辑使用“手动”方法的Dockerfile
  • 生成较少的中间 Docker 层(对于工作空间)

限制和注意事项:

  • cargo chef cookcargo build必须从相同的工作目录执行。如果您检查使用cattarget/debug/deps下的一个项目的*.d文件,您会注意到它们包含引用项目target目录的绝对路径。如果移动,cargo将不会将它们作为缓存的依赖利用;
  • cargo build将从头开始构建本地依赖(项目之外),即使它们没有改变,这是由于它依赖于时间戳的指纹逻辑(请参见`cargo`存储库上的这个*长*问题[8]);
参考资料
[1]

Zero to Production In Rust: https://zero2prod.com

[2]

crates.io: https://crates.io

[3]

Dockerhub: https://hub.docker.com/r/lukemathwalker/cargo-chef

[4]

所有可用的标签: https://hub.docker.com/r/lukemathwalker/cargo-chef

[5]

muslrust: https://github.com/clux/muslrust

[6]

GitHub: https://github.com/LukeMathWalker/cargo-chef

[7]

更多信息在这里: https://www.lpalmieri.com/posts/fast-rust-docker-builds/#caching-rust-builds

[8]

cargo存储库上的这个问题: https://github.com/rust-lang/cargo/issues/2644


编程悟道
自制软件研发、软件商店,全栈,ARTS 、架构,模型,原生系统,后端(Node、React)以及跨平台技术(Flutter、RN).vue.js react.js next.js express koa hapi uniapp Astro
 最新文章