314 lines
7.4 KiB
Markdown
314 lines
7.4 KiB
Markdown
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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 :
|
|
|
|
```typescript
|
|
// 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
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 :
|
|
|
|
```bash
|
|
# 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)
|