24 Conteneurs sur un Seul VPS : Architecture Technique Haute Densité pour DevOps Intermédiaire/Sénior
Un seul serveur, 6 Go de RAM, 24 conteneurs — et tout fonctionne stablement depuis plus d'un an. Ce n'est pas une expérience, mais une infrastructure de production supportant sept projets actifs : un bot EdTech, un service anti-spam, un backend IA, un moteur de méta-recherche, une inférence locale de LLM, et deux services utilitaires. Aucun ne génère de revenus, mais tous sont critiques pour les activités opérationnelles. L'objectif n'était pas de réduire les coûts pour eux-mêmes, mais un compromis conscient : gestion manuelle au lieu de services gérés, charge prévisible au lieu d'élasticité cloud, et intégration profonde au lieu d'abstraction.
Principes Architecturaux : Isolation, Observabilité, Résilience
L'approche principale repose sur une isolation réseau stricte. Chaque projet réside dans son propre réseau Docker (bridge), éliminant les dépendances croisées et minimisant la surface d'attaque. Un réseau partagé shared est utilisé uniquement pour les services système (ex. : monitoring ou Redis commun). Les ports internes (9200, 8088, 9222) sont accessibles exclusivement via 127.0.0.1; l'accès externe est bloqué au niveau des configurations iptables et nginx.
La résilience est assurée par des dépendances healthcheck dans docker-compose.yml. Par exemple, les services PHP du projet EdTech ne démarrent pas tant qu'un tunnel SSH vers la base de données distante ne passe pas un test nc -z 127.0.0.1 3306. Cela garantit que l'application n'essaiera pas de se connecter à une base de données indisponible et ne plantera pas la file d'attente avec des erreurs.
L'observabilité est construite sans Prometheus/Grafana en raison des contraintes de RAM. À la place, un script bash s'exécute toutes les 15 minutes :
- Vérifie les codes de statut HTTP des endpoints publics (
/health,/webhook/advert) - Identifie les conteneurs malsains via
docker ps --filter "health=unhealthy" - Surveille l'espace disque et l'utilisation du swap
- Envoie des alertes vers un chat Telegram via curl vers l'API Bot
Cela fournit une visibilité minimale mais suffisante pour un seul administrateur.
Composants Critiques : Elasticsearch, Chrome Headless et Tunnels SSH
Trois composants définissent les limites de performance du VPS :
- Elasticsearch 8.12.2 consomme 1,47 Go de RAM. Sans une limite explicite
memory: 2gdansdeploy.resources.limits, la JVM commence à faire du swap sur les processus voisins, causant de l'instabilité. Pour un index d'environ 50K documents et la journalisation, cela offre le meilleur équilibre entre fonctionnalité et charge. Une alternative comme Meilisearch nécessiterait de migrer les index existants et de modifier le code client, classée comme une tâche à fort effort et faible ROI à ce stade.
- Chrome Headless (zenika/alpine-chrome) occupe 636 Mo de RAM, bien qu'il soit utilisé seulement une fois par heure pour le scraping de rendu JS. Exécuter
on-demandviadocker run --rméconomiserait de la mémoire mais ajouterait un délai de 5 à 10 secondes par appel. Pour des services avec des SLA stricts, c'est inacceptable ; ici, c'est un compromis acceptable.
- Tunnels SSH sont implémentés dans des conteneurs alpine séparés avec
autossh. Ils fournissent un accès sécurisé à MySQL sur un hôte partagé sans ouvrir de ports externement. La configuration inclutServerAliveInterval=30,AUTOSSH_GATETIME=0, et un healthcheck vianc. Cette solution fonctionne avec tous les hôtes supportant SSH et remplace entièrement les bases de données gérées payantes.
Optimisation Nginx et PHP : Réduire la Latence de Millisecondes
Nginx est configuré exclusivement pour les webhooks et les API — aucun fichier statique sauf /health. L'optimisation clé consiste à décharger le traitement du webhook Telegram vers un fichier PHP séparé (webhook-advert.php) qui contourne le bootstrap Laravel. Cela réduit le temps de réponse de 80–120 ms à 15–25 ms, ce qui est critique lors d'appels massifs de webhooks depuis l'API Telegram (délai maximal — 60 secondes).
La configuration inclut également :
- Redirection forcée HTTP → HTTPS
- Refus de traiter tout chemin autre que
/health,/webhook/, et/api/telegram/ - Augmentation de
fastcgi_read_timeoutà 120 secondes pour les tâches longues - Utilisation de
realpath_rootpour une gestion correcte deindex.php
SSL et Sécurité : Trois Stratégies sur un Hôte Unique
La terminaison SSL est organisée de manière hybride :
- getssl + Let's Encrypt — pour les domaines nécessitant un chiffrement de bout en bout sans Cloudflare
- Proxy Cloudflare — pour la plupart des domaines : la terminaison SSL se fait au bord, le trafic vers le VPS passe en HTTP, mais est protégé par un filtrage IP (seul ASN Cloudflare autorisé)
- Mise à jour Manuelle — une mesure temporaire pour le backend IA, actuellement en cours d'automatisation via Caddy
La configuration Cloudflare inclut Always Use HTTPS, WAF, et Rate Limiting (100 requêtes/min par IP) obligatoires, compensant l'absence de WAF côté VPS.
Points Clés
- Une isolation réseau stricte via des réseaux Docker séparés empêche les défaillances en cascade et simplifie les audits de sécurité.
- Les dépendances healthcheck dans docker-compose garantissent que les services ne démarrent que lorsque leurs dépendances sont prêtes (ex. : tunnel SSH vers la DB).
- Elasticsearch nécessite une limite de mémoire dure (2 Go) ; sinon, la JVM fait du swap sur les conteneurs voisins, déclenchant le tueur OOM.
- Garder Chrome Headless en mode toujours actif est justifié à faible fréquence d'utilisation si la latence de démarrage est critique pour la logique métier.
- Le monitoring Bash avec alertes Telegram est un remplacement fonctionnel pour Prometheus/Grafana sous contraintes de RAM, à condition que les SLO soient clairement définis.
— Editorial Team
Aucun commentaire pour le moment.