使用 Docker build 和 lib.rs 缓存 Rust 依赖项
Cache Rust dependencies with Docker build and lib.rs
我一直在尝试为我的 rust 构建创建一个 Dockerfile,它允许我从依赖项中单独构建应用程序,如下所示:
然而,这似乎对我不起作用,因为 lib.rs 文件的工作树略有不同。我的 Dockerfile 布局如下:
FROM rust:1.60 as build
# create a new empty shell project
RUN USER=root cargo new --bin rocket-example-pro
WORKDIR /rocket-example-pro
# create dummy lib.rs file to build dependencies separately from changes to src
RUN touch src/lib.rs
# copy over your manifests
COPY ./Cargo.lock ./Cargo.lock
COPY ./Cargo.toml ./Cargo.toml
RUN cargo build --release --locked
RUN rm src/*.rs
# copy your source tree
COPY ./src ./src
# build for release
RUN rm ./target/release/deps/rocket_example_pro*
RUN cargo build --release --locked ## <-- fails
# our final base
FROM rust:1.60
# copy the build artifact from the build stage
COPY --from=build /rocket-example-pro/target/release/rocket_example_pro .
# set the startup command to run your binary
CMD ["./rocket_example_pro"]
正如您最初看到的,我复制了 toml 文件并执行构建,类似于之前演示的那样。然而,由于我的项目结构略有不同,我似乎遇到了问题,因为我的 main.rs 几乎只有一行调用我的 lib.rs 中的主要方法。 lib.rs 也在我的 toml 文件中定义,该文件在构建依赖项之前被复制,并要求我触摸 lib.rs 文件以使其不会在此处构建失败,否则会丢失。
在我似乎无法解决的第二个构建步骤中,在我复制了实际的源文件来构建应用程序之后,我收到了错误消息
Compiling rocket_example_pro v0.1.0 (/rocket-example-pro)
error[E0425]: cannot find function `run` in crate `rocket_example_pro`
--> src/main.rs:3:22
|
3 | rocket_example_pro::run().unwrap();
| ^^^ not found in `rocket_example_pro`
在一个空目录中自己执行这些步骤时,我自己似乎并没有遇到相同的错误,而是最后一步成功了,但是生成的 rocket-example-pro 可执行文件似乎仍然是 shell 示例项目只打印 'Hello world' 而不是我在第二次构建之前复制的火箭应用程序。
据我所知,第一个构建似乎影响了第二个,也许当我触摸虚拟 shell 项目中的 lib.rs 文件时,它构建它时没有 运行() 方法?所以当第二个启动时,它没有看到 运行 方法,因为它是空的?但这对我来说没有多大意义,因为我已经用 运行() 方法复制了 lib.rs 文件。
这里是 toml 文件的样子,如果它有帮助的话:
[package]
name = "rocket_example_pro"
version = "0.1.0"
edition = "2021"
[[bin]]
name = "rocket_example_pro"
path = "src/main.rs"
[lib]
name = "rocket_example_pro"
path = "src/lib.rs"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
...
(一开始我无法重现。然后我注意到至少有一个依赖似乎是一个必要条件。)
同线
RUN rm ./target/release/deps/rocket_example_pro*
您正在强制重建 rocket_example_pro
二进制文件。但是库将保持从第一个空文件构建的状态。尝试更改为
RUN rm ./target/release/deps/librocket_example_pro*
虽然就我个人而言,我认为从 target
目录中删除随机文件是一个非常 hacky 的解决方案。我更愿意通过调整时间戳来触发库的重建:
RUN touch src/lib.rs && cargo build --release --locked ## Doesn't fail anymore
要获得干净的解决方案,请查看 cargo-chef。
[编辑:]那么这里发生了什么?
为了决定是否重建,cargo seems to 将 target/…/*.d
的 mtime 与 *.d
文件内容中列出的文件的 mtime 进行比较。
大概是src/lib.rs
先创建,然后docker build
是运行。所以 src/lib.rs
早于 target/release/librocket_example_pro.d
,导致 target/release/librocket_example_pro.rlib
在复制到 src/lib.rs
后没有被重建。
您可以部分验证这就是正在发生的事情。
- 用原来的Dockerfile,运行
cargo build
,看到失败
- 运行
echo >> src/lib.rs
在 docker 之外更新其 mtime 和 hash
- 运行
cargo build
,成功
请注意,对于第 2 步,使用 touch src/lib.rs
更新 mtime 是不够的,因为 docker 将在 COPY
文件时设置 mtime
,但它会 ignore mtime 在决定是否使用缓存步骤时。
我一直在尝试为我的 rust 构建创建一个 Dockerfile,它允许我从依赖项中单独构建应用程序,如下所示:
然而,这似乎对我不起作用,因为 lib.rs 文件的工作树略有不同。我的 Dockerfile 布局如下:
FROM rust:1.60 as build
# create a new empty shell project
RUN USER=root cargo new --bin rocket-example-pro
WORKDIR /rocket-example-pro
# create dummy lib.rs file to build dependencies separately from changes to src
RUN touch src/lib.rs
# copy over your manifests
COPY ./Cargo.lock ./Cargo.lock
COPY ./Cargo.toml ./Cargo.toml
RUN cargo build --release --locked
RUN rm src/*.rs
# copy your source tree
COPY ./src ./src
# build for release
RUN rm ./target/release/deps/rocket_example_pro*
RUN cargo build --release --locked ## <-- fails
# our final base
FROM rust:1.60
# copy the build artifact from the build stage
COPY --from=build /rocket-example-pro/target/release/rocket_example_pro .
# set the startup command to run your binary
CMD ["./rocket_example_pro"]
正如您最初看到的,我复制了 toml 文件并执行构建,类似于之前演示的那样。然而,由于我的项目结构略有不同,我似乎遇到了问题,因为我的 main.rs 几乎只有一行调用我的 lib.rs 中的主要方法。 lib.rs 也在我的 toml 文件中定义,该文件在构建依赖项之前被复制,并要求我触摸 lib.rs 文件以使其不会在此处构建失败,否则会丢失。
在我似乎无法解决的第二个构建步骤中,在我复制了实际的源文件来构建应用程序之后,我收到了错误消息
Compiling rocket_example_pro v0.1.0 (/rocket-example-pro)
error[E0425]: cannot find function `run` in crate `rocket_example_pro`
--> src/main.rs:3:22
|
3 | rocket_example_pro::run().unwrap();
| ^^^ not found in `rocket_example_pro`
在一个空目录中自己执行这些步骤时,我自己似乎并没有遇到相同的错误,而是最后一步成功了,但是生成的 rocket-example-pro 可执行文件似乎仍然是 shell 示例项目只打印 'Hello world' 而不是我在第二次构建之前复制的火箭应用程序。
据我所知,第一个构建似乎影响了第二个,也许当我触摸虚拟 shell 项目中的 lib.rs 文件时,它构建它时没有 运行() 方法?所以当第二个启动时,它没有看到 运行 方法,因为它是空的?但这对我来说没有多大意义,因为我已经用 运行() 方法复制了 lib.rs 文件。
这里是 toml 文件的样子,如果它有帮助的话:
[package]
name = "rocket_example_pro"
version = "0.1.0"
edition = "2021"
[[bin]]
name = "rocket_example_pro"
path = "src/main.rs"
[lib]
name = "rocket_example_pro"
path = "src/lib.rs"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
...
(一开始我无法重现。然后我注意到至少有一个依赖似乎是一个必要条件。)
同线
RUN rm ./target/release/deps/rocket_example_pro*
您正在强制重建 rocket_example_pro
二进制文件。但是库将保持从第一个空文件构建的状态。尝试更改为
RUN rm ./target/release/deps/librocket_example_pro*
虽然就我个人而言,我认为从 target
目录中删除随机文件是一个非常 hacky 的解决方案。我更愿意通过调整时间戳来触发库的重建:
RUN touch src/lib.rs && cargo build --release --locked ## Doesn't fail anymore
要获得干净的解决方案,请查看 cargo-chef。
[编辑:]那么这里发生了什么?
为了决定是否重建,cargo seems to 将 target/…/*.d
的 mtime 与 *.d
文件内容中列出的文件的 mtime 进行比较。
大概是src/lib.rs
先创建,然后docker build
是运行。所以 src/lib.rs
早于 target/release/librocket_example_pro.d
,导致 target/release/librocket_example_pro.rlib
在复制到 src/lib.rs
后没有被重建。
您可以部分验证这就是正在发生的事情。
- 用原来的Dockerfile,运行
cargo build
,看到失败 - 运行
echo >> src/lib.rs
在 docker 之外更新其 mtime 和 hash - 运行
cargo build
,成功
请注意,对于第 2 步,使用 touch src/lib.rs
更新 mtime 是不够的,因为 docker 将在 COPY
文件时设置 mtime
,但它会 ignore mtime 在决定是否使用缓存步骤时。