优化Docker中的货物构建时间

jyztefdp  于 2022-11-22  发布在  Docker
关注(0)|答案(5)|浏览(159)

我正在用Rust开发一个API,并且正在用Docker管理环境,包括外部数据库。每次我对API代码进行更改时,货物都会重新构建,并且由于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

这是我的货物

[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"] }
nnt7mjpx

nnt7mjpx1#

如果源代码没有改变,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将拿起它并构建一个具有更新的依赖关系的新层。

bq9c1y66

bq9c1y662#

这个问题到现在已经有一年半的历史了。仍然没有cargo build --deps-only选项,但是我想我应该分享我的解决方案,它是相当轻量级的。你不需要修改你的任何主机文件来做到这一点:

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

这将构建依赖项并缓存它们。稍后当你复制实际的源文件时(或者在我的例子中,使用--volumes),它将覆盖伪文件,所以伪文件完全是临时的。如果需要,你也可以在构建之后显式地使用rm

wpx232ag

wpx232ag3#

您也可以让建置失败,而不加入虚拟档案:

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

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

dw1jzc5e

dw1jzc5e4#

我对此做了一些深入研究,似乎最好的解决方案是分多个阶段进行。最长的一个阶段是初始索引下载。还要注意,依赖关系预构建步骤使target/的可预测性降低,因此为了得到更清晰的结果,删除它可能是有意义的。不过,它应该可以很好地用于开发/增量构建。
我用alpine做了这个,但是它对任何其他的Rust基础都是一样的--只需要把系统包步骤改为另一个包管理器,比如apt

FROM rust:alpine
ARG RUST_APP_NAME=myapp
WORKDIR /rust_app

# Each of these steps may take a long time, so do them separately to take advantage of Docker caching.
# The order of the steps is important as they are ordered by the likelihood of change.
#
# Downloading the crates.io index. This might be the slowest of the steps.
# Note that we must have at least one dependency in Cargo.toml for fetch to work.
RUN cargo init && \
    cargo add serde && \
    cargo fetch && \
    rm -rf *

# Install system packages
RUN apk add --no-cache musl-dev

# Download just the dependencies listed in the Cargo.toml
COPY Cargo.toml /rust_app/
COPY Cargo.lock /rust_app/
RUN mkdir -p src && \
    echo 'fn main() { eprintln!("The build broke!"); std::process::exit(1); }' > src/main.rs && \
    cargo fetch && \
    rm -rf src

# Compile just the dependencies listed in the Cargo.toml
# Note that this step will create some intermediate files in the target/ directory.
# Cargo will rebuild the final binary/lib in the next step, but for cleaner build
# you may want to remove this step and let it recompile all dependencies on each docker build.
RUN mkdir -p src && \
    echo 'fn main() { eprintln!("The build broke!"); std::process::exit(1); }' > src/main.rs && \
    cargo build --release && \
    rm -rf src && \
    rm -rf target/release/deps/${RUST_APP_NAME}-* && \
    rm -rf target/release/deps/lib${RUST_APP_NAME}-* && \
    rm -rf target/release/.fingerprint/${RUST_APP_NAME}-* && \
    rm -rf target/release/.fingerprint/lib${RUST_APP_NAME}-* && \
    rm -rf target/release/incremental/${RUST_APP_NAME}-* && \
    rm -rf target/release/incremental/lib${RUST_APP_NAME}-* && \
    rm -rf target/release/${RUST_APP_NAME}* && \
    rm -rf target/release/lib${RUST_APP_NAME}*

# Copy the source code and build the target
ADD . .
RUN cargo build --release
9avjhtql

9avjhtql5#

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

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 .
这样,只会重新构建mybase映像

相关问题