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 virtuelle | Conteneur | |
|---|---|---|
| Virtualise | Du matériel (hyperviseur) | Rien : un processus isolé |
| Noyau | Le sien, complet | Celui de l’hôte, partagé |
| Démarrage | Minutes | Millisecondes |
| Empreinte | Go de RAM | Quelques Mo |
| Isolation | Forte (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:80publie 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 -det 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.jsonavant le reste ? », la réponse tient en un mot : le cache. Tant que les dépendances ne changent pas,npm cin’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:5432expose votre base à Internet même si UFW la bloque (les règles iptables de Docker passent avant). Binder sur127.0.0.1:5432:5432quand c’est local. Vérifier avecss -tlnp, jamais avecufw statusseul.
- « Docker isole autant qu’une VM » — non : noyau partagé, donc une faille noyau peut toucher l’hôte. Ne jamais lancer un conteneur en
--privilegedsans raison, éviter root dans le conteneur (USER node). latestn’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 embarquenode_moduleset.git, builds lents et images obèses.
Pour aller plus loin
- Docker — Get started et le Dockerfile reference
- OCI — Open Container Initiative : les specs qui standardisent images et runtimes
- Jouer avec
docker run -it --rm alpine sh, puis regarderls /proc/1/ns/pour toucher les namespaces du doigt - Étape suivante naturelle : l’orchestration (Kubernetes, sujet à venir)
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 machine | Container | |
|---|---|---|
| Virtualizes | Hardware (hypervisor) | Nothing: an isolated process |
| Kernel | Its own, complete | The host’s, shared |
| Startup | Minutes | Milliseconds |
| Footprint | GBs of RAM | A few MB |
| Isolation | Strong (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=512mpossible. - 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
bridgenetwork by default; containers on the same network reach each other by name (internal DNS).-p 8080:80publishes a container port on the host. - Docker Compose: describes a multi-container stack (app + DB + cache) in declarative YAML.
docker compose up -dand 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.jsonbefore the rest?”, the answer is one word: cache. As long as dependencies don’t change,npm cinever 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:5432exposes your database to the Internet even if UFW blocks it (Docker’s iptables rules come first). Bind to127.0.0.1:5432:5432for local-only services. Check withss -tlnp, never withufw statusalone.
- “Docker isolates as much as a VM” — no: shared kernel, so a kernel vulnerability can reach the host. Never run
--privilegedwithout a reason, avoid root inside the container (USER node). latestis 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 shipsnode_modulesand.git: slow builds and bloated images.
Going further
- Docker — Get started and the Dockerfile reference
- OCI — Open Container Initiative: the specs standardizing images and runtimes
- Play with
docker run -it --rm alpine sh, then look atls /proc/1/ns/to see namespaces first-hand - Natural next step: orchestration (Kubernetes, upcoming topic)