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

7.4 KiB

08 — Redis Setup

Redis est utilisé dans Xpeditis pour :

  1. Cache des rate quotes — clés rate:{origin}:{destination}:{containerType}, TTL 15 min
  2. Pub/sub WebSocket — Socket.IO multi-pods nécessite Redis pour broadcaster les notifications

Option A — Upstash (recommandé pour MVP)

Pourquoi Upstash

  • Redis serverless, pay-per-use ($0.2 per 100K commands)
  • Free tier : 10 000 commandes/jour, 256 MB (suffisant pour 100 users)
  • Compatible avec l'interface Redis standard (ioredis)
  • Support TLS natif
  • Régions EU disponibles (Frankfurt)
  • Pas de serveur à gérer

Setup Upstash

  1. Créez un compte sur https://upstash.com
  2. Create Database
    • Name: xpeditis-prod
    • Region: EU-WEST-1 (Frankfurt) ← le plus proche de Hetzner FSN1
    • Type: Regional (pas Global pour commencer)
    • Eviction: Allkeys-LRU (expire les clés les plus anciennes si mémoire pleine)
    • TLS: Enabled
  3. Copiez les credentials affichés

Variables d'environnement

# Upstash fournit une URL Redis complète
REDIS_HOST=your-redis.upstash.io
REDIS_PORT=6379
REDIS_PASSWORD=<upstash_token>
REDIS_DB=0

# OU avec URL (si le code le supporte)
REDIS_URL=redis://:password@your-redis.upstash.io:6379

Vérification de la connexion

# Test avec redis-cli
redis-cli -h your-redis.upstash.io -p 6379 -a <password> --tls ping
# PONG

# Test de set/get
redis-cli -h your-redis.upstash.io -p 6379 -a <password> --tls \
  SET test:connection "xpeditis-ok" EX 60
redis-cli -h your-redis.upstash.io -p 6379 -a <password> --tls \
  GET test:connection
# "xpeditis-ok"

Configuration dans l'app Xpeditis

Le code NestJS utilise ioredis. Vérifiez que TLS est activé dans la config cache :

Dans apps/backend/src/infrastructure/cache/cache.module.ts, assurez-vous que la config Redis accepte TLS :

// La config doit inclure TLS pour Upstash
const redisOptions = {
  host: configService.get('REDIS_HOST'),
  port: configService.get<number>('REDIS_PORT', 6379),
  password: configService.get('REDIS_PASSWORD'),
  db: configService.get<number>('REDIS_DB', 0),
  // TLS requis pour Upstash
  tls: configService.get('NODE_ENV') === 'production' ? {} : undefined,
};

Si la config actuelle ne supporte pas TLS, ajoutez la variable REDIS_TLS=true et adaptez le cache module en conséquence.


Option B — Redis self-hosted dans k3s

Quand choisir cette option

  • 1 000+ users (le free tier Upstash devient limité)
  • Besoin de Redis Cluster pour le WebSocket à grande échelle
  • Contrôle total des données

StatefulSet Redis dans Kubernetes

# Créer le namespace si pas encore fait
kubectl create namespace xpeditis-prod 2>/dev/null || true

# Créer le Secret Redis
cat > /tmp/redis-secret.yaml << 'EOF'
apiVersion: v1
kind: Secret
metadata:
  name: redis-secret
  namespace: xpeditis-prod
type: Opaque
stringData:
  REDIS_PASSWORD: "<MOT_DE_PASSE_FORT_REDIS>"
EOF
kubectl apply -f /tmp/redis-secret.yaml

# Créer la ConfigMap Redis
cat > /tmp/redis-config.yaml << 'EOF'
apiVersion: v1
kind: ConfigMap
metadata:
  name: redis-config
  namespace: xpeditis-prod
data:
  redis.conf: |
    # Sécurité
    requirepass <MOT_DE_PASSE_FORT_REDIS>
    protected-mode yes

    # Persistance (AOF pour durabilité)
    appendonly yes
    appendfsync everysec
    auto-aof-rewrite-percentage 100
    auto-aof-rewrite-min-size 64mb

    # Mémoire
    maxmemory 512mb
    maxmemory-policy allkeys-lru

    # Réseau
    bind 0.0.0.0
    tcp-backlog 511
    timeout 0
    tcp-keepalive 300

    # Logging
    loglevel notice

    # Performances
    lazyfree-lazy-eviction yes
    lazyfree-lazy-expire yes
EOF
kubectl apply -f /tmp/redis-config.yaml

# Créer le StatefulSet Redis
cat > /tmp/redis-statefulset.yaml << 'EOF'
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis
  namespace: xpeditis-prod
spec:
  serviceName: redis-headless
  replicas: 1
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
      - name: redis
        image: redis:7-alpine
        ports:
        - containerPort: 6379
          name: redis
        command:
        - redis-server
        - /etc/redis/redis.conf
        env:
        - name: REDIS_PASSWORD
          valueFrom:
            secretKeyRef:
              name: redis-secret
              key: REDIS_PASSWORD
        resources:
          requests:
            cpu: 100m
            memory: 256Mi
          limits:
            cpu: 500m
            memory: 512Mi
        volumeMounts:
        - name: redis-config-vol
          mountPath: /etc/redis
        - name: redis-data
          mountPath: /data
        readinessProbe:
          exec:
            command:
            - redis-cli
            - -a
            - $(REDIS_PASSWORD)
            - ping
          initialDelaySeconds: 10
          periodSeconds: 5
        livenessProbe:
          exec:
            command:
            - redis-cli
            - -a
            - $(REDIS_PASSWORD)
            - ping
          initialDelaySeconds: 30
          periodSeconds: 30
      volumes:
      - name: redis-config-vol
        configMap:
          name: redis-config
  volumeClaimTemplates:
  - metadata:
      name: redis-data
    spec:
      accessModes: ["ReadWriteOnce"]
      storageClassName: hcloud-volumes
      resources:
        requests:
          storage: 5Gi
---
apiVersion: v1
kind: Service
metadata:
  name: redis-headless
  namespace: xpeditis-prod
spec:
  clusterIP: None
  selector:
    app: redis
  ports:
  - port: 6379
    targetPort: 6379
---
apiVersion: v1
kind: Service
metadata:
  name: redis
  namespace: xpeditis-prod
spec:
  selector:
    app: redis
  ports:
  - port: 6379
    targetPort: 6379
  type: ClusterIP
EOF

kubectl apply -f /tmp/redis-statefulset.yaml

# Attendre que Redis soit prêt
kubectl rollout status statefulset/redis -n xpeditis-prod --timeout=120s

# Tester Redis
kubectl exec -it redis-0 -n xpeditis-prod -- redis-cli -a <password> ping
# PONG

Variables d'environnement pour Redis self-hosted

REDIS_HOST=redis.xpeditis-prod.svc.cluster.local
REDIS_PORT=6379
REDIS_PASSWORD=<MOT_DE_PASSE_FORT_REDIS>
REDIS_DB=0
# Pas de TLS (réseau privé interne k3s)

Vérification du cache Redis dans Xpeditis

Après déploiement de l'application, vérifiez que le cache fonctionne :

# Se connecter à Redis
kubectl exec -it redis-0 -n xpeditis-prod -- redis-cli -a <password>

# Après quelques rate searches depuis l'app :
KEYS rate:*
# 1) "rate:FRNCE:DEHAM:20ft"
# 2) "rate:FRNCE:NLRTM:20ft"
# ...

# Vérifier un TTL (doit être < 900 = 15 min)
TTL "rate:FRNCE:DEHAM:20ft"
# (integer) 647

# Stats globales
INFO stats
# keyspace_hits: 1234
# keyspace_misses: 156
# → Taux de hit = 1234/(1234+156) = 88% ✅

Comparaison des options

Critère Upstash (Option A) Self-hosted (Option B)
Coût 100 users $0 (free tier) ~€5/mois (stockage)
Coût 1 000 users ~$5-10/mois ~€5-10/mois
Setup 5 minutes 30 minutes
HA Automatique Non (StatefulSet 1 replica)
TLS Forcé Non (cluster interne)
Ops Aucun Monitoring mémoire
Latence ~5-10ms (Frankfurt) <1ms (cluster interne)

Recommandation :

  • MVP → Upstash free tier (zéro coût, zéro ops)
  • 1 000+ users → Self-hosted dans k3s (latence minimale, contrôle complet)