Una imagen acoplable es en realidad una lista vinculada de capas del sistema de archivos. Cada instrucción en un Dockerfile crea una capa del sistema de archivos que describe las diferencias en el sistema de archivos antes y después de la ejecución de la instrucción correspondiente. El docker inspect
subcomando se puede usar en una imagen acoplable para revelar su naturaleza de ser una lista vinculada de capas del sistema de archivos.
El número de capas utilizadas en una imagen es importante.
- al empujar o extraer imágenes, ya que afecta la cantidad de cargas o descargas simultáneas que se producen.
- cuando se inicia un contenedor, ya que las capas se combinan para producir el sistema de archivos utilizado en el contenedor; Cuantas más capas estén involucradas, peor será el rendimiento, pero los diferentes backends del sistema de archivos se ven afectados de manera diferente por esto.
Esto tiene varias consecuencias sobre cómo se deben construir las imágenes. El primer y más importante consejo que puedo dar es:
Consejo # 1 Asegúrese de que los pasos de compilación en los que está involucrado su código fuente lleguen lo más tarde posible en el Dockerfile y no estén vinculados a comandos anteriores usando a &&
o a ;
.
La razón de esto es que todos los pasos anteriores se almacenarán en caché y no será necesario descargar las capas correspondientes una y otra vez. Esto significa compilaciones más rápidas y lanzamientos más rápidos, que es probablemente lo que desea. Curiosamente, es sorprendentemente difícil hacer un uso óptimo de la memoria caché del acoplador.
Mi segundo consejo es menos importante, pero lo encuentro muy útil desde el punto de vista del mantenimiento:
Consejo # 2 No escriba comandos complejos en el Dockerfile, sino que use scripts que se copiarán y ejecutarán.
Un Dockerfile siguiendo este consejo sería
COPY apt_setup.sh /root/
RUN sh -x /root/apt_setup.sh
COPY install_pacakges.sh /root/
RUN sh -x /root/install_packages.sh
y así. El consejo de vincular varios comandos &&
solo tiene un alcance limitado. Es mucho más fácil escribir con scripts, donde puede usar funciones, etc. para evitar redundancias o con fines de documentación.
Las personas interesadas por los preprocesadores y dispuestos a evitar los pequeños gastos generales causados por los COPY
pasos y en realidad están generando sobre la marcha un Dockerfile donde el
COPY apt_setup.sh /root/
RUN sh -x /root/apt_setup.sh
las secuencias son reemplazadas por
RUN base64 --decode … | sh -x
donde …
es la versión codificada en base64 de apt_setup.sh
.
Mi tercer consejo es para las personas que desean limitar el tamaño y el número de capas al posible costo de construcciones más largas.
Consejo # 3 Use with
-idiom para evitar archivos presentes en capas intermedias pero no en el sistema de archivos resultante.
Un archivo agregado por alguna instrucción acoplable y eliminado por alguna instrucción posterior no está presente en el sistema de archivos resultante, pero se menciona dos veces en las capas acoplables que constituyen la imagen del acoplador en la construcción. Una vez, con el nombre y el contenido completo en la capa resultante de la instrucción que lo agrega, y una vez como un aviso de eliminación en la capa resultante de la instrucción que lo elimina.
Por ejemplo, supongamos que necesitamos temporalmente un compilador de C y alguna imagen y considere el
# !!! THIS DISPLAYS SOME PROBLEM --- DO NOT USE !!!
RUN apt-get install -y gcc
RUN gcc --version
RUN apt-get --purge autoremove -y gcc
(Un ejemplo más realista construiría algún software con el compilador en lugar de simplemente afirmar su presencia con la --version
bandera).
El fragmento de Dockerfile crea tres capas, la primera contiene el paquete completo de gcc, de modo que incluso si no está presente en el sistema de archivos final, los datos correspondientes siguen siendo parte de la imagen de la misma manera y deben descargarse, cargarse y desempacarse siempre que La imagen final es.
El with
-idiom es una forma común en la programación funcional para aislar la propiedad del recurso y la liberación del recurso de la lógica que lo usa. Es fácil transponer este modismo al scripting de shell, y podemos reformular los comandos anteriores como el siguiente script, para usarlo COPY & RUN
como en el Consejo # 2.
# with_c_compiler SIMPLE-COMMAND
# Execute SIMPLE-COMMAND in a sub-shell with gcc being available.
with_c_compiler()
(
set -e
apt-get install -y gcc
"$@"
trap 'apt-get --purge autoremove -y gcc' EXIT
)
with_c_compiler\
gcc --version
Los comandos complejos se pueden convertir en funciones para que se puedan alimentar al with_c_compiler
. También es posible encadenar llamadas de varias with_whatever
funciones, pero tal vez no sea muy deseable. (Usando características más esotéricas del shell, ciertamente es posible hacer que los with_c_compiler
comandos complejos sean aceptados, pero es preferible en todos los aspectos incluir estos comandos complejos en funciones).
Si queremos ignorar el Consejo # 2, el fragmento de Dockerfile resultante sería
RUN apt-get install -y gcc\
&& gcc --version\
&& apt-get --purge autoremove -y gcc
que no es tan fácil de leer y mantener debido a la ofuscación. Vea cómo la variante shell-script gcc --version
resalta la parte importante mientras que la &&
variante encadenada entierra esa parte en medio del ruido.