run - docker tag example
Optimizar los tiempos de construcción de carga en Docker (3)
Estoy desarrollando una API con Rust y estoy administrando los entornos, incluida la base de datos externa con Docker. Cada vez que realizo un cambio en el código API, la carga se reconstruye, y como Docker no guarda nada en la declaración ADD
para copiar el directorio Rust en el contenedor, vuelve a descargar todos los paquetes, lo que es bastante proceso largo ya que estoy usando Nickel, que parece tener un montón de dependencias.
¿Hay alguna forma de incorporar esas dependencias antes de ejecutar cargo build
? Al menos, si las dependencias cambian, solo instalará lo que se requiere, similar a la compilación de carga local.
Aquí está el archivo Docker que uso actualmente:
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
Y aquí está mi Cargo.toml
[profile.dev]
debug = true
[package]
name = "api"
version = "0.0.1"
authors = ["Vignesh Sankaran <[email protected]>"]
[dependencies]
nickel = "= 0.8.1"
mongodb = "= 0.1.6"
bson = "= 0.3.0"
uuid = { version = "= 0.3.1", features = ["v4"] }
Docker almacena en caché la capa creada a partir de la instrucción ADD
(preferiblemente COPY
), siempre que las fuentes no hayan cambiado. Puede hacer uso de eso y obtener sus dependencias en caché copiando Cargo.toml
primero y haciendo una compilación.
Pero, lamentablemente, necesitas algo para compilar, por lo que puedes hacerlo con un único archivo fuente y un objetivo ficticio lib
en tu manifiesto:
[lib]
name = "dummy"
path = "dummy.rs"
En tu archivo Docker, crea el dummy por separado:
COPY Cargo.toml /app/Cargo.toml
COPY dummy.rs /app/dummy.rs
RUN cargo build --lib
La salida de esta capa se almacenará en caché, con todas las dependencias instaladas, y luego podrá continuar agregando el resto de su código (en el mismo Dockerfile
):
COPY /src/ app/src/
RUN cargo build
Las cosas dummy
son feas, pero significa que tu compilación normal será rápida, ya que proviene de la capa en caché, y cuando cambias las dependencias en tu Cargo.toml
, Docker lo levantará y construirá una nueva capa con dependencias actualizadas.
Puede crear una imagen intermedia y crear su imagen final a partir de ella. P.ej:
FROM ubuntu:xenial
RUN apt-get update && apt-get install curl build-essential ca-certificates file xutils-dev nmap -y
RUN mkdir /rust
...
construir usando la construcción de 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 .
De esa forma solo se reconstruye la imagen mybase
En lugar de agregar un archivo ficticio, también puede dejar que falle la compilación:
RUN cargo build || true
COPY ...
RUN cargo build
No olvides agregar --release
a ambos lugares si quieres compilaciones optimizadas.