¿Cómo crear la imagen de acoplador de trabajo más pequeña cada vez?

19

Objetivo: crear las imágenes de acoplador de trabajo más pequeñas cada vez

Actual

REPOSITORY          TAG       IMAGE ID            CREATED             SIZE
a-docker-image      latest    x                   42 minutes ago       1.92 GB

Intento

Agregar un paso de limpieza al final del Dockerfile:

#clean
RUN apt-get purge -y wget
RUN rm -r a-build-dir
RUN apt-get purge -y a-package

redujo un poco el tamaño de la imagen:

REPOSITORY          TAG       IMAGE ID            CREATED             SIZE
a-docker-image      latest    y                   2 minutes ago       1.86 GB

Discusión

He construido varias imágenes de docker. Cada vez que trato de disminuir el tamaño de la imagen creada, pero siempre siento que es demasiado grande. Estoy buscando un script que ya haya sido creado por alguien en github que elimine todos los paquetes superfluos de la imagen para que el tamaño de la imagen creada sea lo más pequeño posible.

Como dije, siempre trato de reducir el tamaño de la imagen, pero quiero aplicar esto de manera consistente para que cada imagen que cree a partir de ahora sea lo más pequeña posible.

Pregunta

¿Cómo crear la imagen de acoplador de trabajo más pequeña cada vez?

030
fuente

Respuestas:

1

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 ubuntua 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 rmcomandos 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, Co Dantes 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 COPYde 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.

BMitch
fuente
25

A Dockerfilecrea una nueva capa para cada uno de los comandos del archivo. Como las capas están bien, en capas una encima de la otra, no puede eliminar los archivos que agregó una capa anterior. Es por eso que cuando instala paquetes, o descarga archivos, o crea compilaciones cada uno en un comando separado, estos todavía están allí en la imagen, incluso si en una capa futura los eliminó.

Entonces, si solo cambias esto:

RUN apt-get update -y
RUN apt-get install -y wget a-package
# ...
RUN apt-get purge -y wget
RUN rm -r a-build-dir
RUN apt-get purge -y a-package

A esto:

RUN apt-get update -y \
    && apt-get install -y wget a-package \
    && mkdir a-build-dir \
    && wget http://some-site/very-big-source-code.tar.gz \
    && tar xzvf very-big-source-code.tar.gz \
    && do-some-compilation \
    && apt-get purge -y wget \
    && cd .. \
    && rm -rf a-build-dir \
    && apt-get purge -y a-package

Obtendrá una imagen mucho más pequeña.


Otra opción es aplastar la imagen después de haberla construido. P: ¿Cómo funciona el nuevo docker --squash?


Otra opción más es elegir una imagen base delgada. Por ejemplo, las imágenes que usan Alpine Linux como base en lugar de Debian, toman solo 10-15mb en lugar de 180-250mb. Y esto es antes de agregar su propia aplicación y datos. Muchas imágenes base oficiales en Docker Hub tienen una versión alpina.

Evgeny
fuente
3
2.37vs.1.47 GB
030
4

Probablemente no sea exactamente una respuesta, pero vale la pena dar las alternativas.

El hábitat del chef se ha creado con esto en mente, creando un paquete con todas las dependencias necesarias sin la carga extra de imágenes de distribución / base que no desea.

Extrae lo que importa aquí, el tamaño del contenedor de esta publicación de blog con una aplicación simple de nodejs:

michael@ricardo-2:plans_pkg_part_2$ docker images
REPOSITORY           TAG                 IMAGE ID            CREATED             SIZE
mfdii/node-example   latest              36c6568c606b        40 minutes ago      655.9 MB
node                 latest              04c0ca2a8dad        16 hours ago        654.6 MB
mfdii/mytutorialapp  latest              534afd80d74d        2 minutes ago       182.1 MB

mdfii/node-examplees una imagen acoplada de un clásico archivo acoplado, mientras que mfdii/mytutorialappes la imagen acoplada producida con hábitat.

Si el tamaño es su principal preocupación y está dispuesto a tomar la curva de aprendizaje de los planes de Hábitat, esta podría ser una solución para usted.

Tensibai
fuente
0

También se podría usar buceo

docker run --rm -it \
    -v /var/run/docker.sock:/var/run/docker.sock \
    wagoodman/dive:latest <dive arguments...>

para obtener un informe sobre qué desperdicio podría eliminarse de una imagen acoplable para reducir el tamaño.

030
fuente
0

Si desea tener capas de desarrollo reutilizables pero reduce el uso de su disco para la entrega, puede producir una "capa de entrega" fusionada como esta:

  1. Asegúrese de tener un contenedor que use su imagen (si no tiene una, tal vez use algo como docker run IMAGE echo, si el comando echo está disponible)
  2. Encuentra la identificación del contenedor (tal vez usando docker container ls -l)
  3. Canalizar docker exportpara docker importcrear la capa fusionada (algo así como docker export 20f192c6530a | docker import - project:merged)

Esto mantendrá sus capas de desarrollo pero le dará una imagen más pequeña y fusionada que puede entregar.

Bill Burdick
fuente
0

Construcciones de múltiples etapas. Use la imagen que tiene todos sus componentes de compilación para compilar su aplicación y una imagen de tiempo de ejecución más clara. Copie solo su artefacto de construcción a la imagen de tiempo de ejecución. No es necesario eliminar nada.

https://docs.docker.com/develop/develop-images/multistage-build/

frankd
fuente
0

simple ... docker ps verifique las imágenes actuales en ejecución ... para ver un ejemplo de archivo simple a continuación ...

DE ubuntu16

MAINTAINER sreeni (correo electrónico / dominio)

EJECUTAR apt-get update

EJECUTAR apt-get install -y nginx

PUNTO DE ENTRADA ["/ usr / sbin / nginx", "- g", "daemon off;"]

EXPOSE 80 (puerto)

archivo acoplable simple ...

utilice el siguiente comando docker

docker run -d -p 80:80 --name servidor web ubuntu16 (nombre de la imagen) después de eso compruebe localhost o dirección IP: 80 (abra el navegador y verifique)

sreeniwl
fuente
1
por favor arregle el formato de su respuesta ...
Pierre.Vriens