【问题标题】:Optimising cargo build times in Docker优化 Docker 中的货物构建时间
【发布时间】:2017-02-01 08:03:32
【问题描述】:

我正在使用 Rust 开发 API,并管理环境,包括使用 Docker 的外部数据库。每次我对 API 代码进行更改时,cargo 都会重新构建,并且由于 Docker 不会缓存与 ADD 语句相关的任何内容以将 Rust 目录复制到容器中,它会重新下载所有包,即自从我使用 Nickel 以来,这是一个相当漫长的过程,它似乎有很多依赖项。

有没有办法在运行cargo build 之前引入这些依赖项?至少这样,如果依赖项发生变化,它只会安装所需的内容,类似于本地编译的 Cargo。

这是我目前使用的 Dockerfile:

FROM ubuntu:xenial
RUN apt-get update && apt-get install curl build-essential ca-certificates file xutils-dev nmap -y
RUN mkdir /rust
WORKDIR /rust
RUN curl https://sh.rustup.rs -s >> rustup.sh
RUN chmod 755 /rust/rustup.sh
RUN ./rustup.sh -y
ENV PATH=/root/.cargo/bin:$PATH SSL_VERSION=1.0.2h
RUN rustup default 1.11.0
RUN curl https://www.openssl.org/source/openssl-$SSL_VERSION.tar.gz -O && \
    tar -xzf openssl-$SSL_VERSION.tar.gz && \
    cd openssl-$SSL_VERSION && ./config && make depend && make install && \
    cd .. && rm -rf openssl-$SSL_VERSION*
ENV OPENSSL_LIB_DIR=/usr/local/ssl/lib \
    OPENSSL_INCLUDE_DIR=/usr/local/ssl/include \
    OPENSSL_STATIC=1
RUN mkdir /app
WORKDIR /app
ADD . /app/
RUN cargo build
EXPOSE 20000
CMD ./target/debug/api

这是我的 Cargo.toml

[profile.dev]
debug = true

[package]
name = "api"
version = "0.0.1"
authors = ["Vignesh Sankaran <developer@ferndrop.com>"]

[dependencies]
nickel = "= 0.8.1"
mongodb = "= 0.1.6"
bson = "= 0.3.0"
uuid = { version = "= 0.3.1", features = ["v4"] }

【问题讨论】:

  • 也许你可以先添加并编译一个具有相同依赖项的虚拟应用程序。
  • github.com/rust-lang/cargo/issues/1891。另请注意,您可以一步安装正确的工具链./rustup.sh -y --default-toolchain 1.11.0

标签: docker dependencies rust rust-cargo


【解决方案1】:

Docker 确实缓存了由ADD(最好是COPY)指令构建的层,前提是源没有改变。您可以利用它并通过首先复制Cargo.toml 并进行构建来缓存您的依赖项。

但不幸的是,您需要构建一些东西,因此您可以使用单个源文件和清单中的虚拟 lib 目标来完成:

[lib]
name = "dummy"
path = "dummy.rs"

在您的 Dockerfile 中单独构建虚拟对象:

COPY Cargo.toml /app/Cargo.toml
COPY dummy.rs /app/dummy.rs
RUN cargo build --lib

该层的输出将被缓存,所有依赖项都已安装,然后您可以继续添加其余代码(在同一 Dockerfile 中):

COPY /src/ app/src/
RUN cargo build

dummy 的东西很丑,但这意味着你的正常构建会很快,因为它来自缓存层,当你更改 Cargo.toml 中的依赖项时,Docker 会选择它并构建一个新层具有更新的依赖项。

【讨论】:

  • 首先复制Cargo.toml Cargo.lock!
  • 我已经在 Ruby 项目中成功使用了这种技术,复制到 Gemfile / Gemfile.lock 并运行 bundle install
  • 为什么是COPY 而不是ADD
  • COPY 只是将文件/目录从上下文复制到图像层。 ADD 也可以这样做,但它也可以使用网址作为源,如果源是压缩文件,它会将其解压缩到图像上。并不总是期望该功能,因此除非您知道自己需要它,否则最好使用COPY
【解决方案2】:

这个问题已经一年半了。尽管如此,没有cargo build --deps-only 选项,但我想我会分享我的解决方案,它相当轻量级。您无需修改​​任何主机文件即可:

COPY Cargo.toml .
RUN mkdir src \
    && echo "// dummy file" > src/lib.rs \
    && cargo build

这将构建依赖项并缓存它们。稍后当您复制实际的源文件(或者在我的情况下,使用--volumes)时,它将覆盖虚拟文件,因此虚拟文件完全是临时的。如果需要,您也可以在构建之后显式地 rm 它。

【讨论】:

  • 货物中是否存在关于--deps-only 标志的问题?
【解决方案3】:

你也可以让构建失败,而不是添加一个虚拟文件:

RUN cargo build || true
COPY ...
RUN cargo build

如果您想要优化构建,请不要忘记将--release 添加到这两个位置。

【讨论】:

  • 我还需要复制cargo.lockcargo.toml,对吧?
【解决方案4】:

您可以创建一个中间图像并从中构建您的最终图像。 例如:

FROM ubuntu:xenial
RUN apt-get update && apt-get install curl build-essential ca-certificates file xutils-dev nmap -y
RUN mkdir /rust
...

使用docker build -t mybaseimage .构建

FROM mybaseimage
RUN mkdir /app
WORKDIR /app
ADD . /app/
RUN cargo build
EXPOSE 20000
CMD ./target/debug/api

docker build -t finalimage .

这样只会重建 mybaseimage

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-06-14
    • 2021-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-08
    • 1970-01-01
    相关资源
    最近更新 更多