GNU/Linux >> Tutoriels Linux >  >> Panels >> Docker

Comment réduire la taille de l'image Docker :6 méthodes d'optimisation

Si vous souhaitez réduire la taille de l'image docker , vous devez utiliser les meilleures pratiques standard pour créer une image Docker.

Ce blog parle de différentes techniques d'optimisation que vous pouvez rapidement mettre en œuvre pour créer la plus petite et image docker minimale . Nous examinerons également certains des meilleurs outils pour Docker Image Optimization .

Docker en tant que moteur de conteneur facilite la prise d'un morceau de code et son exécution dans un conteneur. Il permet aux ingénieurs de rassembler toutes les dépendances de code et tous les fichiers dans un emplacement unique qui peut être exécuté n'importe où, assez rapidement et facilement.

Tout le concept d'images "exécutables n'importe où" commence à partir d'un simple fichier de configuration appelé Dockerfile. Tout d'abord, nous ajoutons toutes les instructions de construction, telles que les dépendances de code, les commandes et les détails de l'image de base, dans Dockerfile.

Besoin d'optimisation d'image Docker

Même si le processus de création de Docker est simple, de nombreuses organisations commettent l'erreur de créer des images Docker gonflées. sans optimiser les images du conteneur.

Dans le développement logiciel typique, chaque service aura plusieurs versions/versions, et chaque version nécessite plus de dépendances, de commandes et de configurations. Cela introduit un défi dans la construction d'image Docker, comme maintenant - le même code nécessite plus de temps et de ressources pour être construit avant de pouvoir être expédié en tant que conteneur.

J'ai vu des cas où l'image d'application initiale commençait avec 350 Mo et, au fil du temps, elle est passée à plus de 1,5 Go.

De plus, en installant des bibliothèques indésirables, nous augmentons le risque d'un risque de sécurité potentiel en augmentant la surface d'attaque.

Par conséquent, les ingénieurs DevOps doivent optimiser les images Docker pour s'assurer que l'image docker ne soit pas gonflée après les versions d'application ou les versions futures. Pas seulement pour les environnements de production, à chaque étape du processus CI/CD, vous devez optimiser vos images Docker.

De plus, avec des outils d'orchestration de conteneurs comme Kubernetes, il est préférable d'avoir des images de petite taille pour réduire le temps de transfert et de déploiement des images .

Comment réduire la taille de l'image Docker ?

Si nous prenons une image de conteneur d'une application typique, elle contient une image de base, Dependencies/Files/Configs , et cru (logiciel indésirable).

Tout se résume donc à l'efficacité avec laquelle nous pouvons gérer ces ressources à l'intérieur de l'image du conteneur.

Examinons différentes méthodes établies d'optimisation des images Docker. De plus, nous avons donné des exemples pratiques pour comprendre l'optimisation des images Docker en temps réel.

Soit vous utilisez les exemples donnés dans l'article, soit vous essayez les techniques d'optimisation sur des Dockerfiles existants.

Voici les méthodes par lesquelles nous pouvons optimiser l'image Docker.

  1. Utilisation d'images de base sans distribution/minimales
  2. Constructions en plusieurs étapes
  3. Minimiser le nombre de calques
  4. Comprendre la mise en cache
  5. Utiliser Dockerignore
  6. Conserver les données d'application ailleurs

Fichiers d'exercice Docker : Tout le code d'application, les Dockerfiles et les configurations utilisés dans cet article sont hébergés dans ce référentiel Github. Vous pouvez le cloner et suivre le didacticiel.

Méthode 1 :Utiliser un minimum d'images de base

Votre premier objectif doit être de choisir la bonne image de base avec une empreinte minimale du système d'exploitation.

Un tel exemple est les images de base alpines. Les images alpines peuvent être aussi petites que 5,59 Mo . Ce n'est pas seulement petit; c'est aussi très sécurisé.

alpine       latest    c059bfaa849c     5.59MB

L'image de base Nginx alpine ne fait que 22 Mo.

Par défaut, il est livré avec le shell sh qui aide à déboguer le conteneur en l'attachant.

Vous pouvez réduire davantage la taille de l'image de base à l'aide d'images distroless. Il s'agit d'une version simplifiée du système d'exploitation. Les images de base Distroless sont disponibles pour java, nodejs, python, Rust, etc.

Les images Distroless sont si minimes qu'elles n'ont même pas de shell . Alors, vous vous demandez peut-être comment déboguer les applications ? Ils ont la version de débogage de la même image qui est fournie avec la busybox pour le débogage.

De plus, la plupart des distributions ont maintenant leurs images de base minimales.

Remarque : Vous ne pouvez pas utiliser directement les images de base accessibles au public dans les environnements de projet. Vous devez obtenir l'approbation de l'équipe de sécurité de l'entreprise pour utiliser l'image de base. Dans certaines organisations, l'équipe de sécurité elle-même publie des images de base tous les mois après les tests et l'analyse de sécurité. Ces images seraient disponibles dans le référentiel privé docker de l'organisation commune.

Méthode 2 :Utiliser les builds Docker en plusieurs étapes

Le modèle de construction en plusieurs étapes a évolué à partir du concept de modèle de construction où nous utilisons différents Dockerfiles pour construire et empaqueter le code de l'application. Même si ce modèle aide à réduire la taille de l'image, il met peu de frais généraux lorsqu'il s'agit de créer des pipelines.

Dans la construction en plusieurs étapes, nous obtenons des avantages similaires à ceux du modèle de constructeur. Nous utilisons des images intermédiaires (étapes de construction) pour compiler le code, installer les dépendances et empaqueter les fichiers dans cette approche. L'idée derrière cela est d'éliminer les couches indésirables dans l'image.

Après cela, seuls les fichiers d'application nécessaires pour exécuter l'application sont copiés sur une autre image avec uniquement les bibliothèques requises, c'est-à-dire plus légères pour exécuter l'application.

Voyons cela en action, à l'aide d'un exemple pratique où nous créons une application Nodejs simple et optimisons son Dockerfile.

Commençons par créer le code. Nous aurons la structure de dossiers suivante.

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

Enregistrez ce qui suit sous 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}`);
    }
  }
)

Enregistrez ce qui suit sous 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"
  }
}

Enregistrez la variable de port suivante dans un fichier nommé env .

PORT=8080

Un simple Dockerfile pour cette application aimerait ceci - Enregistrez-le sous Dockerfile1 .

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

Voyons l'espace de stockage dont il a besoin en le construisant.

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

Une fois la construction terminée. Vérifions sa taille en utilisant –

docker image ls

C'est ce que nous obtenons.

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

La taille est donc de 910MBs .

Utilisons maintenant cette méthode pour créer une version en plusieurs étapes.

Nous utiliserons node:16 comme image de base, c'est-à-dire l'image de toutes les installations de dépendances et de modules, après cela, nous déplacerons le contenu dans un ‘alpine minimal et plus léger ' image basée. L'alpine ' l'image a le strict minimum d'utilitaires et est donc très légère.

Voici une représentation picturale d'une construction Docker en plusieurs étapes.

Aussi, dans un seul Dockerfile , vous pouvez avoir plusieurs étapes avec différentes images de base. Par exemple, vous pouvez avoir différentes étapes pour la construction, le test, l'analyse statique et le package avec différentes images de base.

Voyons à quoi pourrait ressembler le nouveau Dockerfile. Nous ne faisons que copier les fichiers nécessaires de l'image de base vers l'image principale.

Enregistrez ce qui suit sous 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"]

Voyons l'espace de stockage dont il a besoin en le construisant.

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

Une fois la construction terminée. Vérifions sa taille en utilisant

docker image ls

C'est ce que nous obtenons.

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

Ainsi, la nouvelle taille d'image réduite est de 171 Mo par rapport à l'image avec toutes les dépendances.

C'est une optimisation de plus de 80 % !

Cependant, si nous avions utilisé la même image de base que celle que nous avons utilisée lors de la phase de construction, nous n'aurions pas vu beaucoup de différence.

Vous pouvez réduire davantage la taille de l'image à l'aide d'images distroless . Voici le même Dockerfile avec une étape de construction en plusieurs étapes qui utilise l'image google nodeJS distroless au lieu d'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 vous créez le Dockerfile ci-dessus, votre image sera de 118 Mo ,

devopscube/distroless-node   1.0       302990bc5e76     118MB

Méthode 3 :Minimiser le nombre de calques

Les images Docker fonctionnent de la manière suivante - chaque RUN, COPY, FROM Les instructions Dockerfile ajoutent une nouvelle couche et chaque couche ajoute au temps d'exécution de la construction et augmente les besoins de stockage de l'image.

Voyons cela en action, à l'aide d'un exemple pratique :créons une image Ubuntu avec des bibliothèques mises à jour et mises à niveau, ainsi que certains packages nécessaires installés tels que vim, net-tools, dnsutils.

Un Dockerfile pour y parvenir serait comme suit - Enregistrez ceci sous 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

Nous souhaitons également examiner le temps de construction de cette image.

Le démon Docker a une capacité intégrée pour afficher le temps d'exécution total qu'un Dockerfile prend.

Pour activer cette fonctionnalité, procédez comme suit –

  1. Créer un daemon.json fichier avec le contenu suivant dans /etc/docker/
{
  "experimental": true
}

2. Exécutez la commande suivante pour activer la fonctionnalité.

export DOCKER_BUILDKIT=1

Construisons-le et voyons le temps de stockage et de construction.

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

Il afficherait les temps d'exécution dans le 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

Une fois la construction terminée, le temps d'exécution est de 117,1 secondes .

Vérifions sa taille en utilisant

docker image ls

C'est ce que nous obtenons.

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

La taille est donc de 227 Mo .

Combinons les commandes RUN en une seule couche et enregistrons-la sous 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

Dans la commande RUN ci-dessus, nous avons utilisé --no-install-recommends drapeau pour désactiver les packages recommandés. Il est recommandé chaque fois que vous utilisez install dans vos Dockerfiles

Voyons le stockage et le temps de construction requis pour le construire.

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

Il afficherait les temps d'exécution dans le 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

Une fois la construction terminée, le temps d'exécution est de 91,7 secondes.

Vérifions sa taille en utilisant

docker image ls

C'est ce que nous obtenons.

devopscube/optimize  4.0   37d746b976e3   42 seconds ago      216MB

La taille est donc de 216 Mo.

Grâce à cette technique d'optimisation, le temps d'exécution a été réduit de 117,1 s à 91,7 s et la taille de stockage a été réduite de 227 Mo à 216 Mo.

Méthode 4 :Comprendre la mise en cache

Souvent, la même image doit être reconstruite encore et encore avec de légères modifications dans le code.

Docker aide dans de tels cas en stockant le cache de chaque couche d'une construction, en espérant que cela pourrait être utile à l'avenir.

En raison de ce concept, il est recommandé d'ajouter les lignes utilisées pour installer les dépendances et les packages plus tôt dans le Dockerfile - avant les commandes COPY.

La raison derrière cela est que docker pourrait mettre en cache l'image avec les dépendances requises, et ce cache peut ensuite être utilisé dans les versions suivantes lorsque le code est modifié.

Par exemple, examinons les deux Dockerfiles suivants.

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 pourrait mieux utiliser la fonctionnalité de cache avec Dockerfile6 que Dockerfile5 en raison du meilleur placement de la commande COPY.

Méthode 5 :Utiliser Dockerignore

En règle générale, seuls les fichiers nécessaires doivent être copiés sur l'image Docker.

Docker peut ignorer les fichiers présents dans le répertoire de travail s'il est configuré dans le .dockerignore fichier.

Cette fonctionnalité doit être gardée à l'esprit lors de l'optimisation de l'image Docker.

Méthode 6 :conserver les données d'application ailleurs

Le stockage des données d'application dans l'image augmentera inutilement la taille des images.

Il est fortement recommandé d'utiliser la fonctionnalité de volume des runtimes de conteneur pour séparer l'image des données.

Outils d'optimisation d'image Docker

Voici quelques-uns des outils open source qui vous aideront à optimiser

  1. Plonger :Il s'agit d'un outil d'exploration d'images qui vous aide à découvrir les couches dans les images des conteneurs Docker et OCI. En utilisant la plongée, vous pouvez trouver des moyens d'optimiser vos images Docker. Consultez le référentiel Dive Github pour plus de détails.
  2. Docker Slim : Il vous aide à optimiser vos images Docker pour la sécurité et la taille. Consultez le dépôt Docker Slim Github pour plus de détails.

Je continuerai à ajouter des outils à cette liste.


Docker
  1. Comment déplacer des images Docker entre les hôtes

  2. Comment utiliser un Dockerfile pour créer une image Docker

  3. Comment partager des images Docker avec d'autres

  4. Comment modifier les images Docker

  5. Comment mettre à jour/ajouter un fichier dans l'image Docker

Comment créer une image Docker à partir d'un conteneur en cours d'exécution

Comment réduire la taille de l'image Docker dans les conteneurs Docker

Comment mettre à jour les images Docker vers la dernière version

Comment utiliser Docker Commit pour modifier les images de conteneur

Comment créer une image Docker personnalisée avec Dockerfile

Comment répertorier / rechercher / extraire des images docker sous Linux