Hay una variedad de técnicas involucradas, sin una solución única. Es probable que desee hacer varias de las siguientes acciones:
Primero, optimice sus capas de imágenes para su reutilización. Coloque pasos que cambian con frecuencia más adelante en el Dockerfile para aumentar las posibilidades de que las primeras capas se almacenen en caché de compilaciones anteriores. Una capa reutilizada se mostrará como más espacio en disco en un docker image ls
, pero si examina el sistema de archivos subyacente, solo una copia de cada capa se almacena en el disco. Eso significa 3 imágenes de 2 GB cada una, pero que solo tienen 50 MB diferentes en las últimas capas de la compilación, solo ocuparán 2.1 GB de espacio en disco, a pesar de que la lista parece que están usando 6 GB ya que contando dos veces cada una de las capas reutilizadas.
La reutilización de capas es la razón por la que ve imágenes con dependencias de compilación que cambian con poca frecuencia, instale esas primero antes de copiar el código. Vea cualquier ejemplo de Python que tenga un patrón como:
FROM python
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
# note how the code is copied only after the pip install
# since code changes but requirements.txt doesn't
COPY . .
CMD ["gunicorn", "app:app"]
Elija una imagen base mínima. Es por esto que se ve gente va de ubuntu
a debian:slim
(las variantes delgadas son más pequeñas, con menos herramientas de envío), o incluso alpine
. Esto reduce el tamaño de su punto de partida y es muy útil si constantemente está sacando nuevas versiones de la imagen base. Sin embargo, si su imagen base rara vez cambia, la reutilización de capas elimina gran parte de la ventaja de una imagen base mínima.
La imagen base más pequeña que puede elegir es scratch
, que no es nada, ni shell ni bibliotecas, y solo es útil con binarios compilados estáticamente. De lo contrario, elija una imagen base que incluya las herramientas que necesita sin muchas herramientas que no necesita.
A continuación, cualquier paso que cambie o elimine un archivo debe combinarse con los pasos anteriores que crean ese archivo. De lo contrario, el sistema de archivos en capas, que usa copia en escritura incluso en cosas como un cambio de permiso de archivo, tendrá el archivo original en una capa anterior y el tamaño de la imagen no se reducirá cuando elimine archivos. Es por eso que sus rm
comandos no tienen efecto en el espacio en disco resultante. En cambio, puede encadenar los comandos, como:
RUN apt-get update \
&& apt-get install -y \
a-package \
wget \
&& ... \
&& apt-get purge -y wget \
&& rm -r a-build-dir \
&& apt-get purge -y a-package
Tenga en cuenta que el uso excesivo del encadenamiento de comandos puede ralentizar sus compilaciones, ya que debe reinstalar el mismo conjunto de herramientas cada vez que cambie un requisito previo (por ejemplo, el código que se extrae con wget). Vea las etapas múltiples a continuación para una mejor alternativa.
Cualquier archivo que cree que no necesita en su imagen resultante debe eliminarse, en el paso que lo crea. Esto incluye cachés de paquetes, registros, páginas de manual, etc. Para descubrir qué archivos se están creando en cada capa, puede usar una herramienta como wagoodman / dive (que no he investigado personalmente y expresaría precaución ya que se ejecuta con acceso completo a la raíz en su host), o puede construir sus imágenes de acoplador sin recortar los contenedores intermedios y luego ver el diff con:
# first create and leave containers from any RUN step using options on build
docker image build --rm=false --no-cache -t image_name .
# review which layers use an unexpectedly large amount of space
docker image history image_name
# list all containers, particularly the exited ones from above
docker container ps -a
# examine any of those containers
docker container diff ${container_id}
# ... repeat the diff for other build steps
# then cleanup exited containers
docker container prune
Con cada uno de esos recipientes intermedios, el diff mostrará lo que se añaden archivos, cambia o elimina porque la etapa (estos se indican con un A
, C
o D
antes de cada nombre de archivo). Lo que está mostrando diff es el sistema de archivos de lectura / escritura específico del contenedor, que es cualquier archivo cambiado por el contenedor desde el estado de la imagen mediante la copia en escritura.
La mejor manera de reducir el tamaño de la imagen es eliminar los componentes innecesarios, como los compiladores, de la imagen enviada. Para eso, las compilaciones de varias etapas le permiten compilar en una etapa y luego copiar solo los artefactos resultantes de la etapa de compilación en una imagen de tiempo de ejecución que solo tiene el mínimo necesario para ejecutar la aplicación. Esto evita la necesidad de optimizar cualquiera de los pasos de compilación, ya que no se envían con la imagen resultante.
FROM debian:9 as build
# still chain update with install to prevent stale cache issues
RUN apt-get update \
&& apt-get install -y \
a-package \
wget \
RUN ... # perform any download/compile steps
FROM debian:9-slim as release
COPY --from=build /usr/local/bin/app /usr/local/bin/app
CMD [ "/usr/local/bin/app" ]
Multi-stage es ideal con binarios compilados estáticamente que puede ejecutar desde cero como imagen base, o pasar de un entorno de compilación como JDK a un tiempo de ejecución como JRE. Esta es la forma más fácil de reducir drásticamente el tamaño de la imagen sin dejar de tener una construcción rápida. Aún puede realizar el encadenamiento de pasos en su etapa de lanzamiento si tiene pasos que cambian o eliminan archivos creados en pasos anteriores, pero en su mayor parte, el COPY
de otra etapa aísla la etapa de lanzamiento de cualquier expansión de capa experimentada en las etapas de compilación anteriores.
Tenga en cuenta que no recomiendo aplastar imágenes, ya que esto reduce el tamaño de una imagen a expensas de eliminar la reutilización de capas. Eso significa que las futuras compilaciones de la misma imagen requerirán más tráfico de disco y de red para enviar actualizaciones. Para volver al primer ejemplo, el aplastamiento puede reducir su imagen de 2 GB a 1 GB, pero no 3 imágenes pueden ocupar 3 GB en lugar de los 2.1 GB.
2.37
vs.1.47 GB