# Construction d'image avec Dockerfile

### Dockerfile et layers

Les images Docker fonctionnent par couches successives d'instructions, **les layers**.

Un **Dockerfile** est un fichier texte qui décrit les différentes couches d'une image Docker.

Chaque couche permet d'ajouter des actions à l'image, par exemple :

- Installation de dépendances
- Configuration d'un environnement
- Copie de fichiers depuis la machine hôte
- Définition de la commande lancée au démarrage du conteneur
- Etc.

<p class="callout info">Le dockerfile est comparable à une recette de cuisine : chaque instruction du Dockerfile représente une étape dans la préparation de l’environnement logiciel souhaité.</p>

Son utilisation offre certains avantages :

- **Automatisation** : chaque build est reproductible et scripté.
- **Traçabilité** : le fichier peut être versionné avec Git.
- **Portabilité** : un Dockerfile peut être utilisé sur n'importe quelle machine.
- **Modularité** : il permet de construire des images à partir d’autres images, facilitant la réutilisation.

Ci-dessous un exemple de Dockerfile :

```bash
FROM debian:trixie # point de départ : une image existante
RUN apt-get update -y # commande à éxécuter
RUN apt-get install fastfetch -y # une autre commande, pour une nouvelle couche
ENTRYPOINT ["fastfetch"] # point d'entrée au lancement du conteneur
```

### Les instructions d'un Dockerfile

<p class="callout info">N'hésitez pas à consulter la documentation de Docker à ce sujet : [🔗 Dockerfile reference](https://docs.docker.com/reference/dockerfile/).</p>

#### `FROM` - Définir l'image de base

```bash
FROM ubuntu:24.04
```

C'est le point de départ de l'image : une autre image de base officielle ou personnalisée.

#### `LABEL` - Ajouter des métadonnées

```bash
LABEL Maintainer="Thibaud FRICHET"
LABEL Description="Description de l'image"
```

Les métadonnées de l'image s'affichent avec `docker image inspect`.

#### `WORKDIR` - Définir le répertoire de travail

```bash
WORKDIR /var/www/html
```

Les instructions `RUN`, `CMD` et `ENTRYPOINT` s’exécutent dans le répertoire de travail.

#### `COPY` - Copier des fichiers locaux dans l'image

```bash
# copie fichier.txt à partir du répertoire courant de la machine hôte
# dans le répertoire courant de l'image (défini avec WORKDIR)
COPY fichier.txt ./ 

# copie le répertoire sources depuis la machine hôte vers le répertoire /var/www de l'image
COPY /home/thibaud/projet/sources/ /var/www/
```

#### `RUN` - Exécuter des commandes pendant la construction

```bash
# installation et lancement de Redis
RUN apt-get update -y
RUN apt-get install redis-server -y
RUN service redis-server start
```

Les commandes `RUN` s’exécutent pendant la construction de l'image, pas au démarrage d'un conteneur.

<p class="callout warning">Les commandes doivent se terminer sans saisie utilisateur.</p>

##### `EXPOSE` - Indiquer les ports utilisés par l'application

```bash
EXPOSE 80
EXPOSE 443
```

Ce sont les ports d'écoute du conteneur, ils peuvent être redirigés avec `docker run -p 90:80` à l'exécution.

##### `CMD` - Définir les commandes par défaut à exécuter

L'instruction CMD accepte un tableau en paramètre.

```bash
CMD ["date", "echo Bonjour"] # affiche la date courante, puis "Bonjour"
```

Contrairement à `RUN`, l'instruction `CMD` est exécutée au démarrage du conteneur, pas pendant la construction.

##### `ENTRYPOINT` - Définir le point d'entrée du conteneur

```bash
ENTRYPOINT ["service nginx start"] # lance le serveur web nginx
```

`CMD` et `ENTRYPOINT` définissent les commandes exécutées au démarrage d'un conteneur, mais avec un comportement différent :

- `CMD` indique la commande avec des arguments par défaut qui peuvent être remplacés lors de l’exécution avec docker run.
- `ENTRYPOINT` fixe le point d’entrée du conteneur, rendant son comportement plus rigide.

Lorsqu’ils sont utilisés ensemble, `ENTRYPOINT` définit le programme à exécuter et `CMD` les arguments par défaut.

<p class="callout info">Dans la majorité des cas, vous pouvez utiliser l'un ou l'autre uniquement.</p>

### Bonnes pratiques

- Utilisez des images légères. Exemples : `python:3.10-slim`, `nginx-stable-alpine`, etc.
- Utilisez des versions fixes dans vos dépendances. Évitez les tags `-latest` par exemple.
- Minimisez le nombre de couches, par exemple avec plusieurs commandes sur une ligne `RUN` et `&&`.  
    ```
    RUN apt-get update -y && apt-get install redis-server
    ```
- Utilisez `.dockerignore` pour exclure les fichiers inutiles, de la même manière qu'un `.gitignore`.  
    Le contenu du répertoire courant de la machine hôte est envoyé à Docker lors de la construction d'une image. Docker appelle cela le `context`.
- Respectez la convention de nommage d'un dockerfile : 
    - Par défaut : `Dockerfile`
    - Avec un préfixe : `dev.Dockerfile`, `prod.Dockerfile`, `app-v3.Dockerfile`

### La construction de l'image

La construction d'une image docker s'effectue avec `docker build`.

Le paramètre `-t`, `--tag` indique le nom de l'image et sa version. Le paramètre `-f`, `--file` le nom du fichier dockerfile, par défaut `Dockerfile`.

Un répertoire de contexte doit être indiqué, par exemple le répertoire courant avec `.`.

```bash
docker build -t mon_appli:1.0.0 -f monappli.Dockerfile .
```

[![image.png](https://formation.tfrichet.fr/uploads/images/gallery/2025-09/scaled-1680-/tZrv72npXnLC5DX7-image.png)](https://formation.tfrichet.fr/uploads/images/gallery/2025-09/tZrv72npXnLC5DX7-image.png)

[![image.png](https://formation.tfrichet.fr/uploads/images/gallery/2025-09/scaled-1680-/AJw5mu7IRM7XQskp-image.png)](https://formation.tfrichet.fr/uploads/images/gallery/2025-09/AJw5mu7IRM7XQskp-image.png)

<p class="callout info">Par la suite, une image Docker peut être exportée dans un fichier ou uploadée sur un registre privé par exemple.</p>