Pour la version en français, c'est ici: [Français](#français) # English ## How to reduce the size of your docker 🐳 images 💿? We're starting from a basic React application générated with a simple Dockerfile with disastrous conséquences: ```Dockerfile FROM node:18 COPY . . RUN npm install EXPOSE 3000 CMD ["npm", "start"] ``` For your information, the image weight a whopping 2.03GB 🤯 !!!!! A mountain 🏔️ of data for a simple web application that is empty at the moment!!! You can test it yourself by first running the following command: ```shell npm install ``` Then run the following command to build the docker image: ```shell docker build -t 'react-simple' . ``` You can inspect your images and verify the size yourself with the following command: ```shell docker image ls ``` I use Nushell and rigrep to filter only on the image in question using the following command: ```shell docker image ls | from ssv | where repository starts-with 'react' ``` Here's the result of running this command that shows how shamefully big the image is: ![Size of the image](./assets/react-simple-size.png) ### First optimisation: using a .dockerignore file and multi-stage build The first optimization that we can do is using a `.dockerignore` file. Just like a `.gitignore` file, a `.dockerignore` file allows us to exclude file and folders that we don't want to include in the docker image that we are building. Here's the `.dockerignore` file that I added to this simple project to mainly exclude the dark hole that node_modules are `node_modules`: ``` node_modules npm-debug.log Dockerfile .dockerignore .git .gitignore build assets ``` In our context, this command only optimizes the build time and not the build size, as the `309 MB` that the node_modules folder occupies are not needed to build the docker imagen but as we run the `npm install` command in any case inside the Dockerfile, this folder is generated and included from inside the Dockerfile. A better optimisation of the image is to use multi-stage build. Multi-stage builds allows us to include only what is necessary to run the image, and to not include the build files or node_modules for example. Here's the new version of the `Dockerfile` using multi-stage build: ```Dockerfile # Build stage of the application FROM node:18 AS build WORKDIR /app COPY package*.json ./ RUN npm install COPY . . RUN npm run build # Production stage to run the application FROM node:18 AS production COPY --from=build /app/build /prod RUN npm install --global serve EXPOSE 3000 CMD ["serve", "prod/"] ``` You can run the following command to build the image: ```shell docker build -t 'react-multistage' . ``` You can run the following command to verify that the new image size is smaller than the previous one: ```shell docker image ls ``` I am still usig Nushell on my end to have a pretty print and filter on the images that I want which gives me the following output: ![Multistage image size](./assets/react-multistage-size.png) The image now weighs `44 MB` less, which is 1.27 times smaller, and a whopping 21.67% reduction in size! # Français ## Comment réduire la taille de vos images 💿 docker 🐳 ? On part d'une application React tout juste généré et avec un Dockerfile simple mais avec de grosses conséquences : ```Dockerfile FROM node:18 COPY . . RUN npm install EXPOSE 3000 CMD ["npm", "start"] ``` Pour indication, l'image qui en résulte pèse environ 2.03GB 🤯 !!!!! Une montagne 🏔️ de données pour une simple application web vide pour l'instant !!! Vous pouvez le testez vous même en lançant d'abord la commande suivante: ```shell npm install ``` Puis en lançant la commande suivante pour builder l'image: ```shell docker build -t 'react-simple' . ``` Vous pouvez inspecter la taille de votre image avec la commande suivante: ```shell docker image ls ``` J'utilise personnellement Nushell et rigrep pour ne filtrer que sur l'image en question: ```shell docker image ls | from ssv | where repository starts-with 'react' ``` Voici un aperçu de ce que cette commande m'affiche avec le poids de l'image assez désastreux: ![Taille de cette image](./assets/react-simple-size.png) ### Première optimisation: utilisation de .dockerignore et multi-stage Une des première optimisation que l'on peut faire est d'utiliser un fichier `.dockerignore`. Tout comme un fichier `.gitignore`, le fichier `.dockerignore` permet de ne pas include certains dossiers ou fichier qui ne sont pas nécessaire pour l'image que l'on construit. Voici le `.dockerignore` que j'ai rajouté à ce simple projet pour notamment éviter d'inclure le trou noir qu'est le dossier `node_modules`: ``` node_modules npm-debug.log Dockerfile .dockerignore .git .gitignore build assets ``` Cependant, ce que cette commande optimise est le temps du build de l'image, car les `309 MB` qu'occupe actuellement ce dossier ne sont pas nécessaire à inclure dans l'image lors du build de celle-ci, mais comme nous lançons la commande `npm install` dans tous les cas dans le Dockerfile, ils sont réinstallé et ré-inclut dans l'image. Une meilleure optimisation de l'image est d'utiliser le multi-stage build. Le multi-stage build permet de n'inclure que ce qui est nécessaire pour exécuter votre programme et de ne pas inclure les fichier de build ou les node_modules par exemple. Voici la nouvelle version du `Dockerfile` découpé en multi-stage: ```Dockerfile # Build stage of the application FROM node:18 AS build WORKDIR /app COPY package*.json ./ RUN npm install COPY . . RUN npm run build # Production stage to run the application FROM node:18 AS production COPY --from=build /app/build /prod RUN npm install --global serve EXPOSE 3000 CMD ["serve", "prod/"] ``` Vous pouvez lancer la commande suivante pour construire l'image: ```shell docker build -t 'react-multistage' . ``` Vous pouvez relancer la comande suivante pour vérifier que la taille a bien diminué cette fois-ci: ```shell docker image ls ``` Pour ma part j'utilise toujours Nushell pour avoir cette belle présentation des résultats filtré: ![Taille de l'image multistage](./assets/react-multistage-size.png) L'image pèse maintenant `44 MB` de moins, on a une image 1.27 fois plus petite, soit une diminution de la taille de 21.67% !