Las compilaciones de Docker de varias etapas le permiten escribir Dockerfiles con múltiples
FROM
declaraciones. Esto significa que puedes crear imágenes que se deriven de varias bases, lo que puede ayudar a reducir el tamaño de tu construcción final.
Las imágenes de Docker se crean seleccionando una imagen base usando el
FROM
declaración. Luego agrega capas a esa imagen agregando comandos a su Dockerfile.
Con construcciones de varias etapas, puedes dividir tu Dockerfile en varias secciones. Cada etapa tiene su propia
FROM
declaración para que pueda involucrar más de una imagen en sus compilaciones. Las etapas se construyen secuencialmente y pueden hacer referencia a sus predecesoras, por lo que puede copiar la salida de una capa a la siguiente.
Construcciones de varias etapas en acción
Veamos cómo se puede crear una compilación de varias etapas. Estamos trabajando con un proyecto PHP básico que utiliza Composer para sus dependencias y Sass para sus hojas de estilo.
Aquí hay un Dockerfile de varias etapas que encapsula toda nuestra compilación:
FROM node:14 AS sassWORKDIR /example
RUN npm install -g node-sass
COPY example.scss .
RUN node-sass example.scss example.css
FROM php:8.0-apache
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
COPY composer.json .
COPY composer.lock .
RUN composer install --no-dev
COPY --from=sass /example/example.css example.css
COPY index.php .
COPY src/ src
Inmediatamente observarás que tenemos dos.
FROM
declaraciones que dividen nuestro Dockerfile en dos secciones lógicas. La primera etapa está dedicada a compilar Sass, mientras que la segunda se centra en combinar todo en el contenedor final.
Estamos usando el
node-sass
implementación de Sass. Por lo tanto, comenzamos con una imagen base de Node.JS, dentro de la cual instalamos
node-sass
globalmente desde npm. Luego usamos
node-sass
para compilar nuestra hoja de estilo
example.scss
en el CSS puro
example.css
. El resumen de alto nivel de esta etapa es que tomamos una imagen base, ejecutamos un comando y obtenemos un resultado que nos gustaría usar más adelante en nuestra compilación (
example.css
).
La siguiente etapa presenta la imagen base para nuestra aplicación:
php8.0-apache
. El último
FROM
La declaración en su Dockerfile define la imagen que sus contenedores terminarán ejecutando. nuestro anterior
node
La imagen es, en última instancia, irrelevante para los contenedores de nuestra aplicación: se usa únicamente como una herramienta conveniente en el momento de la compilación.
A continuación usamos Composer para instalar nuestras dependencias de PHP. Composer es el administrador de paquetes de PHP, pero no está incluido con las imágenes oficiales de PHP Docker. Por lo tanto, copiamos el binario en nuestro contenedor desde la imagen dedicada de Composer.
No necesitábamos un
FROM
declaración para hacer esto. Como no estamos ejecutando ningún comando en la imagen de Composer, podemos usar el
--from
bandera con
COPY
para hacer referencia a la imagen. Ordinariamente,
COPY
copia archivos de su contexto de compilación local en su imagen; con
--from
y un nombre de imagen, creará un nuevo contenedor usando esa imagen y luego copiará el archivo especificado.
Más adelante, nuestro Dockerfile usa
COPY --from
De nuevo, esta vez en una forma diferente. De vuelta en la cima, escribimos nuestro primer
FROM
declaración como
FROM node:14 AS sass
. El
AS
cláusula creó una etapa con nombre llamada
sass
.
Ahora hacemos referencia al contenedor transitorio creado por esta etapa usando
COPY --from=sass
. Esto nos permite copiar nuestro CSS integrado en nuestra imagen final. El resto de los pasos son rutinarios.
COPY
operaciones, utilizadas para obtener nuestro código fuente de nuestro directorio de trabajo local.
Ventajas de las construcciones de varias etapas
Las compilaciones de varias etapas le permiten crear rutinas de compilación complejas con un único Dockerfile. Antes de su introducción, era común que los proyectos complejos utilizaran múltiples Dockerfiles, uno para cada etapa de su construcción. Luego, estos debían ser orquestados mediante scripts de shell escritos manualmente.
Con compilaciones de varias etapas, todo nuestro sistema de compilación puede estar contenido en un solo archivo. No necesita ningún script contenedor para llevar su proyecto desde el código base sin formato hasta la imagen final de la aplicación. Un habitual
docker build -t my-image:latest .
es suficiente.
Esta simplificación también brinda oportunidades para mejorar la eficiencia de sus imágenes. Las imágenes de Docker pueden volverse grandes, especialmente si utiliza un tiempo de ejecución de lenguaje como base.
golang
Imagen: está cerca de 300 MB. Tradicionalmente, puedes copiar tu código fuente de Go en la imagen y usarlo para compilar tu binario. Luego copiaría su binario nuevamente a su máquina host antes de comenzar otra compilación. Este usaría un Dockerfile con una imagen base liviana como
alpine
(alrededor de 10 MB). Volverías a agregar tu binario, lo que daría como resultado una imagen mucho más pequeña que si hubieras usado el original.
golang
base para ejecutar sus contenedores.
Con compilaciones de varias etapas, este tipo de sistema es mucho más fácil de implementar:
FROM golang:latestWORKDIR /go
COPY app.go .
RUN go build -o my-binary
FROM alpine:latest
WORKDIR /app
COPY --from=build /go/my-binary .
CMD ["./my-binary"]
En ocho líneas hemos conseguido realizar un procedimiento que antes habría necesitado al menos tres archivos: un
golang
Dockerfile, un
alpine
Dockerfile y un script de shell para gestionar los pasos intermedios.
Conclusión
Las compilaciones de varias etapas pueden simplificar drásticamente la construcción de imágenes complejas de Docker. Le permiten involucrar múltiples pasos de compilación interconectados que pueden transmitir artefactos de salida.
El modelo también promueve la eficiencia de la construcción. La facilidad con la que puede hacer referencia a diferentes imágenes base ayuda a los desarrolladores a garantizar que el resultado final sea lo más pequeño posible. Se beneficiará de costos reducidos de almacenamiento y ancho de banda, lo que puede ser significativo cuando se utiliza Docker dentro de un sistema CI/CD.