En résumé : FuryBee regroupe une vingtaine de sites et d'applications publics, servis par 35 conteneurs sur un seul serveur que j'exploite seul. La recette : Docker Compose, un reverse proxy Traefik, Cloudflare devant tout, une observabilité complète avec Prometheus, Grafana et Loki, et des sauvegardes chiffrées surveillées. Et surtout, une liste de pièges que je préfère partager.

L'architecture

Tout tient dans un projet Docker Compose sur un serveur de 12 cœurs :

  • Traefik reçoit tout le trafic et le route vers le bon conteneur. Chaque service déclare lui-même son nom de domaine par des labels Docker, que Traefik découvre automatiquement. Les certificats TLS sont générés par validation DNS auprès de Cloudflare.
  • Les applications : sites statiques, applications Node.js et Nuxt, jeux multijoueurs en temps réel, outils pour développeurs.
  • Les données : Redis pour les applications qui en ont besoin, et une base PostgreSQL dédiée pour celle qui gère des données financières.
  • L'observabilité : Prometheus, Grafana, Loki, Alloy, cAdvisor et node-exporter.

Un sous-domaine inconnu est redirigé vers le portail principal, et chaque déploiement se termine par une purge du cache Cloudflare.

Cloudflare devant tout, et rien d'autre

Tous les domaines passent par Cloudflare, et le serveur n'accepte que les adresses IP de Cloudflare. Un middleware Traefik filtre l'entrée HTTPS, avec la liste officielle des plages d'adresses de Cloudflare, rafraîchie automatiquement chaque semaine par un script.

Conséquences pratiques :

  • un appel direct à l'adresse du serveur reçoit une erreur 403 : impossible de contourner le pare-feu et le cache de Cloudflare ;
  • l'adresse réelle des visiteurs se lit dans l'en-tête Cf-Connecting-Ip des journaux d'accès, pas dans l'adresse de connexion, qui est celle de Cloudflare ;
  • tout nouveau domaine doit être proxifié par Cloudflare, sinon il est bloqué.

L'observabilité

  • Métriques : Prometheus collecte toutes les 15 secondes les métriques des conteneurs, du serveur et de Traefik, conservées 30 jours.
  • Journaux : Grafana Alloy envoie les journaux des conteneurs vers Loki, conservés 30 jours, avec les journaux d'accès de Traefik en JSON.
  • Alertes : gérées par Grafana. Les règles sur les conteneurs sont dynamiques : ajouter un service ne demande pas de mettre à jour une liste.

Les sauvegardes

Chaque nuit, un script sauvegarde les bases, Redis, la configuration de Grafana et les certificats. Il les envoie dans une seule archive chiffrée (AES-256) vers un stockage S3 versionné, avec une durée de conservation de 90 jours. Le compte utilisé pour l'envoi ne peut pas supprimer de sauvegardes : un serveur compromis ne pourrait pas effacer l'historique.

Le script publie aussi ses propres métriques, et une alerte se déclenche si la dernière sauvegarde a plus de 48 heures. Elle a servi : deux nuits de suite, l'envoi a échoué parce que l'outil d'envoi n'était pas dans le PATH minimal de cron. L'alerte est remontée, le script ajoute désormais ce chemin lui-même.

Les pièges rencontrés

Les certificats qui expirent sans prévenir. Après environ deux mois sans redémarrage, Traefik a cessé de renouveler ses certificats, sans la moindre erreur dans ses journaux. Le symptôme côté visiteurs : une erreur 526 de Cloudflare (certificat d'origine invalide). Un simple redémarrage ne suffisait pas : il a fallu recréer le conteneur, qui a renouvelé tous les certificats en quelques secondes. Le diagnostic le plus rapide : la date de modification du fichier où Traefik stocke ses certificats, figée depuis des semaines.

Le collecteur de journaux qui se lit lui-même. Un collecteur qui envoie aussi ses propres journaux crée une boucle infinie : avec l'ancien outil (Promtail), 9 millions de lignes en une soirée. Exclure ces conteneurs par une simple règle de filtrage ne suffit pas, il faut les exclure dès la lecture de la liste des conteneurs.

La montée de version majeure de Traefik (v2 → v3). Je l'ai validée avec un second Traefik v3 de test, sur d'autres ports, lisant les mêmes labels, avec l'environnement de test de Let's Encrypt. Une seule incompatibilité : la syntaxe de la règle qui attrape les sous-domaines inconnus, qui utilise désormais les expressions régulières du langage Go.

Une IPv6 cassée qui ralentit tout. L'interface IPv6 du serveur était configurée mais ne faisait passer aucun trafic : certains appels sortants attendaient l'échec de l'IPv6 avant de passer en IPv4. Forcer la préférence IPv4 a fait passer l'un de ces appels de 3 minutes à 0,6 seconde.

Les petites règles qui évitent les grosses pannes :

  • des versions d'images figées, jamais latest, pour qu'une mise à jour soit toujours un choix ;
  • une rotation des journaux Docker déclarée service par service, faute d'accès administrateur au démon ;
  • un déploiement sans interruption pour les jeux en temps réel, dont les joueurs restent connectés par WebSocket : un conteneur temporaire prend le relais pendant la mise à jour.

Ce que j'en retiens

La même discipline qu'en entreprise s'applique à petite échelle, sans équipe d'exploitation pour rattraper les oublis : tout ce qui n'est pas automatisé, surveillé et documenté finit par casser, généralement sans prévenir. C'est cette expérience que j'apporte aux équipes qui veulent fiabiliser leur infrastructure.