Ma Publicité

Soutenez la Création

Aidez-moi à partager du contenu exclusif.

Soutenir

Comments

Nouveau Drop

Boutique Officielle

Soutenez le blog monblog-sa-abasse et découvrez nos vêtements & accessoires exclusifs en édition limitée.

Découvrir la collection
Paiement Sécurisé
Livraison Monde

Docker en prod : 15 erreurs qui finissent en incident

Docker en prod : 15 erreurs qui finissent en incident

Docker, c’est pratique. C’est même un peu trop pratique. On passe de « ça marche sur mon laptop » à « on push en prod » en une après midi, et franchement, je comprends. Sur Le Blog Tech Pro de Samyn-Antoy ABASSE, je parle souvent de cette zone floue entre build vite et build propre. Docker est exactement là.

Mais en production, les petites négligences se transforment en incidents. Pas forcément un gros crash spectaculaire. Plutôt des trucs pénibles : un service qui redémarre en boucle, un disque saturé à 3 h du matin, un secret qui fuite, une image qui pèse 2 Go et qui met 10 minutes à pull… bref, la vraie vie.

Ici je te liste 15 erreurs que je vois encore et encore. Et surtout, quoi faire à la place.

Terminal Docker en production, logs et alertes

1. taguer « latest » et espérer que tout ira bien

Le tag latest est séduisant. C’est court. Ça fait moderne. Et c’est une source d’ennuis.

En prod, latest veut dire : « je ne sais pas exactement ce que je déploie ». Tu rebuild ton image, tu redéploies, et sans t’en rendre compte tu changes une version de dépendance, un binaire, une base d’OS. Ensuite, bon courage pour rollback.

À faire à la place :

  • Taguer avec une version immuable (ex. 1.3.7) ou un digest (@sha256:...).
  • Garder un historique de releases.
  • Savoir exactement quel artefact tourne.

Exemple :

bash docker pull registry.tld/app:1.3.7

docker pull registry.tld/app@sha256:xxxxxxxx

2. construire des images énormes « parce que ça marche »

Tu mets Node, Python, curl, vim, net-tools, des outils de build, et la moitié d’Ubuntu. Ça marche, oui. Jusqu’au jour où :

  • les pulls deviennent lents,
  • les surfaces d’attaque explosent,
  • tu te retrouves avec des CVE partout,
  • et le stockage de registry gonfle comme un ballon.

À faire à la place :

  • Multi stage builds.
  • Images distroless ou Alpine (avec prudence selon les libs).
  • Nettoyer cache et artefacts.

Exemple Dockerfile (simplifié) :

dockerfile FROM node:20 AS build WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build

FROM node:20-slim WORKDIR /app COPY --from=build /app/dist ./dist COPY --from=build /app/node_modules ./node_modules CMD ["node", "dist/index.js"]

Schéma mental : build stage vs runtime stage

3. exécuter en root dans le conteneur

Celle là, je la vois même dans des boîtes sérieuses. En dev, ok. En prod, tu t’offres un bonus gratuit à un attaquant.

À faire à la place :

  • Créer un user non privilégié.
  • Fixer les permissions de fichiers.
  • Ajouter readOnlyRootFilesystem côté orchestrateur si possible.

Exemple :

dockerfile RUN useradd -m appuser USER appuser

Et si tu es sur Kubernetes, tu peux aller plus loin avec runAsNonRoot.

4. mettre des secrets dans l’image ou dans le dépôt

Un .env copié dans l’image. Une clé API dans une variable d’environnement dans docker-compose.yml commité. Un token dans l’historique Git. Incident.

Même si tu supprimes après, c’est trop tard. Les layers Docker, les caches CI, les logs de build… tout garde des traces.

À faire à la place :

  • Docker secrets (Swarm) ou secrets Kubernetes.
  • Vault, SSM, secret manager cloud.
  • Injecter au runtime, pas au build.

Et au passage : évite de logguer les variables au démarrage.

5. oublier les limites CPU et mémoire

En prod, un conteneur sans limites, c’est un processus sans garde fou. Un pic de trafic, une fuite mémoire, et tu tues le node entier. Pas juste ton app. Les autres services tombent aussi.

À faire à la place :

  • Définir des limites et réservations.
  • Tester le comportement en OOM.
  • Observer avec des métriques.

Docker run :

bash docker run -m 512m --cpus="1.0" registry.tld/app:1.3.7

Compose :

yaml deploy: resources: limits: memory: 512M cpus: "1.0"

6. pas de healthcheck

Sans HEALTHCHECK, ton orchestrateur ne sait pas si le service est réellement fonctionnel. Il sait juste que le process tourne. Spoiler : un process peut tourner en étant mort de l’intérieur.

À faire à la place :

  • Ajouter un endpoint /healthz qui vérifie le strict minimum.
  • Définir un HEALTHCHECK Docker.
  • Coupler avec readiness et liveness sur Kubernetes.

Exemple :

dockerfile HEALTHCHECK --interval=30s --timeout=3s --retries=3
CMD curl -fsS http://localhost:8080/healthz || exit 1

Monitoring et checks de santé

7. logs en fichier dans le conteneur

Tu écris les logs dans /var/log/app.log. Ça grossit. Ça remplit le filesystem du conteneur. Puis le node. Puis incident disque, et tout part en vrille.

À faire à la place :

  • Loguer sur stdout et stderr.
  • Centraliser (ELK, Loki, CloudWatch, etc.).
  • Configurer la rotation côté daemon Docker.

Petit rappel : un conteneur est éphémère. Traite le comme tel.

8. pas de stratégie de rotation des logs Docker

Même si tu logues sur stdout, Docker peut stocker beaucoup. Beaucoup trop. Le driver json-file sans limites, c’est un classique.

À faire à la place : Configurer la rotation, par exemple dans /etc/docker/daemon.json :

json { "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "5" } }

Oui, ça évite des nuits pénibles.

9. exposer des ports inutilement

-p 0.0.0.0:5432:5432 parce que c’est plus simple. Ensuite tu te demandes pourquoi tu as des scans, des tentatives, ou pire.

À faire à la place :

  • N’exposer que ce qui doit l’être.
  • Laisser les services internes sur un réseau privé Docker.
  • Mettre un reverse proxy en frontal (Traefik, Nginx).

Et si tu dois publier, publie sur 127.0.0.1 quand c’est pour un accès local :

bash docker run -p 127.0.0.1:5432:5432 postgres:16

10. utiliser le réseau par défaut et tout laisser parler à tout

Le réseau bridge par défaut, c’est pratique. Mais en prod, segmenter est plus sain.

À faire à la place :

  • Créer des réseaux dédiés.
  • Séparer frontend, backend, data.
  • Limiter qui peut atteindre quoi.

Exemple compose :

yaml networks: front: back:

services: web: networks: [front, back] api: networks: [back] db: networks: [back]

11. monter le socket Docker dans un conteneur « juste pour dépanner »

/var/run/docker.sock dans un conteneur, c’est presque équivalent à donner root sur l’hôte. Beaucoup de gens ne réalisent pas.

À faire à la place :

  • Éviter.
  • Si besoin réel, isoler, restreindre, auditer.
  • Utiliser des alternatives : API proxy, permissions minimales, outils dédiés.

Si tu dois le faire, considère que tu ouvres une porte énorme.

12. mélanger état et conteneurs

La base de données dans un conteneur sans volume persistant, c’est l’accident annoncé. Tu redéploies, tu perds tout. Ou tu fais une migration et tu casses les données.

À faire à la place :

  • Volumes persistants.
  • Backups testés.
  • Si possible, DB managée (selon contexte).

Compose simple :

yaml services: db: image: postgres:16 volumes: - pgdata:/var/lib/postgresql/data

volumes: pgdata:

Stockage et volumes Docker

13. ignorer la supply chain : images non scannées, builds non reproductibles

Tu tires ubuntu:latest, tu installes des paquets au hasard, tu rebuild. Deux builds à une semaine d’intervalle peuvent produire des artefacts différents.

Et côté sécurité, tu peux intégrer une image compromise sans t’en rendre compte.

À faire à la place :

  • Pinner les versions.
  • Scanner les images (Trivy, Grype, scanner registry).
  • Signer les artefacts (cosign).
  • SBoM si tu peux (Syft).

Ce n’est pas « enterprise only ». Ça évite de mauvaises surprises.

14. pas de plan de rollback et des migrations destructrices

Tu déploies une nouvelle image et tu fais une migration DB irréversible. Puis tu découvres un bug. Le rollback devient impossible. Et là, ça devient une vraie crise.

À faire à la place :

  • Stratégie blue green ou canary si possible.
  • Migrations compatibles arrière (expand and contract).
  • Versionner le schéma.
  • Tester les rollbacks.

Même un plan simple vaut mieux que rien : « si ça casse, on revient à l’image N-1 et on désactive la feature ».

15. oublier l’observabilité : métriques, traces, alertes

Sans monitoring, tu ne gères pas. Tu subis. Et l’incident arrive toujours quand tu es occupé ailleurs.

À faire à la place :

  • Métriques techniques : CPU, RAM, disk, réseau, restarts.
  • Métriques applicatives : taux d’erreur, latence, saturation.
  • Alerting : simple, actionnable, pas du bruit.
  • Dashboards minimalistes.

Une règle perso : si ton service est critique, tu dois savoir en moins de 5 minutes qu’il a un problème. Pas quand un utilisateur t’écrit.


checklist rapide avant de mettre Docker en prod

Je te la mets ici, un peu brute, parce que c’est le genre de liste qu’on colle dans un README et qu’on relit avant une mise en prod :

  • Image taguée de façon immuable, pas de latest.
  • Image légère, multi stage, runtime minimal.
  • User non root.
  • Secrets gérés proprement, pas dans l’image.
  • Limites CPU et mémoire définies.
  • Healthcheck en place.
  • Logs sur stdout, rotation configurée.
  • Ports exposés au strict nécessaire.
  • Réseaux segmentés.
  • Pas de montage docker.sock sauf cas très cadré.
  • Données persistantes via volumes, backups testés.
  • Scan sécurité CI, dépendances pincées.
  • Rollback possible, migrations non destructrices.
  • Monitoring et alertes.
  • Documentation d’exploitation : quoi faire quand ça tombe.

Si tu veux, je peux transformer cette checklist en template prêt à copier pour tes projets, du genre « compose de prod de base », avec bonnes pratiques, annotations, et deux trois garde fous. Sur Le Blog Tech Pro de Samyn-Antoy ABASSE ( https://monblog-sa-abasse.blogspot.com ), j’ajoute souvent des ressources prêtes à l’emploi dans cet esprit, donc garde le site sous la main si tu aimes les formats concrets.

conclusion : Docker n’est pas le problème, l’approximation oui

Docker en prod, ça marche très bien. Mais pas en mode freestyle.

Les incidents dont on se souvient ne viennent pas d’un détail ésotérique du kernel. Ils viennent de trucs simples : une image non taguée, un disque rempli par les logs, un secret exposé, une mémoire illimitée. Et ce sont des erreurs faciles à éviter, une fois qu’on les a vues une fois.

Si tu veux pousser plus loin, passe faire un tour sur Le Blog Tech Pro de Samyn-Antoy ABASSE ( https://monblog-sa-abasse.blogspot.com ) : je publie régulièrement des articles pratiques sysadmin et productivité, dans le même style, avec des checklists et des méthodes que j’utilise vraiment.

Questions fréquemment posées

Pourquoi est-il déconseillé d'utiliser le tag « latest » pour les images Docker en production ?

Le tag « latest » signifie que vous ne savez pas exactement quelle version de l'image vous déployez. Cela peut entraîner des changements inattendus de dépendances ou de base d'OS lors des rebuilds, compliquant ainsi les rollbacks et la traçabilité. Il est préférable d'utiliser des tags immuables comme une version précise (ex. 1.3.7) ou un digest sha256.

Comment éviter de construire des images Docker trop volumineuses et pourquoi est-ce important ?

Les images trop grosses ralentissent les pulls, augmentent la surface d'attaque et gonflent le stockage du registre. Pour éviter cela, utilisez des multi-stage builds, privilégiez des images minimalistes comme Alpine ou distroless, et nettoyez les caches et artefacts inutiles dans votre Dockerfile.

Quels sont les risques d'exécuter un conteneur Docker en tant qu'utilisateur root et comment y remédier ?

Exécuter en root augmente le risque de compromission si un attaquant exploite le conteneur. En production, il faut créer un utilisateur non privilégié dans l'image, définir les permissions adéquates et utiliser des mécanismes comme readOnlyRootFilesystem ou runAsNonRoot sur Kubernetes pour renforcer la sécurité.

Pourquoi ne faut-il pas inclure de secrets directement dans l'image Docker ou dans le dépôt Git ?

Inclure des secrets dans l'image ou le dépôt expose ces informations sensibles à travers les layers Docker, caches CI ou historiques Git, rendant difficile leur suppression complète en cas de fuite. Il faut utiliser des solutions dédiées comme Docker secrets, Vault, ou injecter les secrets au runtime via l'orchestrateur.

Quelle est l'importance de définir des limites CPU et mémoire pour les conteneurs en production ?

Sans limites, un conteneur peut consommer excessivement ressources CPU ou mémoire, provoquant un crash du nœud entier et impactant tous les services. Définir ces limites permet d'isoler la consommation et garantir la stabilité globale du système.

Quel rôle joue la directive HEALTHCHECK dans un Dockerfile pour la production ?

La directive HEALTHCHECK permet à l'orchestrateur de vérifier régulièrement si le service dans le conteneur fonctionne correctement. Sans elle, il ne peut pas détecter automatiquement une défaillance fonctionnelle même si le conteneur tourne toujours, ce qui peut retarder la résolution d'incidents.

Enregistrer un commentaire

0 Commentaires

Comments

Nouveau Drop

Boutique Officielle

Soutenez le blog monblog-sa-abasse et découvrez nos vêtements & accessoires exclusifs en édition limitée.

Découvrir la collection
Paiement Sécurisé
Livraison Monde