Introduction
Docker Compose simplifie le développement et l’exploitation multi‑services, mais il est facile de livrer une pile lente si l’on conserve tous les paramètres par défaut. Les goulots d’étranglement les plus fréquents sont :
- E/S fichiers : les montages bind sur macOS et Windows peuvent être 5 à 10 fois plus lents que les volumes natifs Linux. Les bases de données en souffrent le plus.
- CPU et mémoire : des limites trop basses ou des voisins bruyants provoquent du throttling et du swapping.
- Réseau : sauts supplémentaires et NAT ajoutent de la latence ; les recherches DNS et les health‑checks bavards augmentent la charge.
- Journalisation : des logs non bornés grossissent et volent de l’I/O au travail réel.
- Conteneurs qui écrivent massivement sur disque : caches et fichiers temporaires dégradent le débit s’ils ne sont pas déplacés vers
tmpfs.
Ce guide présente une méthode sûre, par étapes, pour identifier les goulots, dimensionner les ressources et optimiser la latence comme le débit. Vous y trouverez des extraits Compose concrets pour NGINX, PostgreSQL et Redis, ainsi que des vérifications rapides à exécuter localement avant toute modification dans des environnements partagés.
Vue d’ensemble du flux de travail
Utilisez un flux court et reproductible pour que les améliorations soient évidentes et sans risque.
1. Définir les objectifs
- SLO de latence : p50, p95, p99 pour vos points de terminaison et commandes clés.
- Cibles de débit : requêtes par seconde, TPS, ops/sec.
- Plafonds de ressources : CPU et mémoire maximum par service.
2. Établir une ligne de base rapidement
- Démarrez la pile et attendez qu’elle soit saine.
- Enregistrez un instantané de 1 à 2 minutes de CPU, mémoire et I/O avec
docker stats. - Mesurez la latence HTTP :
curl -s -o /dev/null -w "time_connect=%{time_connect} time_starttransfer=%{time_starttransfer} time_total=%{time_total}\n" http://localhost:8080/
- Mesurez des opérations simples de base de données et de cache : lancez un
SELECTtrivial dans PostgreSQL et unGET/SETdans Redis.
3. Identifier les contraintes
- Points chauds d’E/S fichiers : bases de données ou services à fort taux d’écriture sur montages bind.
- Saturation CPU : conteneurs près de 100 % d’un cœur ou souvent throttlés.
- Pression mémoire : OOM kills ou récupération intensive ;
shared_buffersde la base trop petit. - Problèmes réseau : temps de connexion lents, retards DNS, ou backlog SYN élevé.
- Journalisation : débit de logs élevé ou fichiers volumineux.
4. Appliquer un changement à la fois
- Privilégiez les volumes nommés pour les bases de données. Utilisez les montages bind avec
cached/delegateduniquement pour le code source ou les configurations qui profitent de l’édition en direct. - Ajoutez
tmpfspour les caches chauds et les répertoires temporaires. - Définissez des limites CPU et mémoire conservatrices, puis augmentez progressivement.
- Bornez et compactez les logs avec le driver
local. - Ajustez les intervalles des health‑checks pour réduire le bruit sans sacrifier la sécurité.
5. Re‑tester et comparer
Répétez les mêmes vérifications curl et de charge. Conservez la même fenêtre de temps, la même concurrence et le même jeu de données que pour la ligne de base. Si les chiffres s’améliorent et que les taux d’erreur restent stables, conservez le changement. Sinon, revenez en arrière.
Plan pilote local
Commencez par un pilote étroit, facile à inspecter en local. L’exemple ci‑dessous utilise NGINX, PostgreSQL et Redis. Il se concentre sur l’E/S fichiers, les plafonds de ressources, la journalisation et les health‑checks. Remplacez l’upstream web et les identifiants de base de données pour adapter à votre pile.
compose.yml de référence
version: "3.9"
services:
web:
image: nginx:1.25-alpine
ports:
- "8080:80"
volumes:
# Config en montage bind read‑only pour commodité
- ./nginx.conf:/etc/nginx/nginx.conf:ro
# Cache dans un volume nommé pour éviter la lenteur du bind mount
- nginx_cache:/var/cache/nginx
healthcheck:
test: ["CMD", "wget", "-qO-" , "http://localhost" ]
interval: 10s
timeout: 2s
retries: 3
# Logs bornés et légers
logging:
driver: local
options:
max-size: "10m"
max-file: "3"
# Limites de départ conservatrices (supportées par docker compose sur Linux)
cpus: "1.5"
mem_limit: "512m"
# Augmenter les descripteurs de fichiers pour tests à forte concurrence
ulimits:
nofile: 65536
# Réduire les pertes de file d’attente de connexion sous charge
sysctls:
net.core.somaxconn: "65535"
db:
image: postgres:16-alpine
environment:
POSTGRES_PASSWORD: example
POSTGRES_DB: app
POSTGRES_USER: app
# Volume nommé pour stockage durable (plus rapide que bind mount sur macOS/Windows)
volumes:
- pgdata:/var/lib/postgresql/data
# Augmenter la mémoire partagée pour Postgres afin de réduire la contention IPC
shm_size: "512m"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d app" ]
interval: 10s
timeout: 3s
retries: 5
logging:
driver: local
options:
max-size: "10m"
max-file: "3"
cpus: "2"
mem_limit: "1g"
cache:
image: redis:7-alpine
command:
- "redis-server"
- "--appendonly"
- "no"
- "--maxmemory"
- "256mb"
- "--maxmemory-policy"
- "allkeys-lru"
healthcheck:
test: ["CMD", "redis-cli", "ping" ]
interval: 10s
timeout: 2s
retries: 5
logging:
driver: local
options:
max-size: "10m"
max-file: "3"
cpus: "1"
mem_limit: "384m"
# File d’attente d’acceptation plus grande sous charge burst
sysctls:
net.core.somaxconn: "65535"
volumes:
pgdata: {}
nginx_cache: {}
Notes
- Utilisez des volumes nommés pour Postgres et le cache NGINX. Cela évite les bind mounts lents sur macOS et Windows et garde Linux rapide.
- Conservez la configuration en montage bind read‑only pour la commodité, pas pour de l’I/O à haut débit.
- Les réglages
cpusetmem_limitsont des points de départ conservateurs. Adaptez‑les à votre hôte.
Ajustement optionnel des bind mounts pour macOS/Windows
Lorsque vous devez binder un chemin à forte écriture (déconseillé pour les bases de données), ajoutez les drapeaux de cohérence :
services:
app:
volumes:
- ./src:/app/src:cached # lecture majoritaire
- ./tmp:/app/tmp:delegated # écriture majoritaire
Réseau hôte Linux uniquement pour les bancs d’essai
Pour des expériences de latence sur Linux, vous pouvez éviter un saut NAT en utilisant le réseau hôte pour le point d’entrée HTTP :
services:
web:
network_mode: host # Linux seulement
ports: [] # non utilisé avec le réseau hôte
N’utilisez pas le réseau hôte sur macOS ou Windows, et évitez‑le pour le développement courant sauf besoin spécifique de benchmark.
Ajouter tmpfs pour les chemins d’écriture chauds
Réduisez l’I/O disque en déplaçant les répertoires temporaires ou de cache en RAM :
services:
web:
tmpfs:
- /var/cache/nginx
cache:
tmpfs:
- /tmp
Vérifications rapides de latence et de débit
Exécutez après que la pile est saine pour capturer une ligne de base :
- Latence HTTP (p50 sur un petit échantillon) :
curl -s -o /dev/null -w "time_connect=%{time_connect} time_starttransfer=%{time_starttransfer} time_total=%{time_total}\n" http://localhost:8080/
- Débit HTTP (si
heyest installé) :
hey -z 30s -c 50 http://localhost:8080/
- Vérification rapide PostgreSQL :
psql "postgresql://app:example@localhost:5432/app" -c "select 1;"
Si vous avez pgbench :
pgbench -S -T 30 -c 16 -j 4 "postgresql://app:example@localhost:5432/app"
- Vérification rapide Redis :
redis-cli -h localhost -p 6379 ping
Si vous avez redis-benchmark :
redis-benchmark -t get,set -n 100000 -q
Répétez les mêmes commandes après chaque modification. Maintenez la durée, la concurrence et le jeu de données constants pour que les résultats soient comparables.
Actions de réglage pratiques
- Éliminer les bind mounts lents là où ils nuisent le plus
- Utilisez des volumes nommés pour les bases de données et les caches de service.
- Gardez le code source et les configs en bind mount si l’édition en direct est nécessaire.
- Sur macOS/Windows, préférez
:cachedpour les montages à lecture majoritaire et:delegatedpour les montages à écriture majoritaire.
- Dimensionner correctement CPU et mémoire
- Partez de
cpusetmem_limitconservateurs et augmentez jusqu’à ce que le throttling disparaisse et que la latence ne progresse plus. - Envisagez d’épingler des CPU pour isoler les voisins bruyants :
services:
db:
cpuset: "1-2"
- Maîtriser la charge de journalisation
- Utilisez le driver
localet limitez la taille et la rotation. Évitezjson-filesans bornes.
- Ajuster les health‑checks
- Les health‑checks protègent la disponibilité mais peuvent générer de la charge. Utilisez une sonde simple et rapide, un intervalle de 10 à 30 s et un timeout court.
- Déplacer les données temporaires chaudes vers
tmpfs
- Idéal pour les caches et fichiers éphémères qu’il est sûr de perdre.
- Augmenter les files d’attente noyau quand c’est sûr
- Les taux de connexion élevés bénéficient d’un
somaxconnplus grand.
- Mesurer, ne pas deviner
- Relancez les mêmes tests pour des comparaisons avant/après. Notez les changements et les résultats.
Optimisation sûre via overrides et profils
Gardez votre compose.yml de base simple. Placez le réglage dans un fichier d’override pour pouvoir l’activer à la demande :
# compose.override.yml
services:
web:
tmpfs:
- /var/cache/nginx
environment:
NGINX_ENV: bench
db:
mem_limit: "2g"
cache:
command:
- "redis-server"
- "--appendonly"
- "no"
- "--save"
- ""
- "--maxmemory"
- "512mb"
- "--maxmemory-policy"
- "allkeys-lru"
Ou utilisez des profils pour des réglages réservés aux bancs d’essai :
services:
web:
profiles: ["bench" ]
network_mode: host # Linux seulement
ports: []
Activez avec :
COMPOSE_PROFILES=bench docker compose up -d
Liste de contrôle de dépannage
- Pics de latence après quelques minutes : vérifiez les logs pour des blocages de rotation, augmentez
tmpfspour les caches, confirmez l’absence d’OOM kills. - Écritures Postgres lentes : assurez‑vous que les données sont sur un volume nommé, augmentez
shm_size, gardez le WAL sur le même volume nommé. - Timeouts Redis en rafale : augmentez
somaxconnetnofile, envisagez le réseau hôte sur Linux pour les tests, vérifiez la politiquemaxmemory. - Throttling CPU : haussez
cpusou réduisez la concurrence ; vérifiez l’isolation viacpuset. - E/S fichiers macOS/Windows : remplacez les bind mounts par des volumes nommés pour les chemins chauds ; appliquez
:cachedou:delegateduniquement si le bind mount est indispensable.
Conclusion
Maintenez la boucle serrée : définissez les objectifs, établissez une ligne de base, changez une seule chose, re‑mesurez et consignez les résultats. Partez des valeurs par défaut sûres ci‑dessus, privilégiez les volumes nommés pour l’I/O chaud, bornez vos logs et utilisez tmpfs pour les écritures éphémères. Une fois qu’un pilote local montre des gains clairs, capturez‑les dans des overrides ou des profils pour que l’équipe puisse les activer de façon cohérente. Revérifiez vos chiffres à chaque évolution des images, des versions d’OS hôte et des charges de travail afin que votre pile Compose reste rapide et prévisible.