I am developing an API with Rust, and am managing the environments, including the external database with Docker. Every time I make a change to the API code, cargo rebuilds, and since Docker doesn't cache anything to do with the ADD
statement to copy the Rust directory over to the container, it re-downloads all the packages, which is a fairly lengthy process since I'm using Nickel, which seems to have a boatload of dependencies.
Is there a way to bring those dependencies in prior to running cargo build
? At least that way if the dependencies change it will only install what's required, similar to Cargo compiling locally.
Here's the Dockerfile I currently use:
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
And here's my 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"] }
You can create an intermediate image and build your final image from it. Eg:
build using
docker build -t mybaseimage .
docker build -t finalimage .
That way only the mybaseimage is rebuilt
Docker does cache the layer built from the
ADD
(preferablyCOPY
) instruction, provided the sources haven't changed. You could make use of that and get your dependencies cached by copying theCargo.toml
in first, and doing a build.But unfortunately you need something to build, so you could do it with a single source file and a dummy
lib
target in your manifest:In your Dockerfile build the dummy separately:
The output of this layer will be cached, with all the dependencies installed, and then you can go on to add the rest of your code (in the same
Dockerfile
):The
dummy
stuff is ugly, but it means your normal build will be quick, as it comes from the cached layer, and when you change dependencies in yourCargo.toml
then Docker will pick it up and build a new layer with updated dependencies.This question is a year and a half old at this point. Still, no
cargo build --deps-only
option, but I thought I'd share my solution, which is reasonably lightweight. You don't have to modify any of your host files to do it:This will build the dependencies and cache them. Later when you copy in the actual source files (or in my case, use
--volumes
), it will overwrite the dummy file, so the dummy file is totally temporary. You could also explicitlyrm
it after the build if you needed to.Instead of adding a dummy file, you can also let the build fail:
Don't forget to add
--release
to both places if you want optimized builds.