Jour 1 Day 1 · lundi 27 juillet 2026 Monday 27 July 2026 DevOps Fondamental

Docker & la conteneurisation Docker & containerization

Images, conteneurs, volumes, réseaux : comprendre ce que Docker fait vraiment (et ce qu'il n'est pas) — le sujet DevOps le plus fréquent en entretien de stage. Images, containers, volumes, networks: understand what Docker actually does (and what it isn't) — the most common DevOps topic in internship interviews.

L’essentiel

Docker est un outil de conteneurisation : il permet d’empaqueter une application avec toutes ses dépendances (runtime, bibliothèques, config) dans une unité standardisée — le conteneur — qui s’exécute de façon identique sur n’importe quelle machine équipée d’un moteur de conteneurs. C’est la réponse au classique « ça marche sur ma machine ».

Contrairement à une machine virtuelle, un conteneur ne virtualise pas de matériel et n’embarque pas de noyau : tous les conteneurs partagent le noyau Linux de l’hôte. Un conteneur n’est qu’un processus ordinaire, isolé par des mécanismes du noyau.

Machine virtuelleConteneur
VirtualiseDu matériel (hyperviseur)Rien : un processus isolé
NoyauLe sien, completCelui de l’hôte, partagé
DémarrageMinutesMillisecondes
EmpreinteGo de RAMQuelques Mo
IsolationForte (frontière matérielle)Plus légère (namespaces)

Comment ça marche

Trois briques du noyau Linux font tout le travail :

  • Namespaces — isolent ce que le processus voit : PID (arbre de processus), NET (interfaces réseau), MNT (points de montage), UTS (hostname), IPC, USER. Le conteneur croit être seul sur la machine.
  • Cgroups — limitent ce que le processus consomme : CPU, RAM, I/O. C’est ce qui permet de dire --memory=512m.
  • Union filesystem (OverlayFS) — les images sont constituées de couches en lecture seule empilées ; le conteneur ajoute une fine couche accessible en écriture au sommet. Deux conteneurs issus de la même image partagent toutes les couches : c’est ce qui rend les images si économes.

Le cycle de vie complet tient dans un schéma :

Dockerfile ──build──▶ Image (couches RO) ──run──▶ Conteneur (+ couche RW)
                        │      ▲
                   push │      │ pull
                        ▼      │
                    Registry (Docker Hub, GHCR, privé…)

L’image est immuable (versionnée par tag et par digest), le conteneur est éphémère. Architecture : le CLI docker parle à un démon (dockerd) via une API REST ; le démon délègue l’exécution à containerd puis runc (le standard OCI). C’est pour ça que Kubernetes peut utiliser containerd sans Docker.

🎤 En entretien — le combo gagnant : expliquer namespaces + cgroups en trente secondes, puis enchaîner naturellement sur « et c’est pour ça qu’un conteneur démarre en millisecondes là où une VM met des minutes ». Vous venez de montrer le pourquoi derrière le quoi.

Concepts clés à maîtriser

  • Image vs conteneur : l’image est le modèle figé (une classe), le conteneur l’instance vivante (un objet). On ne modifie jamais une image en marche, on la reconstruit.
  • Volumes & bind mounts : la couche d’écriture disparaît avec le conteneur. Toute donnée persistante (base de données, uploads) vit dans un volume géré par Docker ou un bind mount vers l’hôte.
  • Réseaux : par défaut, un réseau bridge ; les conteneurs d’un même réseau se joignent par nom (DNS interne). -p 8080:80 publie un port du conteneur sur l’hôte.
  • Docker Compose : décrit une stack multi-conteneurs (app + DB + cache) en YAML déclaratif. docker compose up -d et tout démarre dans le bon ordre, sur un réseau commun.
  • Multi-stage builds : on compile dans une image lourde (SDK), on copie l’artefact dans une image minimale (runtime). Image finale plus petite et sans outils de build = surface d’attaque réduite.
  • Cache de build : chaque instruction du Dockerfile crée une couche mise en cache, invalidée dès qu’une instruction change. D’où la règle : les instructions qui changent le moins souvent en premier (dépendances avant le code source).

Les deux réunis dans un Dockerfile Node typique :

# --- Étape 1 : build, avec tout le SDK ---
FROM node:20 AS build
WORKDIR /app
COPY package*.json ./     # les dépendances d'abord → couche mise en cache
RUN npm ci
COPY . .                  # le code ensuite : lui seul invalide les couches suivantes
RUN npm run build

# --- Étape 2 : runtime minimal, sans outils de build ---
FROM node:20-alpine
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY --from=build /app/node_modules ./node_modules
USER node                 # jamais root en production
CMD ["node", "dist/server.js"]

💡 Réflexe à montrer — si on vous demande « pourquoi copier package.json avant le reste ? », la réponse tient en un mot : le cache. Tant que les dépendances ne changent pas, npm ci n’est jamais réexécuté.

En entretien

« Quelle différence entre un conteneur et une VM ? » — La VM virtualise du matériel et embarque un OS complet avec son noyau (hyperviseur) ; le conteneur est un processus isolé qui partage le noyau de l’hôte (namespaces + cgroups). VM = isolation forte, démarrage en minutes, Go de RAM. Conteneur = isolation plus légère, démarrage en ms, Mo. Bonus : mentionner que les deux se combinent (les nœuds Kubernetes cloud sont des VM).

« Que se passe-t-il quand tu fais docker run nginx ? » — Le démon cherche l’image localement, sinon la télécharge (pull) depuis le registry couche par couche ; il crée la couche d’écriture, les namespaces et cgroups du conteneur, le branche au réseau bridge, puis lance le processus défini par ENTRYPOINT/CMD. PID 1 du conteneur = ce processus ; s’il meurt, le conteneur s’arrête.

« Comment persister les données d’une base dans un conteneur ? » — Volume nommé monté sur le répertoire de données (-v pgdata:/var/lib/postgresql/data). Jamais dans la couche d’écriture. Citer la sauvegarde : le volume survit au conteneur, se sauvegarde indépendamment.

« Comment réduire la taille d’une image ? » — Image de base minimale (alpine, distroless), multi-stage build, .dockerignore, fusionner les RUN qui installent puis nettoient, ne pas embarquer les dépendances de dev.

« CMD vs ENTRYPOINT ? »ENTRYPOINT = l’exécutable fixe ; CMD = ses arguments par défaut, écrasables à la ligne de commande. Les deux se combinent : ENTRYPOINT ["node"] + CMD ["server.js"].

Pièges & idées reçues

⚠️ Piège vécu — Docker publie ses ports en contournant le pare-feu UFW : -p 0.0.0.0:5432:5432 expose votre base à Internet même si UFW la bloque (les règles iptables de Docker passent avant). Binder sur 127.0.0.1:5432:5432 quand c’est local. Vérifier avec ss -tlnp, jamais avec ufw status seul.

  • « Docker isole autant qu’une VM » — non : noyau partagé, donc une faille noyau peut toucher l’hôte. Ne jamais lancer un conteneur en --privileged sans raison, éviter root dans le conteneur (USER node).
  • latest n’est pas « la dernière version » — c’est juste un tag par défaut, mutable. En prod : tag de version explicite, idéalement un digest.
  • Un conteneur = un processus : pas de SSH ni de superviseur dans le conteneur ; les logs vont sur stdout/stderr (récupérés par docker logs).
  • Oublier le .dockerignore → le contexte de build embarque node_modules et .git, builds lents et images obèses.

Pour aller plus loin

The essentials

Docker is a containerization tool: it packages an application together with all its dependencies (runtime, libraries, config) into a standardized unit — the container — that runs identically on any machine with a container engine. It’s the answer to the classic “works on my machine”.

Unlike a virtual machine, a container does not virtualize hardware and does not ship a kernel: all containers share the host’s Linux kernel. A container is just a regular process, isolated by kernel mechanisms.

Virtual machineContainer
VirtualizesHardware (hypervisor)Nothing: an isolated process
KernelIts own, completeThe host’s, shared
StartupMinutesMilliseconds
FootprintGBs of RAMA few MB
IsolationStrong (hardware boundary)Lighter (namespaces)

How it works

Three Linux kernel building blocks do all the work:

  • Namespaces — isolate what the process sees: PID (process tree), NET (network interfaces), MNT (mount points), UTS (hostname), IPC, USER. The container believes it’s alone on the machine.
  • Cgroups — limit what the process consumes: CPU, RAM, I/O. This is what makes --memory=512m possible.
  • Union filesystem (OverlayFS) — images are made of stacked read-only layers; the container adds a thin writable layer on top. Two containers from the same image share every layer: that’s why images are so cheap to run.

The whole lifecycle fits in one diagram:

Dockerfile ──build──▶ Image (RO layers) ──run──▶ Container (+ RW layer)
                        │      ▲
                   push │      │ pull
                        ▼      │
                    Registry (Docker Hub, GHCR, private…)

The image is immutable (versioned by tag and digest), the container is ephemeral. Architecture: the docker CLI talks to a daemon (dockerd) over a REST API; the daemon delegates execution to containerd then runc (the OCI standard). That’s why Kubernetes can use containerd without Docker.

🎤 In an interview — the winning combo: explain namespaces + cgroups in thirty seconds, then naturally follow with “and that’s why a container starts in milliseconds where a VM takes minutes”. You’ve just shown the why behind the what.

Key concepts to master

  • Image vs container: the image is the frozen template (a class), the container the live instance (an object). You never patch a running image — you rebuild it.
  • Volumes & bind mounts: the writable layer dies with the container. Any persistent data (database, uploads) lives in a Docker-managed volume or a bind mount to the host.
  • Networks: a bridge network by default; containers on the same network reach each other by name (internal DNS). -p 8080:80 publishes a container port on the host.
  • Docker Compose: describes a multi-container stack (app + DB + cache) in declarative YAML. docker compose up -d and everything starts in the right order, on a shared network.
  • Multi-stage builds: compile in a heavy image (SDK), copy the artifact into a minimal runtime image. Smaller final image with no build tools = reduced attack surface.
  • Build cache: each Dockerfile instruction creates a cached layer, invalidated as soon as an instruction changes. Hence the rule: least-changing instructions first (dependencies before source code).

Both combined in a typical Node Dockerfile:

# --- Stage 1: build, with the full SDK ---
FROM node:20 AS build
WORKDIR /app
COPY package*.json ./     # dependencies first → cached layer
RUN npm ci
COPY . .                  # code last: only it invalidates the following layers
RUN npm run build

# --- Stage 2: minimal runtime, no build tools ---
FROM node:20-alpine
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY --from=build /app/node_modules ./node_modules
USER node                 # never root in production
CMD ["node", "dist/server.js"]

💡 Reflex to show — if asked “why copy package.json before the rest?”, the answer is one word: cache. As long as dependencies don’t change, npm ci never runs again.

In an interview

“What’s the difference between a container and a VM?” — A VM virtualizes hardware and ships a full OS with its own kernel (hypervisor); a container is an isolated process sharing the host kernel (namespaces + cgroups). VM = strong isolation, minutes to boot, GBs of RAM. Container = lighter isolation, ms to start, MBs. Bonus: mention they combine (cloud Kubernetes nodes are VMs).

“What happens when you run docker run nginx?” — The daemon looks for the image locally, otherwise pulls it from the registry layer by layer; it creates the container’s writable layer, namespaces and cgroups, attaches it to the bridge network, then starts the process defined by ENTRYPOINT/CMD. The container’s PID 1 is that process; if it dies, the container stops.

“How do you persist a database’s data in a container?” — Named volume mounted on the data directory (-v pgdata:/var/lib/postgresql/data). Never in the writable layer. Mention backup: the volume outlives the container and is backed up independently.

“How do you shrink an image?” — Minimal base image (alpine, distroless), multi-stage build, .dockerignore, merge RUN steps that install then clean up, don’t ship dev dependencies.

“CMD vs ENTRYPOINT?”ENTRYPOINT = the fixed executable; CMD = its default arguments, overridable on the command line. They combine: ENTRYPOINT ["node"] + CMD ["server.js"].

Pitfalls & misconceptions

⚠️ Real-world trap — Docker publishes ports bypassing the UFW firewall: -p 0.0.0.0:5432:5432 exposes your database to the Internet even if UFW blocks it (Docker’s iptables rules come first). Bind to 127.0.0.1:5432:5432 for local-only services. Check with ss -tlnp, never with ufw status alone.

  • “Docker isolates as much as a VM” — no: shared kernel, so a kernel vulnerability can reach the host. Never run --privileged without a reason, avoid root inside the container (USER node).
  • latest is not “the latest version” — it’s just a mutable default tag. In production: explicit version tag, ideally a digest.
  • One container = one process: no SSH or supervisor inside the container; logs go to stdout/stderr (collected by docker logs).
  • Forgetting .dockerignore → the build context ships node_modules and .git: slow builds and bloated images.

Going further

S'entraîner sur ce sujet → Practice this topic →