Depuis plusieurs années, j'accompagne des équipes qui déploient leurs applications avec Docker Kanvas pour migrer vers Kubernetes. Que ce soit des startups ou des structures plus établies, je retrouve régulièrement les mêmes erreurs. Certaines sont bénignes, d'autres peuvent coûter cher : failles de sécurité, pannes en cascade, données perdues.
Cet article n'est pas un tutoriel Docker -- si vous cherchez ça, consultez plutôt mon guide complet Docker. Ici, je partage un retour d'expérience terrain sur les sept erreurs que je croise le plus souvent en monitoring Linux en production, et comment les corriger concrètement.
1. Des conteneurs qui tournent en root
C'est de loin l'erreur la plus fréquente. Par défaut, Docker exécute les processus dans le conteneur en tant que root. Beaucoup d'équipes ne changent jamais ce comportement, souvent parce que "ça marche comme ça".
Même sans évasion, un processus root dans le conteneur peut écraser des fichiers montés en volume, modifier la configuration réseau du conteneur, ou consommer des ressources sans restriction.
La correction est simple : créez un utilisateur dédié dans votre Dockerfile et utilisez l'instruction USER.
FROM node:20-alpine
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
COPY --chown=appuser:appgroup . .
RUN npm ci --only=production
USER appuser
EXPOSE 3000
CMD ["node", "server.js"]
2. Utiliser le tag :latest en production
Le tag :latest est un piège classique. Il ne désigne pas "la dernière version stable" mais simplement "le dernier build poussé sans tag explicite". Deux déploiements identiques à une heure d'intervalle peuvent produire des conteneurs différents si l'image a été mise à jour entre-temps.
Utilisez toujours un tag précis, idéalement le digest SHA256 pour les environnements critiques.
services:
api:
# Mauvais : imprevisible
# image: myapp:latest
# Correct : version explicite
image: myapp:2.4.1
# Optimal : immutable par digest
# image: myapp@sha256:a1b2c3d4e5f6...
Intégrez le tagging dans votre pipeline CI/CD. Chaque build produit une image avec un tag unique (numéro de version, hash de commit, timestamp). C'est la base d'un déploiement fiable.
3. Pas de healthcheck
Docker sait si un conteneur tourne. Il ne sait pas si l'application à l'intérieur fonctionne. Sans healthcheck, un conteneur dont le processus principal est bloqué (deadlock, pool de connexions saturé, mémoire corrompue) reste marqué comme "running" indéfiniment.
L'orchestrateur (Docker Swarm, Kubernetes) ne peut pas prendre de décision intelligente sans cette information. Pas de redémarrage automatique, pas de retrait du load balancer.
FROM python:3.12-slim
COPY . /app
WORKDIR /app
RUN pip install --no-cache-dir -r requirements.txt
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3
CMD curl -f http://localhost:8000/health || exit 1
USER appuser
CMD ["gunicorn", "main:app", "-b", "0.0.0.0:8000"]
4. Des secrets dans les variables d'environnement
Les variables d'environnement sont pratiques pour la configuration, mais elles sont un mauvais choix pour les secrets. Elles apparaissent en clair dans docker inspect, dans les logs de debug, dans les dumps de processus, et souvent dans le code source via les fichiers .env commités par erreur.
docker inspect sur un serveur partagé. Les variables d'environnement ne sont pas un mécanisme de sécurité.Utilisez Docker Secrets (en Swarm) ou un gestionnaire de secrets externe (Vault, AWS Secrets Manager). En compose, vous pouvez monter les secrets en fichiers.
services:
api:
image: myapp:2.4.1
secrets:
- db_password
- api_key
environment:
# Configuration non sensible uniquement
LOG_LEVEL: info
DB_HOST: postgres
secrets:
db_password:
file: ./secrets/db_password.txt
api_key:
file: ./secrets/api_key.txt
Dans l'application, lisez le secret depuis le fichier monté dans /run/secrets/. C'est un changement mineur dans le code qui améliore considérablement la posture de sécurité.
5. Volumes mal montés et problèmes de permissions
Les volumes Docker sont essentiels pour persister les données, mais leur montage est une source constante de problèmes. Le cas typique : un conteneur qui tourne avec un utilisateur non-root (comme recommandé au point 1) mais dont le volume est détenu par root sur l'hôte.
Résultat : l'application ne peut ni lire ni écrire ses propres données. Pire, certains corrigent ça avec chmod 777, ce qui revient à ouvrir les portes à tous les processus du système.
FROM python:3.12-slim
RUN groupadd -r appgroup && useradd -r -g appgroup -u 1001 appuser
RUN mkdir -p /app/data && chown -R appuser:appgroup /app/data
VOLUME /app/data
USER appuser
WORKDIR /app
CMD ["python", "main.py"]
services:
api:
image: myapp:2.4.1
user: "1001:1001"
volumes:
- app_data:/app/data
volumes:
app_data:
driver: local
6. Aucune limite de ressources
Sans limites définies, un seul conteneur peut consommer toute la mémoire ou tout le CPU de la machine hôte. J'ai vu des serveurs de production tomber parce qu'une fuite mémoire dans un conteneur a déclenché l'OOM killer du noyau, qui a tué des processus critiques au hasard -- y compris d'autres conteneurs.
services:
api:
image: myapp:2.4.1
deploy:
resources:
limits:
cpus: "1.0"
memory: 512M
reservations:
cpus: "0.25"
memory: 128M
Les limits définissent le plafond absolu. Les reservations garantissent un minimum disponible pour le conteneur. Dimensionnez ces valeurs en fonction de vos tests de charge, pas au doigt mouillé. Un conteneur qui atteint régulièrement sa limite mémoire a probablement une fuite ou est sous-dimensionné.
7. Des logs qui remplissent le disque
Par défaut, Docker stocke les logs de chaque conteneur dans un fichier JSON sur le disque, sans aucune limite de taille ni rotation. Une application un peu bavarde peut générer des gigaoctets de logs en quelques jours. J'ai diagnostiqué plus d'une panne de production causée par un disque plein à cause des logs Docker.
Configurez systématiquement la rotation des logs, soit au niveau du daemon Docker, soit par conteneur.
services:
api:
image: myapp:2.4.1
logging:
driver: json-file
options:
max-size: "10m"
max-file: "5"
compress: "true"
Pour une configuration globale, modifiez le fichier /etc/docker/daemon.json :
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
Conclusion
Ces sept erreurs ne sont pas des cas de figure théoriques. Ce sont des problèmes que je rencontre régulièrement, y compris dans des équipes expérimentées. Docker simplifie le déploiement, mais il n'élimine pas la complexité -- il la déplace.
Pour récapituler les bonnes pratiques essentielles :
- Exécutez vos conteneurs avec un utilisateur non-root
- Taguez vos images avec des versions explicites
- Implémentez des healthchecks qui testent réellement l'état de l'application
- Utilisez un gestionnaire de secrets, pas des variables d'environnement
- Gérez les permissions des volumes avec des UID/GID fixes
- Définissez des limites de CPU et mémoire pour chaque conteneur
- Configurez la rotation des logs dès le premier déploiement
Aucune de ces corrections n'est complexe individuellement. C'est leur application systématique qui fait la différence entre un environnement Docker fragile et une infrastructure de production fiable. Si vous débutez avec Docker, mon tutoriel Docker couvre les fondamentaux avant d'aborder ces sujets avancés.
Commentaires