xpeditis2.0/docs/deployment/hetzner/01-architecture.md
2026-03-26 18:08:28 +01:00

11 KiB
Raw Blame History

01 — Architecture de production sur Hetzner


Vue d'ensemble

┌─────────────────────────────────────────────────────────────────────────┐
│                           INTERNET                                       │
└───────────────────────────────┬─────────────────────────────────────────┘
                                │
                    ┌───────────▼───────────┐
                    │   Cloudflare          │
                    │   WAF + CDN + DNS     │
                    │   TLS termination     │
                    └───────────┬───────────┘
                                │ HTTPS (443)
                    ┌───────────▼───────────┐
                    │  Hetzner Load         │
                    │  Balancer (LB11)      │
                    │  €7.49/mois           │
                    └─────┬─────────┬───────┘
                          │         │
           ┌──────────────▼──┐  ┌───▼──────────────┐
           │  Worker Node 1  │  │  Worker Node 2   │
           │  CX42 (8c/16G)  │  │  CX42 (8c/16G)  │
           │  €21.49/mois    │  │  €21.49/mois     │
           │                 │  │                  │
           │ ┌─────────────┐ │  │ ┌─────────────┐ │
           │ │ NestJS Pod  │ │  │ │ NestJS Pod  │ │
           │ │ (backend)   │ │  │ │ (backend)   │ │
           │ └─────────────┘ │  │ └─────────────┘ │
           │ ┌─────────────┐ │  │ ┌─────────────┐ │
           │ │ Next.js Pod │ │  │ │ Next.js Pod │ │
           │ │ (frontend)  │ │  │ │ (frontend)  │ │
           │ └─────────────┘ │  │ └─────────────┘ │
           └────────┬────────┘  └────────┬─────────┘
                    │  Réseau privé Hetzner (10.0.0.0/16)
           ┌────────▼────────────────────▼─────────┐
           │         Control Plane Node             │
           │         CX22 (2c/4G) €5.11/mois       │
           │         k3s server (etcd)              │
           └────────────────────────────────────────┘
                                │
        ┌───────────────────────┼───────────────────────┐
        │                       │                       │
┌───────▼───────┐   ┌───────────▼──────────┐   ┌───────▼───────┐
│  PostgreSQL   │   │       Redis           │   │  Hetzner      │
│  Neon.tech    │   │  Upstash (serverless) │   │  Object       │
│  ou self-host │   │  ou self-hosted       │   │  Storage      │
│  $19/mois     │   │  $0-10/mois          │   │  S3-compat.   │
│               │   │                      │   │  €4.99/mois   │
└───────────────┘   └──────────────────────┘   └───────────────┘

Composants et rôles

Couche réseau

Composant Rôle Port
Cloudflare DNS, WAF, CDN, protection DDoS, cache assets 443 (HTTPS)
Hetzner Load Balancer Distribution trafic entre workers, sticky sessions WebSocket 80, 443
Réseau privé Hetzner Communication inter-nœuds (10.0.0.0/16), base de données Interne

Couche Kubernetes (k3s)

Composant Rôle Ressource
Control Plane (CX22) etcd, kube-apiserver, scheduler, controller-manager 2 vCPU / 4 GB
Worker Nodes (CX42) Exécution des pods NestJS + Next.js 8 vCPU / 16 GB chacun
Traefik Ingress Routage HTTP/HTTPS, sticky sessions Socket.IO Built-in k3s
cert-manager TLS automatique via Let's Encrypt In-cluster
Hetzner Cloud Controller Provisionne LB + volumes depuis Kubernetes In-cluster
Hetzner CSI Driver PersistentVolumes sur Hetzner Volumes In-cluster

Couche application

Pod Image Replicas Ports
xpeditis-backend ghcr.io/<org>/xpeditis-backend:latest 215 4000
xpeditis-frontend ghcr.io/<org>/xpeditis-frontend:latest 18 3000

Couche données

Service Option MVP Option Production Protocole
PostgreSQL 15 Neon.tech Pro ($19/mois) Self-hosted sur CX32 5432
Redis 7 Upstash free ($0-10/mois) Self-hosted StatefulSet 6379
Stockage fichiers Hetzner Object Storage (€4.99/mois) Idem (scale automatique) HTTPS/S3 API

Flux réseau détaillé

Client Browser
    │ HTTPS
    ▼
Cloudflare (cache miss → forward)
    │ HTTPS, header CF-Connecting-IP
    ▼
Hetzner Load Balancer :443
    │ HTTP (TLS terminé par Cloudflare ou cert-manager)
    ▼
Traefik Ingress (api.xpeditis.com)
    │ HTTP :80 interne
    ▼
NestJS Pod (port 4000)
    ├── Redis (cache rate:FSN:HAM:20ft) → HIT → retour direct
    └── MISS → 5× appels APIs carriers (Maersk/MSC/etc.)
              └── Réponse → Store Redis TTL 15min
                         └── Réponse client

Connexion WebSocket (notifications temps réel)

Client Browser
    │ wss:// upgrade
    ▼
Cloudflare (WebSocket proxy activé)
    │
    ▼
Hetzner LB (sticky session cookie activé)
    │ Même backend pod à chaque reconnexion
    ▼
Traefik (annotation sticky cookie)
    │
    ▼
NestJS Pod /notifications namespace (Socket.IO)
    ├── Auth: JWT validation on connect
    ├── Join room: user:{userId}
    └── Redis pub/sub → broadcast cross-pods

Upload de document (carrier portal)

Carrier Browser
    │
    ▼
NestJS POST /api/v1/csv-bookings/{id}/documents
    │ Validation: type (PDF/XLS/IMG), taille max 10 MB
    ▼
S3StorageAdapter.upload()
    │ AWS SDK v3, forcePathStyle: true
    ▼
Hetzner Object Storage
    │ Endpoint: https://fsn1.your-objectstorage.com
    └── Stocké: xpeditis-docs/{orgId}/{bookingId}/{filename}

Ports et protocoles

Ports externes (ouverts sur Hetzner Firewall)

Port Protocole Source Destination Usage
22 TCP Votre IP uniquement Tous nœuds SSH administration
80 TCP 0.0.0.0/0 LB Redirection HTTP → HTTPS
443 TCP 0.0.0.0/0 LB HTTPS + WebSocket
6443 TCP Votre IP + workers Control plane Kubernetes API

Ports internes (réseau privé 10.0.0.0/16 uniquement)

Port Protocole Usage
5432 TCP PostgreSQL (si self-hosted)
6379 TCP Redis (si self-hosted)
4000 TCP NestJS API (pod → pod)
3000 TCP Next.js (pod → pod)
10250 TCP kubelet API
2379-2380 TCP etcd (control plane)

Namespaces Kubernetes

cluster
├── xpeditis-prod          # Application principale
│   ├── Deployments: backend, frontend
│   ├── Services: backend-svc, frontend-svc
│   ├── ConfigMaps: backend-config, frontend-config
│   ├── Secrets: backend-secrets, frontend-secrets
│   ├── HPA: backend-hpa, frontend-hpa
│   └── Ingress: xpeditis-ingress
│
├── cert-manager           # Gestion certificats TLS
│   └── ClusterIssuer: letsencrypt-prod, letsencrypt-staging
│
├── monitoring             # Observabilité
│   ├── Prometheus
│   ├── Grafana
│   └── Loki
│
└── kube-system            # Système k3s
    ├── Traefik (Ingress Controller)
    ├── Hetzner Cloud Controller Manager
    └── Hetzner CSI Driver

Pourquoi k3s plutôt que k8s complet

Critère k3s (choisi) k8s complet
RAM control plane 512 MB 2-4 GB
CPU control plane 1 vCPU 2-4 vCPU → serveur plus cher
Temps install 5 min (hetzner-k3s) 30-60 min
Maintenance System Upgrade Controller inclus Manuelle
Compatibilité 100% compatible kubectl/helm
Traefik Inclus par défaut Installation séparée
Coût CX22 (€5.11/mois) comme control plane Minimum CX42 (€21.49)
Production Oui (utilisé par des milliers de startups) Oui

Stratégie de scaling

Horizontal Pod Autoscaler (HPA)

Métriques surveillées :
- CPU > 70% → Scale up
- CPU < 30% (5 min) → Scale down
- Mémoire > 80% → Scale up (custom metric)

Backend : min 2 → max 15 pods
Frontend : min 1 → max 8 pods

Cluster Autoscaler

Worker nodes : min 2 → max 8
Déclenché par : pods en état "Pending" (pas assez de ressources)
Délai scale-down : 10 min d'utilisation < 50%

Décisions d'architecture

Pourquoi Hetzner Object Storage plutôt que MinIO self-hosted

Le code utilise déjà AWS SDK v3 avec forcePathStyle: true et un endpoint configurable. Hetzner Object Storage est 100% compatible S3 → zéro modification de code, juste les variables d'environnement :

# Avant (MinIO local)
AWS_S3_ENDPOINT=http://localhost:9000

# Après (Hetzner Object Storage)
AWS_S3_ENDPOINT=https://fsn1.your-objectstorage.com
AWS_ACCESS_KEY_ID=<hetzner_access_key>
AWS_SECRET_ACCESS_KEY=<hetzner_secret_key>
AWS_REGION=eu-central-1
AWS_S3_BUCKET=xpeditis-prod

Pourquoi Neon.tech pour PostgreSQL (MVP)

  • PostgreSQL 15 managé, compatible TypeORM
  • Extensions uuid-ossp et pg_trgm disponibles (requis par Xpeditis)
  • Backups automatiques inclus
  • Connection pooling built-in (via PgBouncer)
  • Pas de gestion de HA à faire manuellement
  • Free tier pour le dev, $19/mois pour la prod
  • Migration vers self-hosted possible à tout moment

Pourquoi Cloudflare devant Hetzner LB

  • CDN mondial (cache des assets Next.js)
  • Protection DDoS free
  • WAF avec règles OWASP
  • DNS avec failover automatique
  • Certificats TLS optionnels (on peut laisser cert-manager gérer le TLS)
  • Cache des PDFs générés → économise les appels S3