Présentation

Les applications web modernes fonctionnent rarement comme un seul processus monolithique. Elles sont plutôt composées de multiples services collaborateurs : une interface, une API, une base de données, un cache, une file d'attente de messages et peut-être un coureur de travail. La gestion manuelle du cycle de vie de chaque conteneur, de leur réseau et de leurs dépendances devient rapidement sujette aux erreurs et prend du temps. Docker Compose et Nginx offrent ensemble une solution facile à produire et éprouvée par la bataille pour définir et déployer ces systèmes multicontainers avec un frottement minimal. Docker Compose vous permet de décrire toute votre pile d'application dans un seul fichier YAML, tandis que Nginx agit comme un mandataire inverseur fiable et un équilibreur de charge, acheminant le trafic vers le service approprié.

Préalables

Avant de plonger dans le système, assurez-vous que vous avez installé les éléments suivants sur votre système :

  • Moteur à double sens (version 20.10 ou plus récent) – Installer Docker
  • Compose de Docker (V2 est maintenant intégré dans Docker CLI; vérifier avec ) – Installer Docker Compose
  • Familiarité de base avec les commandes terminales et la syntaxe YAML.

Si vous prévoyez de suivre la section de configuration SSL, vous pouvez également avoir un nom de domaine pointant vers votre serveur.

Comprendre Docker Compose en profondeur

Docker Compose n'est pas seulement un simple -enrouleur -encombre. C'est un outil déclaratif qui vous permet de définir des services, des réseaux, des volumes, des variables d'environnement, des politiques de redémarrage et des contrôles de santé dans un seul fichier appelé . La spécification Compose (maintenant partie du Compose Specification) standardise la description des applications multi-conteneurs, rendant votre configuration portable sur différents pipelines CI/CD et fournisseurs de cloud.

Services

Chaque image conteneur, ses ports, volumes, environnement et dépendances sont définis sous la touche . Les services peuvent communiquer entre eux via un réseau de ponts dédié que Compose crée automatiquement. Cela signifie que vous n'avez jamais besoin d'ouvrir les ports hôtes pour la communication interservice — seul le proxy inversé doit exposer les ports au monde extérieur.

Réseaux

Par défaut, Compose crée un réseau unique pour tous les services, leur donnant la découverte de service DNS en utilisant le nom de service comme nom d'hôte. Par exemple, un service nommé peut être atteint par d'autres services simplement comme . Vous pouvez également définir des réseaux personnalisés pour l'isolement — par exemple, en ne mettant que le proxy inverse sur un réseau public et en conservant des conteneurs de base de données sur un réseau interne.

networks:
 frontend:
 backend:
services:
 nginx:
 networks:
 - frontend
 app:
 networks:
 - frontend
 - backend
 db:
 networks:
 - backend

Volumes

Les volumes sont le mécanisme privilégié pour les données persistantes générées par les conteneurs Docker. Dans Compose, vous pouvez déclarer les volumes nommés au niveau supérieur et les monter dans les services. Ceci est essentiel pour les bases de données, les téléchargements de fichiers et tout service d'état.

volumes:
 db_data:
services:
 postgres:
 image: postgres:16
 volumes:
 - db_data:/var/lib/postgresql/data

Variables d'environnement

Externaliser la configuration en utilisant des variables d'environnement. Vous pouvez les coder en dur dans le fichier Composer pour le développement, mais pour la production vous devriez utiliser des fichiers ou des secrets Docker. Composer charge automatiquement un fichier situé dans le même répertoire si vous ne spécifiez pas .

services:
 api:
 image: my-api:latest
 env_file:
 - ./api.env
 environment:
 - NODE_ENV=production

Configuration de Nginx comme un mandataire inversé – avancé

Nginx brille comme un proxy inversé en raison de ses hautes performances, de son empreinte mémoire faible et de son ensemble de fonctionnalités riches. Dans une architecture multicontainer, Nginx se trouve au bord, accepte les demandes du client et les transmet au service approprié en fonction de l'URI de demande, des en-têtes, ou même du contenu corporel.

Proxy inverse de base avec plusieurs flux en amont

Voici une configuration plus complète qui montre comment répartir le trafic entre deux applications différentes, y compris la gestion des actifs statiques et WebSockets:

upstream app1_upstream {
 server app1:8000;
}

upstream app2_upstream {
 server app2:8001;
}

server {
 listen 80;
 server_name example.com;
 return 301 https://$server_name$request_uri;
}

server {
 listen 443 ssl http2;
 server_name example.com;

 ssl_certificate /etc/nginx/certs/fullchain.pem;
 ssl_certificate_key /etc/nginx/certs/privkey.pem;

 # Security headers
 add_header X-Frame-Options "SAMEORIGIN" always;
 add_header X-Content-Type-Options "nosniff" always;
 add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

 # App 1 – main web frontend
 location / {
 proxy_pass http://app1_upstream;
 proxy_set_header Host $host;
 proxy_set_header X-Real-IP $remote_addr;
 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 proxy_set_header X-Forwarded-Proto $scheme;
 }

 # App 2 – admin dashboard
 location /admin/ {
 proxy_pass http://app2_upstream/;
 proxy_set_header Host $host;
 proxy_set_header X-Real-IP $remote_addr;
 }

 # WebSocket support (e.g., for live updates)
 location /ws/ {
 proxy_pass http://app1_upstream;
 proxy_http_version 1.1;
 proxy_set_header Upgrade $http_upgrade;
 proxy_set_header Connection "upgrade";
 }

 # Static assets – serve directly for better performance
 location /static/ {
 alias /var/www/static/;
 expires 30d;
 add_header Cache-Control "public, immutable";
 }
}

Remarquez l'utilisation de blocs . Ils vous permettent de définir un groupe de serveurs pour l'équilibrage de charge. Même avec un seul serveur, l'utilisation d'un groupe en amont facilite l'ajout de répliques plus tard sans toucher le bloc serveur.

SSL/TLS avec Encryptons

Pour la production, le service HTTPS n'est pas négociable. Vous pouvez automatiser le renouvellement de certificat en utilisant Certbot ou acme.sh à l'intérieur d'un conteneur sidecar. Un modèle commun est de monter les certificats en volumes dans le conteneur Nginx. Par exemple:

services:
 nginx:
 image: nginx:alpine
 volumes:
 - ./nginx.conf:/etc/nginx/conf.d/default.conf
 - ./certs:/etc/nginx/certs:ro
 - ./static:/var/www/static:ro
 ports:
 - "80:80"
 - "443:443"
 depends_on:
 - app1
 - app2

Pour le renouvellement automatique, vous pouvez ajouter un conteneur comme ou qui exécute un travail de cron et recharge Nginx lorsque les certificats sont rafraîchis.

Équilibre de charge et contrôles de santé

Lorsque vous adaptez un service à plusieurs répliques, Nginx peut distribuer des requêtes en utilisant un robin rond, des connexions moins importantes ou un hachage IP. Combinez ceci avec l'option Docker Compose , :

upstream api_servers {
 least_conn;
 server api:80 max_fails=3 fail_timeout=30s;
}

Pour détecter les contenants malsains, Nginx peut être configuré avec (qui nécessite NGINX Plus ou utilise le de la version open-source avec un petit contournement). Une solution plus simple est de se fier aux contrôles de santé de Docker et de n'enregistrer que les contenants qui passent.

Construire le fichier composé de Docker – Un exemple du monde réel

Let="s combine tout en un fichier Compose pratique pour une application web composée d'une API Node.js, d'un frontend React desservi par Nginx, d'une base de données PostgreSQLTM et d'un cache Redis. Le conteneur Nginx servira des requêtes d'API statiques et proxy.

version: "3.9"

services:
 nginx:
 image: nginx:alpine
 container_name: reverse-proxy
 restart: unless-stopped
 volumes:
 - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro
 - ./frontend/build:/var/www/frontend:ro
 - ./certs:/etc/nginx/certs:ro
 ports:
 - "80:80"
 - "443:443"
 depends_on:
 - api
 - frontend
 networks:
 - public

 frontend:
 image: my-frontend:latest
 # In production, frontend is built into static files and served by Nginx
 # This container may run a dev server, but Nginx will bypass it for static files
 container_name: frontend-dev
 ports:
 - "3000:3000"
 networks:
 - public
 # healthcheck: ...

 api:
 image: my-api:latest
 container_name: api-server
 restart: unless-stopped
 env_file:
 - ./api/.env.production
 depends_on:
 postgres:
 condition: service_healthy
 redis:
 condition: service_started
 networks:
 - public
 - internal
 healthcheck:
 test: ["CMD", "curl", "-f", "http://localhost:4000/health"]
 interval: 30s
 timeout: 10s
 retries: 3

 postgres:
 image: postgres:16-alpine
 container_name: database
 restart: unless-stopped
 environment:
 POSTGRES_USER: app_user
 POSTGRES_DB: app_db
 POSTGRES_PASSWORD_FILE: /run/secrets/db_password
 volumes:
 - pgdata:/var/lib/postgresql/data
 secrets:
 - db_password
 networks:
 - internal
 healthcheck:
 test: ["CMD-SHELL", "pg_isready -U app_user"]
 interval: 10s
 timeout: 5s
 retries: 5

 redis:
 image: redis:7-alpine
 container_name: cache
 restart: unless-stopped
 volumes:
 - redisdata:/data
 networks:
 - internal
 healthcheck:
 test: ["CMD", "redis-cli", "ping"]
 interval: 10s
 timeout: 3s

volumes:
 pgdata:
 redisdata:

networks:
 public:
 internal:
 internal: true

secrets:
 db_password:
 file: ./secrets/db_password.txt

Points clés de ce fichier :

  • Isolation du réseau: Le réseau est défini comme , ce qui signifie que seuls les services connectés à lui peuvent communiquer. La base de données et le cache ne sont pas accessibles de l'extérieur de la superposition Docker.
  • Secrets: Des données sensibles comme les mots de passe sont stockées comme des secrets Docker, montés comme des fichiers à l'intérieur du conteneur. Cela évite de les fuiter dans des variables d'environnement qui peuvent être exposées via .
  • : Les services déclarent des contrôles de santé afin que puisse attendre la condition . Nginx ne commencera qu'après que l'API est en bonne santé.
  • Assets statiques: La sortie de construction frontend est montée directement dans Nginx, permettant à Nginx de servir des fichiers statiques sans toucher le serveur frontend dev. Cela améliore les performances et sépare les préoccupations.

Déploiement de l'application – étape par étape

Avec le fichier Compose et la configuration Nginx prêts, le déploiement implique quelques commandes simples.

project/
├── docker-compose.yml
├── nginx/
│ └── nginx.conf
├── api/
│ └── .env.production
├── frontend/
│ └── build/
├── certs/
│ └── (fullchain.pem, privkey.pem)
└── secrets/
 └── db_password.txt

Puis lancez :

docker compose pull # pull latest images
docker compose up -d # start all services in background
docker compose ps # verify services are running
docker compose logs -f # tail logs for all services

Une fois tout est terminé, testez l'application en visitant votre domaine. Vérifiez que les journaux Nginx confirment que les requêtes sont correctement proxées. Si vous devez mettre à jour les variables de configuration ou d'environnement, modifiez les fichiers pertinents et exécutez :

docker compose up -d --no-deps --build nginx # rebuild only nginx if config changed

Pour les déploiements à temps zéro, envisager d'utiliser bleu-vert ou des mises à jour de roulement[ avec la fonction Compose=] et les contrôles de santé en amont de Nginx.

Élargissement et réglage des performances

Docker Compose rend trivial d'étendre les services apatrides comme l'API :

docker compose up -d --scale api=3

Maintenant trois répliques du conteneur API sont en cours d'exécution. Nginx, configuré avec le bloc en amont, distribuera automatiquement les requêtes parmi eux. Pour améliorer encore les performances:

  • Activer la compression Gzip[ dans Nginx pour les réponses texte.
  • Filtre les en-têtes appropriés du cache pour les actifs statiques (comme indiqué dans l'exemple).
  • Utilisez un CDN pour la livraison globale de fichiers statiques.
  • Tune Nginx traite les processus [ pour correspondre aux cœurs du processeur:
  • Augmentation des limites de connexion[:

Pour les bases de données, assurez-vous d'avoir une connexion dans votre API et envisagez d'utiliser PgBoncer comme conteneur de sidecar.

Dépannage de problèmes communs

Même avec une configuration bien structurée, les choses peuvent mal tourner. Voici des pièges fréquents et comment les réparer:

  • Container ne peut pas atteindre un autre service par son nom[ : Vérifiez que les deux services sont sur le même réseau Docker. Utilisez pour tester.
  • Nginx retourne 502 Bad Gateway : Le conteneur en amont peut ne pas fonctionner ou ne pas écouter sur le port prévu. Vérifiez avec . Assurez-vous également que l'URL proxy pass inclut le protocole et le port corrects.
  • Erreurs de permission avec des volumes: Les fichiers montés dans des conteneurs héritent de la propriété de l'hôte. Utilisez des espaces de noms d'utilisateur ou définissez la directive dans Nginx et le conteneur de service.
  • Certificat SSL ne renouvelant pas: Si vous utilisez certbot standalone, assurez-vous que les ports 80 et 443 ne sont pas bloqués par Nginx pendant le renouvellement.
  • Les variables environnementales ne sont pas chargées: Vérifiez que le fichier est au bon endroit ou utilisez la directive correctement. Vous pouvez imprimer env à l'intérieur du conteneur avec .

Pratiques exemplaires en matière de sécurité

La mise en service d'applications multiconteneurs dans la production nécessite une vigilance:

  • Ne jamais exécuter de conteneurs comme racine sauf si nécessaire. Utilisez la directive ou spécifiez une dans le fichier Docker.
  • Gardez les images à jour avec un balayage de vulnérabilité régulier. Utilisez les images de base officielles et les versions de broche.
  • Restriction de l'exposition au réseau: Ne exposer que les ports proxy inversés à Internet. Tous les autres services devraient être sur des réseaux internes.
  • Utilisez la gestion des secrets pour les mots de passe, les clés API et les jetons. Les secrets Docker sont un bon début ; pour les configurations plus grandes, considérez HashiCorp Vault.
  • Activer Docker Content Trust[ pour vérifier les signatures d'image.
  • Loger et vérifier régulièrement l'activité du conteneur en utilisant ou une solution centralisée de l'enregistrement comme la pile ELK.
  • Appliquez les limites de ressources[ dans votre fichier Composez pour empêcher un seul conteneur de mourir de faim :
services:
 api:
 deploy:
 resources:
 limits:
 cpus: '0.50'
 memory: 256M
 reservations:
 cpus: '0.25'
 memory: 128M

Ces limites sont respectées lors de l'utilisation de l'essaim Docker; pour Compose simple, elles sont imposées avec (bien qu'il soit préférable d'utiliser l'essaim ou les K8 pour l'orchestration de production).

Conclusion

En définissant votre pile dans un seul fichier YAML, vous obtenez la parité environnementale du développement à la production. Nginx, comme mandataire inverse, fournit un point central pour la terminaison SSL, le routage du trafic, l'équilibrage de charge et le cache. Nous avons couvert les composants essentiels : définitions de service, mise en réseau, volumes, variables d'environnement, configuration Nginx avec optimisations de sécurité et de performance, conseils de mise à l'échelle et dépannage. Avec cette fondation, vous êtes prêt à concevoir, déployer et exploiter des applications conteneurisées modernes avec confiance. Pour plus de détails, consultez la documentation Docker Compose et la documentation Nginx, et explorez des exemples du monde réel provenant de l'exemple de Compose impressionnant pour vous inspirer.