# Dockerfile Un **Dockerfile** est un fichier texte contenant une suite d’instructions permettant d’automatiser la construction d’une **image Docker**. C’est la “recette” qui décrit pas à pas comment obtenir une image personnalisée, réutilisable et portable. Chaque ligne du Dockerfile est interprétée par le moteur Docker (`dockerd`) lors d’un `docker build`. Chaque instruction produit une **couche** (layer) qui s’empile sur l’image de base. --- ## Structure générale Un Dockerfile est lu de haut en bas. Les instructions principales sont : - `FROM` : définit l’image de base (ex. `FROM ubuntu:24.04`). - `RUN` : exécute une commande lors de la construction (ex. installation de paquets). - `COPY` ou `ADD` : copie des fichiers du **contexte de build** dans l’image. - `WORKDIR` : définit le répertoire de travail par défaut. - `CMD` : définit la commande par défaut exécutée au démarrage du conteneur. - `ENTRYPOINT` : définit le programme principal du conteneur (arguments ajoutés à l’exécution). --- ## Exemple minimal ```dockerfile FROM ubuntu:24.04 RUN apt-get update && apt-get install -y curl CMD ["bash"] ``` Cette image est basée sur Ubuntu 24.04, inclut `curl`, et lance `bash` par défaut. --- ## CMD vs ENTRYPOINT ### CMD - Définit une commande par défaut. - Peut être remplacée par des arguments donnés à `docker run`. Exemple : ```dockerfile CMD ["echo", "Hello World"] ``` ```bash docker run monimage echo "Autre commande" ``` Ici, `echo "Autre commande"` remplace le CMD. ### ENTRYPOINT - Définit un programme fixe. - Les arguments passés en ligne de commande sont ajoutés. Exemple : ```dockerfile ENTRYPOINT ["echo"] ``` ```bash docker run monimage "Hello" ``` Résultat : `echo Hello` ### Combinaison Il est courant de combiner les deux : ```dockerfile ENTRYPOINT ["ping"] CMD ["127.0.0.1"] ``` - `docker run monimage` → `ping 127.0.0.1` - `docker run monimage google.com` → `ping google.com` --- ## Couches et cache - Chaque instruction (`RUN`, `COPY`, etc.) crée une nouvelle couche immuable. - Docker conserve un **cache** pour accélérer les builds. - Si une étape n’a pas changé, Docker réutilise la couche précédente. - ⚠️ Conséquence : l’ordre des instructions est important. Exemple : ```dockerfile RUN apt-get update RUN apt-get install -y curl ``` crée deux couches. En regroupant : ```dockerfile RUN apt-get update && apt-get install -y curl ``` une seule couche, plus efficace. --- ## Contexte de build Lors d’un `docker build .`, Docker envoie le contenu du répertoire courant (`.`) au démon. Ce répertoire est appelé le **contexte de build**. - Le Dockerfile et les fichiers copiés avec `COPY` doivent être dans ce contexte. - Éviter d’envoyer des fichiers inutiles (logs, binaires, node_modules…). - On peut utiliser un fichier `.dockerignore` pour exclure certains fichiers. # Astuce : utiliser l'option -f avec docker build Par défaut, Docker cherche un fichier nommé **`Dockerfile`** à la racine du **contexte de build** (`.`). Si ton fichier s’appelle autrement ou se trouve ailleurs, il faut utiliser l’option `-f`. ```bash # Exemple : Dockerfile situé dans le répertoire build/ docker build -f build/Dockerfile -t monimage:1.0 . ``` `-f build/Dockerfile` : indique où se trouve le Dockerfile. - `.` : reste le **contexte** (fichiers copiés via `COPY`/`ADD`). --- ## ⚠️ Attention - Le chemin du Dockerfile (`-f`) et le contexte (`.`) sont deux notions **indépendantes**. - Si le Dockerfile fait un `COPY ./fichier.txt /app/`, ce fichier doit exister **dans le contexte**, pas forcément à côté du Dockerfile. --- ## Exemple pratique Avoir deux Dockerfiles (`Dockerfile.dev`, `Dockerfile.prod`) pour construire des images différentes : ```bash docker build -f Dockerfile.dev -t monapp:dev . docker build -f Dockerfile.prod -t monapp:prod . ``` --- ## Bonnes pratiques - Utiliser des images officielles fiables (Docker Hub). - Choisir des images minimales (`alpine`, `debian-slim`). - Grouper les commandes `RUN` pour réduire le nombre de couches. - Nettoyer les caches d’installation (ex. `apt-get clean`). - Éviter d’utiliser `latest` en production, préférer des tags de version stables. - Ajouter un `.dockerignore` pour réduire la taille du contexte. - Séparer construction et exécution avec les **multi-stage builds**. --- ## Multi-stage builds Les multi-stage builds permettent de produire des images finales beaucoup plus légères. On utilise plusieurs `FROM` dans un même Dockerfile. Exemple : ```dockerfile # Étape 1 : construction de l’app FROM node:22-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build # produit le dossier /app/dist # Étape 2 : image finale, minimaliste FROM alpine:3.19 RUN apk add --no-cache nodejs WORKDIR /app # Cette ligne est essentielle et copie l'applicatif depuis le conteneur de construction. COPY --from=builder /app/dist ./dist CMD ["node", "dist/index.js"] ``` L’image finale ne contient que l’exécutable `./dist`, pas le compilateur ni les sources --- ## Résumé - Le Dockerfile est la recette pour créer une image Docker. - Chaque instruction crée une couche, réutilisable grâce au cache. - `CMD` et `ENTRYPOINT` définissent le comportement par défaut d’un conteneur. - Le contexte de build doit être maîtrisé (`.dockerignore` recommandé). - Les bonnes pratiques et les multi-stage builds permettent des images plus légères, sécurisées et maintenables.