← Retour aux tutoriels

Bien démarrer avec Docker : guide pour débutants

dockerconteneursdevopsdebutantstutorielinstallationimages-dockercommandes-docker

Bien démarrer avec Docker : guide pour débutants

Docker est un outil qui permet d’exécuter des applications dans des conteneurs : des environnements isolés, reproductibles et légers. L’objectif de ce guide est de vous donner une compréhension solide (pas seulement des recettes) et de vous faire pratiquer avec de vraies commandes.


1) Pourquoi Docker ? Le problème que Docker résout

Avant Docker, déployer une application signifiait souvent :

Docker apporte :


2) Concepts essentiels : image, conteneur, registre

Image Docker

Une image est un modèle immuable (un “snapshot” de système de fichiers + métadonnées) qui contient :

On peut voir une image comme une recette prête à l’emploi.

Commandes utiles :

docker images
docker image ls

Conteneur Docker

Un conteneur est une instance en cours d’exécution (ou arrêtée) d’une image. Il ajoute une couche en écriture au-dessus de l’image (vos fichiers temporaires, logs, etc.).

Lister les conteneurs :

docker ps
docker ps -a

Registre (Docker Hub, registre privé)

Un registre stocke des images. Le plus connu est Docker Hub. On peut :

Exemple :

docker pull nginx:latest
docker pull python:3.12-slim

3) Installation et vérification

Après installation de Docker Desktop (Windows/macOS) ou Docker Engine (Linux), vérifiez :

docker version
docker info

Test rapide avec l’image “hello-world” :

docker run hello-world

Cette commande télécharge l’image si nécessaire puis exécute un conteneur qui affiche un message.


4) Votre première exécution : comprendre docker run

La commande la plus importante au début est :

docker run [options] image[:tag] [commande]

Exemples :

Lancer un conteneur interactif

docker run -it ubuntu:24.04 bash

Dans le conteneur, essayez :

cat /etc/os-release
ls -la
exit

Lancer en arrière-plan (mode détaché)

docker run -d nginx:latest

Voir les conteneurs :

docker ps

Nommer un conteneur

docker run -d --name web nginx:latest
docker ps

Exposer des ports : -p

Un conteneur peut écouter sur un port interne (ex. 80). Pour y accéder depuis votre machine :

docker run -d --name web -p 8080:80 nginx:latest

Puis ouvrez http://localhost:8080.

Variables d’environnement : -e

Exemple avec PostgreSQL :

docker run -d --name db \
  -e POSTGRES_PASSWORD=secret \
  -p 5432:5432 \
  postgres:16

5) Cycle de vie des conteneurs : démarrer, arrêter, supprimer

Arrêter :

docker stop web

Démarrer un conteneur déjà créé :

docker start web

Redémarrer :

docker restart web

Supprimer un conteneur :

docker rm web

Supprimer un conteneur en cours d’exécution (forcé) :

docker rm -f web

6) Observer et diagnostiquer : logs, exec, inspect

Logs

Pour voir ce que l’application écrit :

docker logs web
docker logs -f web

-f suit les logs en temps réel.

Exécuter une commande dans un conteneur : docker exec

Très utile pour déboguer :

docker exec -it web bash

Sur Nginx, l’image peut ne pas contenir bash. Essayez alors :

docker exec -it web sh

Inspecter la configuration : docker inspect

docker inspect web

Pour extraire une info précise (ex. l’adresse IP interne) :

docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' web

7) Volumes : persister les données

Par défaut, les données écrites dans un conteneur disparaissent quand on supprime le conteneur. Pour une base de données, c’est inacceptable. Les volumes permettent de persister.

Volume nommé

Créer un volume :

docker volume create pgdata
docker volume ls

Lancer PostgreSQL avec volume :

docker run -d --name db \
  -e POSTGRES_PASSWORD=secret \
  -v pgdata:/var/lib/postgresql/data \
  -p 5432:5432 \
  postgres:16

Supprimez le conteneur puis recréez-le : les données restent dans pgdata.

Supprimer un volume (attention, destructif) :

docker volume rm pgdata

Montage de dossier (bind mount)

Utile en développement pour partager votre code avec le conteneur.

Exemple : servir un fichier HTML local avec Nginx.

Créez un dossier et un fichier :

mkdir -p site
echo "<h1>Bonjour Docker</h1>" > site/index.html

Lancez Nginx en montant ce dossier :

docker run -d --name web \
  -p 8080:80 \
  -v "$(pwd)/site":/usr/share/nginx/html:ro \
  nginx:latest

8) Réseaux Docker : communiquer entre conteneurs

Docker crée un réseau par défaut, mais en pratique on utilise souvent un réseau bridge dédié pour un groupe de services (application + base de données).

Créer un réseau :

docker network create appnet
docker network ls

Lancer une base de données sur ce réseau :

docker run -d --name db \
  --network appnet \
  -e POSTGRES_PASSWORD=secret \
  -e POSTGRES_DB=appdb \
  postgres:16

Lancer un conteneur client sur le même réseau pour tester la connexion :

docker run -it --rm \
  --network appnet \
  postgres:16 \
  psql -h db -U postgres -d appdb

Remarquez -h db : le nom du conteneur devient un nom DNS sur le réseau Docker. C’est un point clé : on évite de coder des IP.


9) Construire vos propres images : Dockerfile expliqué en profondeur

Une image se construit généralement avec un Dockerfile. C’est un fichier texte décrivant les étapes.

Exemple 1 : application Python simple (Flask)

Créez un dossier :

mkdir -p demo-flask
cd demo-flask

Créez app.py :

cat > app.py <<'EOF'
from flask import Flask
app = Flask(__name__)

@app.get("/")
def home():
    return "Bonjour depuis un conteneur Docker !\n"

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)
EOF

Créez requirements.txt :

cat > requirements.txt <<'EOF'
flask==3.0.3
EOF

Créez Dockerfile :

cat > Dockerfile <<'EOF'
FROM python:3.12-slim

# Bonnes pratiques : éviter d’écrire des .pyc et activer des logs immédiats
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1

WORKDIR /app

# Installer les dépendances d’abord (meilleur cache)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copier le code ensuite
COPY app.py .

EXPOSE 5000

CMD ["python", "app.py"]
EOF

Comprendre chaque instruction

Construire l’image :

docker build -t demo-flask:1.0 .

Lancer :

docker run -d --name flask -p 5000:5000 demo-flask:1.0

Tester :

curl http://localhost:5000/

Voir les logs :

docker logs -f flask

Arrêter et supprimer :

docker rm -f flask

10) Caching et couches : pourquoi l’ordre du Dockerfile compte

Docker construit une image en couches. Chaque instruction (FROM, RUN, COPY, etc.) crée une couche. Si une couche ne change pas, Docker peut la réutiliser (cache), accélérant les builds.

Dans l’exemple Flask, on copie requirements.txt et on installe les dépendances avant de copier le code. Ainsi :

Si vous faisiez COPY . . avant pip install, la moindre modification de code invaliderait le cache et forcerait une réinstallation complète.


11) .dockerignore : éviter d’embarquer l’inutile

Quand vous faites docker build, Docker envoie un “contexte” (les fichiers du dossier) au moteur. Il faut exclure ce qui n’est pas nécessaire : .git, caches, environnements virtuels, etc.

Créez un .dockerignore :

cat > .dockerignore <<'EOF'
.git
__pycache__
*.pyc
.venv
venv
EOF

12) Tags, versions et bonnes pratiques d’images

Tags

Une image est identifiée par nom:tag. Exemples :

Bonnes pratiques :

Tagger une image :

docker tag demo-flask:1.0 demo-flask:stable
docker images | grep demo-flask

13) Docker Compose : orchestrer plusieurs conteneurs

Quand une application a plusieurs services (API + base de données + cache), lancer tout à la main devient pénible. Docker Compose permet de décrire et lancer un ensemble.

Créez un dossier :

mkdir -p demo-compose
cd demo-compose

Créez app.py :

cat > app.py <<'EOF'
import os
import psycopg2
from flask import Flask

app = Flask(__name__)

def get_conn():
    return psycopg2.connect(
        host=os.environ["DB_HOST"],
        dbname=os.environ["DB_NAME"],
        user=os.environ["DB_USER"],
        password=os.environ["DB_PASSWORD"],
    )

@app.get("/")
def home():
    conn = get_conn()
    cur = conn.cursor()
    cur.execute("SELECT NOW();")
    now = cur.fetchone()[0]
    cur.close()
    conn.close()
    return f"Connexion OK, heure DB: {now}\n"

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)
EOF

Créez requirements.txt :

cat > requirements.txt <<'EOF'
flask==3.0.3
psycopg2-binary==2.9.9
EOF

Créez Dockerfile :

cat > Dockerfile <<'EOF'
FROM python:3.12-slim

ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY app.py .

EXPOSE 5000
CMD ["python", "app.py"]
EOF

Créez compose.yaml :

cat > compose.yaml <<'EOF'
services:
  api:
    build: .
    ports:
      - "5000:5000"
    environment:
      DB_HOST: db
      DB_NAME: appdb
      DB_USER: postgres
      DB_PASSWORD: secret
    depends_on:
      - db

  db:
    image: postgres:16
    environment:
      POSTGRES_DB: appdb
      POSTGRES_PASSWORD: secret
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:
EOF

Lancer :

docker compose up -d --build
docker compose ps

Tester :

curl http://localhost:5000/

Voir les logs :

docker compose logs -f

Arrêter et supprimer (en gardant le volume) :

docker compose down

Supprimer aussi les volumes (destructif) :

docker compose down -v

Comprendre depends_on

depends_on gère l’ordre de démarrage, mais ne garantit pas que la base est prête à accepter des connexions. En pratique, on ajoute souvent :


14) Nettoyage : éviter d’encombrer votre machine

Docker peut accumuler images, conteneurs arrêtés, volumes orphelins.

Voir l’espace :

docker system df

Supprimer conteneurs arrêtés, réseaux inutilisés, images non référencées :

docker system prune

Inclure aussi les images non utilisées :

docker system prune -a

Supprimer aussi les volumes non utilisés (attention) :

docker system prune -a --volumes

15) Sécurité et bonnes pratiques de base

Exécuter en non-root (quand c’est pertinent)

Beaucoup d’images tournent en root par défaut. Pour réduire les risques, on peut créer un utilisateur.

Exemple (principe général) :

RUN useradd -m appuser
USER appuser

Cela dépend des besoins (accès aux ports, écriture dans certains dossiers, etc.).

Minimiser l’image

Épingler les versions

Ne pas mettre de secrets dans l’image

Évitez de mettre des mots de passe dans le Dockerfile. Préférez :


16) Dépannage : erreurs fréquentes et méthodes

“Port is already allocated”

Vous mappez un port déjà utilisé.

Diagnostic :

docker ps

Solution : changer le port hôte, par exemple :

docker run -p 8081:80 nginx:latest

“Cannot connect to the Docker daemon”

Le service Docker n’est pas démarré ou droits insuffisants. Vérifiez Docker Desktop ou le service. Sur Linux, selon configuration, il peut falloir appartenir au groupe docker.

Un conteneur s’arrête immédiatement

Souvent, la commande principale se termine. Regardez :

docker logs <nom>
docker ps -a

Comprendre ce qui tourne réellement

Inspectez :

docker inspect <nom>

Vérifiez :


17) Résumé des commandes indispensables

Images

docker pull nginx:latest
docker images
docker rmi nginx:latest
docker build -t monimage:1.0 .

Conteneurs

docker run -d --name monconteneur -p 8080:80 nginx:latest
docker ps
docker ps -a
docker stop monconteneur
docker start monconteneur
docker rm -f monconteneur
docker logs -f monconteneur
docker exec -it monconteneur sh

Volumes et réseaux

docker volume create data
docker volume ls
docker network create monreseau
docker network ls

Compose

docker compose up -d --build
docker compose logs -f
docker compose down
docker compose down -v

18) Prochaines étapes recommandées

Pour progresser après ce guide :

  1. Construire une image multi-étapes (multi-stage build) pour réduire la taille finale, surtout pour les projets compilés.
  2. Ajouter des healthchecks et une stratégie de redémarrage.
  3. Étudier les différences entre bind mounts et volumes selon les environnements.
  4. Apprendre à publier une image sur un registre (public ou privé).
  5. Explorer les limites de ressources (CPU/RAM) et l’observabilité (métriques, logs structurés).

Conclusion

Docker simplifie la vie en rendant les environnements prévisibles et reproductibles. En comprenant bien la différence entre images et conteneurs, la gestion des volumes, des réseaux, et la construction via Dockerfile, vous avez les bases nécessaires pour conteneuriser une application réelle et la faire tourner localement ou sur un serveur.

Si vous me dites votre système (Linux, macOS, Windows) et le type d’application (Python, Node, PHP, Java…), je peux proposer un exemple adapté avec une structure de projet réaliste et des commandes prêtes à exécuter.