GNU/Linux >> Tutoriales Linux >  >> Panels >> Docker

Cómo reducir el tamaño de la imagen de Docker:6 métodos de optimización

Si desea reducir el tamaño de la imagen de la ventana acoplable , debe usar las mejores prácticas estándar para crear una imagen de Docker.

Este blog habla sobre diferentes técnicas de optimización que puede implementar rápidamente para crear la imagen acoplable más pequeña y mínima. . También veremos algunas de las mejores herramientas para la optimización de imágenes de Docker. .

Docker como motor de contenedor facilita tomar un fragmento de código y ejecutarlo dentro de un contenedor. Permite a los ingenieros recopilar todas las dependencias del código y los archivos en una única ubicación que se puede ejecutar en cualquier lugar, de forma bastante rápida y sencilla.

Todo el concepto de imágenes "ejecutar en cualquier lugar" comienza con un archivo de configuración simple llamado Dockerfile. Primero, agregamos todas las instrucciones de compilación, como las dependencias del código, los comandos y los detalles de la imagen base, en Dockerfile.

Necesidad de optimización de imágenes de Docker

Aunque el proceso de creación de Docker es sencillo, muchas organizaciones cometen el error de crear imágenes de Docker infladas. sin optimizar las imágenes del contenedor.

En el desarrollo de software típico, cada servicio tendrá múltiples versiones/lanzamientos, y cada versión requiere más dependencias, comandos y configuraciones. Esto presenta un desafío en la compilación de imágenes de Docker, como ahora:el mismo código requiere más tiempo y recursos para compilarse antes de que pueda enviarse como un contenedor.

He visto casos en los que la imagen de la aplicación inicial comenzaba con 350 MB y, con el tiempo, aumentó a más de 1,5 GB.

Además, al instalar bibliotecas no deseadas, aumentamos la posibilidad de un posible riesgo de seguridad al aumentar la superficie de ataque.

Por lo tanto, los ingenieros de DevOps deben optimizar las imágenes de la ventana acoplable para asegurarse de que la imagen de la ventana acoplable no se hinche después de las compilaciones de la aplicación o versiones futuras. No solo para entornos de producción, en cada etapa del proceso de CI/CD, debe optimizar sus imágenes acoplables.

Además, con herramientas de orquestación de contenedores como Kubernetes, es mejor tener imágenes de tamaño pequeño para reducir la transferencia de imágenes y el tiempo de implementación. .

¿Cómo reducir el tamaño de la imagen de Docker?

Si tomamos una imagen de contenedor de una aplicación típica, contiene una imagen base, Dependencias/Archivos/Configuraciones , y crudo (software no deseado).

Todo se reduce a la eficiencia con la que podemos administrar estos recursos dentro de la imagen del contenedor.

Veamos diferentes métodos establecidos para optimizar las imágenes de Docker. Además, proporcionamos ejemplos prácticos para comprender la optimización de imágenes de la ventana acoplable en tiempo real.

O utiliza los ejemplos proporcionados en el artículo o prueba las técnicas de optimización en Dockerfiles existentes.

Los siguientes son los métodos mediante los cuales podemos lograr la optimización de la imagen de la ventana acoplable.

  1. Uso de imágenes base mínimas/sin distribución
  2. Construcciones de varias etapas
  3. Minimizar el número de capas
  4. Comprender el almacenamiento en caché
  5. Uso de Dockerignore
  6. Mantener los datos de la aplicación en otro lugar

Archivos de ejercicios de Docker: Todo el código de la aplicación, los Dockerfiles y las configuraciones que se usan en este artículo están alojados en este repositorio de Github. Puedes clonarlo y seguir el tutorial.

Método 1:usar imágenes base mínimas

Su primer enfoque debe ser elegir la imagen base correcta con una huella mínima del sistema operativo.

Un ejemplo de ello son las imágenes de base alpina. Las imágenes alpinas pueden ser tan pequeñas como 5,59 MB . No es solo pequeño; también es muy seguro.

alpine       latest    c059bfaa849c     5.59MB

La imagen base de Nginx alpine tiene solo 22 MB.

De forma predeterminada, viene con el shell sh que ayuda a depurar el contenedor al adjuntarlo.

Puede reducir aún más el tamaño de la imagen base utilizando imágenes sin distribución. Es una versión simplificada del sistema operativo. Las imágenes base sin distribución están disponibles para java, nodejs, python, Rust, etc.

Las imágenes sin distribución son tan mínimas que no ni siquiera tienen un caparazón . Entonces, podría preguntarse, entonces, ¿cómo depuramos las aplicaciones? Tienen la versión de depuración de la misma imagen que viene con el cuadro ocupado para la depuración.

Además, la mayoría de las distribuciones ahora tienen sus imágenes base mínimas.

Nota: No puede usar directamente las imágenes base disponibles públicamente en entornos de proyecto. Debe obtener la aprobación del equipo de seguridad de la empresa para usar la imagen base. En algunas organizaciones, el propio equipo de seguridad publica imágenes base todos los meses después de las pruebas y el escaneo de seguridad. Esas imágenes estarían disponibles en el repositorio privado de la ventana acoplable de la organización común.

Método 2:usar compilaciones de varias etapas de Docker

El patrón de construcción de varias etapas se desarrolla a partir del concepto de patrón de construcción en el que usamos diferentes Dockerfiles para construir y empaquetar el código de la aplicación. Aunque este patrón ayuda a reducir el tamaño de la imagen, genera poca sobrecarga cuando se trata de crear canalizaciones.

En la construcción de varias etapas, obtenemos ventajas similares a las del patrón de construcción. Usamos imágenes intermedias (etapas de compilación) para compilar código, instalar dependencias y empaquetar archivos en este enfoque. La idea detrás de esto es eliminar capas no deseadas en la imagen.

Después de eso, solo los archivos de la aplicación necesarios para ejecutar la aplicación se copian en otra imagen con solo las bibliotecas requeridas, es decir, más ligeras para ejecutar la aplicación.

Veamos esto en acción, con la ayuda de un ejemplo práctico donde creamos una aplicación Nodejs simple y optimizamos su Dockerfile.

Primero, vamos a crear el código. Tendremos la siguiente estructura de carpetas.

├── Dockerfile1
├── Dockerfile2
├── env
├── index.js
└── package.json

Guarde lo siguiente como index.js .

const dotenv=require('dotenv'); 
dotenv.config({ path: './env' });

dotenv.config();

const express=require("express");
const app=express();

app.get('/',(req,res)=>{
  res.send(`Learning to Optimize Docker Images with DevOpsCube!`);
});


app.listen(process.env.PORT,(err)=>{
    if(err){
        console.log(`Error: ${err.message}`);
    }else{
        console.log(`Listening on port ${process.env.PORT}`);
    }
  }
)

Guarde lo siguiente como package.json .

{
  "name": "nodejs",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "dotenv": "^10.0.0",
    "express": "^4.17.2"
  }
}

Guarde la siguiente variable de puerto en un archivo llamado env .

PORT=8080

Un Dockerfile simple para esta aplicación le gustaría esto:guárdelo como Dockerfile1 .

FROM node:16
COPY . .
RUN npm installEXPOSE 3000
CMD [ "node", "index.js" ]

Veamos el espacio de almacenamiento que requiere construyéndolo.

docker build -t devopscube/node-app:1.0 --no-cache -f Dockerfile1 .

Después de que se complete la construcción. Verifiquemos su tamaño usando –

docker image ls

Esto es lo que obtenemos.

devopscube/node-app   1.0       b15397d01cca   22 seconds ago   910MB

Entonces el tamaño es 910MBs .

Ahora, usemos este método para crear una compilación de varias etapas.

Usaremos node:16 como imagen base, es decir, la imagen para todas las instalaciones de módulos y dependencias, después de eso, moveremos el contenido a un mínimo y más ligero 'alpine 'imagen basada. El 'alpine La imagen tiene las utilidades mínimas y, por lo tanto, es muy ligera.

Esta es una representación pictórica de una compilación de varias etapas de Docker.

Además, en un solo Dockerfile , puede tener varias etapas con diferentes imágenes base. Por ejemplo, puede tener diferentes etapas para compilación, prueba, análisis estático y paquete con diferentes imágenes base.

Veamos cómo se vería el nuevo Dockerfile. Solo estamos copiando los archivos necesarios de la imagen base a la imagen principal.

Guarde lo siguiente como Dockerfile2 .

FROM node:16 as build

WORKDIR /app
COPY package.json index.js env ./
RUN npm install

FROM node:alpine as main

COPY --from=build /app /
EXPOSE 8080
CMD ["index.js"]

Veamos el espacio de almacenamiento que requiere construyéndolo.

docker build -t devopscube/node-app:2.0 --no-cache -f Dockerfile2 .

Después de que se complete la construcción. Comprobemos su tamaño usando

docker image ls

Esto es lo que obtenemos.

devopscube/node-app   2.0       fa6ae75da252   32 seconds ago   171MB

Entonces, el nuevo tamaño de imagen reducido es de 171 MB en comparación con la imagen con todas las dependencias.

¡Eso es una optimización de más del 80 %!

Sin embargo, si hubiéramos usado la misma imagen base que usamos en la etapa de construcción, no veríamos mucha diferencia.

Puede reducir aún más el tamaño de la imagen usando imágenes sin distribución . Aquí está el mismo Dockerfile con un paso de compilación de varias etapas que usa la imagen distroless de google nodeJS en lugar de alpine.

FROM node:16 as build

WORKDIR /app
COPY package.json index.js env ./
RUN npm install

FROM gcr.io/distroless/nodejs

COPY --from=build /app /
EXPOSE 3000
CMD ["index.js"]

Si crea el Dockerfile anterior, su imagen tendrá 118 MB ,

devopscube/distroless-node   1.0       302990bc5e76     118MB

Método 3:minimizar el número de capas

Las imágenes de Docker funcionan de la siguiente manera:cada RUN, COPY, FROM Las instrucciones de Dockerfile agregan una nueva capa y cada capa aumenta el tiempo de ejecución de la compilación y aumenta los requisitos de almacenamiento de la imagen.

Veamos esto en acción, con la ayuda de un ejemplo práctico:creemos una imagen de ubuntu con bibliotecas actualizadas y actualizadas, junto con algunos paquetes necesarios instalados, como vim, net-tools, dnsutils.

Un Dockerfile para lograr esto sería lo siguiente:Guardar esto como Dockerfile3 .

FROM ubuntu:latest
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update -y
RUN apt-get upgrade -y
RUN apt-get install vim -y
RUN apt-get install net-tools -y
RUN apt-get install dnsutils -y

También deseamos analizar el tiempo de compilación de esta imagen.

El demonio Docker tiene una capacidad incorporada para mostrar el tiempo de ejecución total que está tomando un Dockerfile.

Para habilitar esta función, siga los siguientes pasos:

  1. Cree un daemon.json archivo con el siguiente contenido en /etc/docker/
{
  "experimental": true
}

2. Ejecute el siguiente comando para habilitar la función.

export DOCKER_BUILDKIT=1

Construyámoslo y veamos el almacenamiento y el tiempo de construcción.

time docker build -t devopscube/optimize:3.0 --no-cache -f Dockerfile3 .

Mostraría los tiempos de ejecución en la terminal.

time docker build -t devopscube/optimize:3.0 --no-cache -f Dockerfile3 .


[+] Building 117.1s (10/10) FINISHED                                                                   
 => [internal] load build definition from Dockerfile                                              
.
.
.
.                                                                       
 => => writing image sha256:9601bcac010062c656dacacbc7c554b8ba552c7174f32fdcbd24ff9c7482a805      0.0s 
 => => naming to docker.io/devopscube/optimize:3.0                                                0.0s 
                                                                                                       
real    1m57.219s                                                                                      
user	0m1.062s
sys	0m0.911s

Una vez completada la compilación, el tiempo de ejecución llega a ser 117,1 segundos .

Comprobemos su tamaño usando

docker image ls

Esto es lo que obtenemos.

devopscube/optimize  3.0   9601bcac0100   About a minute ago   227MB

Entonces el tamaño es de 227 MB .

Combinemos los comandos EJECUTAR en una sola capa y guárdelo como Dockerfile4.

FROM ubuntu:latest
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update -y && apt-get upgrade -y && apt-get install --no-install-recommends vim net-tools dnsutils -y

En el comando EJECUTAR anterior, hemos usado --no-install-recommends marca para deshabilitar los paquetes recomendados. Se recomienda siempre que utilice install en tus Dockerfiles

Veamos el almacenamiento y el tiempo de construcción necesarios para construirlo.

time docker build -t devopscube/optimize:4.0 --no-cache -f Dockerfile4 .

Mostraría los tiempos de ejecución en la terminal.

time docker build -t devopscube/optimize:0.4 --no-cache -f Dockerfile4 .


[+] Building 91.7s (6/6) FINISHED                                                                      
 => [internal] load build definition from Dockerfile2                                             0.4s
.
.
.  
 => => naming to docker.io/devopscube/optimize:4.0                                                0.0s 
                                                                                                       
real    1m31.874s                                                                                      
user	0m0.884s
sys	0m0.679s

Una vez completada la compilación, el tiempo de ejecución llega a ser de 91,7 segundos.

Comprobemos su tamaño usando

docker image ls

Esto es lo que obtenemos.

devopscube/optimize  4.0   37d746b976e3   42 seconds ago      216MB

Entonces, el tamaño es de 216 MB.

Con esta técnica de optimización, el tiempo de ejecución se redujo de 117,1 s a 91,7 s y el tamaño de almacenamiento se redujo de 227 MB a 216 MB.

Método 4:comprender el almacenamiento en caché

A menudo, la misma imagen debe reconstruirse una y otra vez con ligeras modificaciones en el código.

Docker ayuda en estos casos al almacenar el caché de cada capa de una compilación, con la esperanza de que pueda ser útil en el futuro.

Debido a este concepto, se recomienda agregar las líneas que se usan para instalar dependencias y paquetes antes dentro del Dockerfile, antes de los comandos COPY.

La razón detrás de esto es que la ventana acoplable podría almacenar en caché la imagen con las dependencias requeridas, y este caché se puede usar en las siguientes compilaciones cuando se modifica el código.

Por ejemplo, echemos un vistazo a los siguientes dos Dockerfiles.

Dockerfile5

FROM ubuntu:latest
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update -y
RUN apt-get upgrade -y
RUN apt-get install vim -y
RUN apt-get install net-tools -y
RUN apt-get install dnsutils -y
COPY . .

Dockerfile6

FROM ubuntu:latest
COPY . .
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update -y
RUN apt-get upgrade -y
RUN apt-get install vim -y
RUN apt-get install net-tools -y
RUN apt-get install dnsutils -y

Docker podría utilizar mejor la función de caché con Dockerfile6 que con Dockerfile5 debido a la mejor ubicación del comando COPY.

Método 5:Usar Dockerignore

Como regla general, solo los archivos necesarios deben copiarse sobre la imagen de la ventana acoplable.

Docker puede ignorar los archivos presentes en el directorio de trabajo si está configurado en .dockerignore archivo.

Esta función debe tenerse en cuenta al optimizar la imagen de la ventana acoplable.

Método 6:Mantener los datos de la aplicación en otro lugar

El almacenamiento de datos de la aplicación en la imagen aumentará innecesariamente el tamaño de las imágenes.

Se recomienda encarecidamente utilizar la función de volumen de los tiempos de ejecución del contenedor para mantener la imagen separada de los datos.

Herramientas de optimización de imágenes de Docker

Las siguientes son algunas de las herramientas de código abierto que lo ayudarán a optimizar

  1. Bucear :es una herramienta de exploración de imágenes que lo ayuda a descubrir capas en las imágenes de contenedores Docker y OCI. Con Dive, puede encontrar formas de optimizar sus imágenes de Docker. Consulte el repositorio de Dive Github para obtener más detalles.
  2. Docker Delgado: Le ayuda a optimizar la seguridad y el tamaño de sus imágenes de Docker. Consulte el repositorio de Docker Slim Github para obtener más detalles.

Seguiré agregando herramientas a esta lista.


Docker
  1. Cómo usar un Dockerfile para crear una imagen de Docker

  2. Cómo compartir imágenes de Docker con otros

  3. Cómo modificar imágenes de Docker

  4. Cómo confirmar cambios en una imagen de Docker

  5. Cómo crear una imagen de Docker a partir de un contenedor y un archivo Docker

Cómo crear una imagen de Docker desde un contenedor en ejecución

Cómo reducir el tamaño de la imagen de Docker en los contenedores de Docker

Cómo actualizar las imágenes de Docker a la última versión

Cómo usar Docker Commit para cambiar imágenes de contenedores

Cómo crear una imagen Docker personalizada con Dockerfile

Cómo listar/buscar/extraer imágenes de la ventana acoplable en Linux