# 15 — Opérations, Scaling et Troubleshooting

Référence quotidienne pour gérer le cluster en production.

---

## Commandes kubectl essentielles

### Vue d'ensemble rapide

```bash
# État du cluster
kubectl get nodes
kubectl get pods -n xpeditis-prod
kubectl get pods -n xpeditis-prod -o wide     # + infos sur les nœuds

# Ressources consommées
kubectl top nodes
kubectl top pods -n xpeditis-prod --sort-by=cpu

# Événements récents (erreurs, warnings)
kubectl get events -n xpeditis-prod --sort-by='.lastTimestamp' | tail -30
kubectl get events -n xpeditis-prod -w           # En temps réel

# État des déploiements
kubectl get deployments -n xpeditis-prod
kubectl get hpa -n xpeditis-prod
kubectl get pvc -n xpeditis-prod
```

### Logs

```bash
# Logs backend (tous les pods)
kubectl logs -l app=xpeditis-backend -n xpeditis-prod --since=1h

# Logs frontend
kubectl logs -l app=xpeditis-frontend -n xpeditis-prod --since=1h

# Logs en temps réel (un pod spécifique)
kubectl logs -f pod/xpeditis-backend-5b8d6c7f9-xxxxx -n xpeditis-prod

# Logs multi-pods en temps réel (avec stern)
stern xpeditis-backend -n xpeditis-prod
stern xpeditis -n xpeditis-prod    # Backend + frontend

# Filtrer les erreurs
kubectl logs -l app=xpeditis-backend -n xpeditis-prod --since=1h | grep -E "ERROR|error|Error"

# Logs des dernières 100 lignes d'un pod crashé
kubectl logs --previous pod/xpeditis-backend-xxx -n xpeditis-prod | tail -100
```

### Exécution dans un pod

```bash
# Shell interactif dans un pod backend
kubectl exec -it deployment/xpeditis-backend -n xpeditis-prod -- /bin/sh

# Commande unique
kubectl exec deployment/xpeditis-backend -n xpeditis-prod -- \
  node -e "console.log(process.env.NODE_ENV)"

# Vérifier la connectivité DB depuis un pod
kubectl exec deployment/xpeditis-backend -n xpeditis-prod -- \
  nc -zv 10.0.1.100 5432
```

---

## Déploiements

### Déploiement d'une nouvelle version

```bash
# Via CI/CD (automatique sur push main) — voir doc 11

# Manuel : mettre à jour l'image
IMAGE_TAG="sha-$(git rev-parse --short HEAD)"

kubectl set image deployment/xpeditis-backend \
  backend=ghcr.io/<ORG>/xpeditis-backend:${IMAGE_TAG} \
  -n xpeditis-prod

# Suivre le déploiement
kubectl rollout status deployment/xpeditis-backend -n xpeditis-prod --timeout=300s

# Vérifier la version déployée
kubectl get deployment xpeditis-backend -n xpeditis-prod \
  -o jsonpath='{.spec.template.spec.containers[0].image}'
```

### Rollback

```bash
# Rollback vers la version précédente
kubectl rollout undo deployment/xpeditis-backend -n xpeditis-prod

# Rollback vers une version spécifique
kubectl rollout history deployment/xpeditis-backend -n xpeditis-prod
# REVISION  CHANGE-CAUSE
# 1         Initial deployment
# 2         sha-abc1234
# 3         sha-def5678  ← actuelle

kubectl rollout undo deployment/xpeditis-backend \
  --to-revision=2 \
  -n xpeditis-prod

# Vérifier
kubectl rollout status deployment/xpeditis-backend -n xpeditis-prod
```

### Redémarrage forcé (sans changer l'image)

```bash
# Utile après modification des secrets ou configmaps
kubectl rollout restart deployment/xpeditis-backend -n xpeditis-prod
kubectl rollout restart deployment/xpeditis-frontend -n xpeditis-prod

# Ou redémarrer un pod spécifique (K8s en recrée un nouveau)
kubectl delete pod xpeditis-backend-5b8d6c7f9-xxxxx -n xpeditis-prod
```

---

## Scaling manuel

```bash
# Scale horizontal (nombre de pods)
kubectl scale deployment xpeditis-backend \
  --replicas=5 \
  -n xpeditis-prod

# Scale horizontal frontend
kubectl scale deployment xpeditis-frontend \
  --replicas=3 \
  -n xpeditis-prod

# Désactiver temporairement le HPA (maintenance)
kubectl patch hpa backend-hpa -n xpeditis-prod \
  -p '{"spec":{"minReplicas":0,"maxReplicas":0}}'

# Réactiver le HPA
kubectl patch hpa backend-hpa -n xpeditis-prod \
  -p '{"spec":{"minReplicas":2,"maxReplicas":15}}'
```

---

## Gestion des nœuds

### Maintenance d'un nœud (drain)

```bash
# 1. Mettre le nœud en maintenance (draine les pods, bloque les nouveaux)
kubectl cordon xpeditis-prod-cx32-worker-1
kubectl drain xpeditis-prod-cx32-worker-1 \
  --ignore-daemonsets \
  --delete-emptydir-data \
  --grace-period=30

# 2. Effectuer la maintenance (mise à jour OS, etc.)
ssh -i ~/.ssh/xpeditis_hetzner root@<NODE_IP>
apt-get update && apt-get upgrade -y
reboot

# 3. Remettre le nœud en service
kubectl uncordon xpeditis-prod-cx32-worker-1

# Vérifier que les pods reviennent
kubectl get pods -n xpeditis-prod -o wide | grep worker-1
```

### Ajouter un nœud worker

```bash
# Méthode 1 : Via hetzner-k3s (recommandé)
# Modifier cluster.yaml → instance_count: 3 (ou plus)
hetzner-k3s apply --config ~/.xpeditis/cluster.yaml

# Méthode 2 : Via Cluster Autoscaler (automatique)
# Le CA crée des nœuds quand des pods sont en état "Pending"
# Pour forcer : déployer une charge
kubectl scale deployment xpeditis-backend --replicas=20 -n xpeditis-prod
# Le CA va créer des nœuds automatiquement
# Remettre en place après test
kubectl scale deployment xpeditis-backend --replicas=2 -n xpeditis-prod
```

---

## Mise à jour de k3s

Le System Upgrade Controller gère les upgrades automatiquement. Pour une mise à jour manuelle :

```bash
# Vérifier la version actuelle
kubectl get nodes -o jsonpath='{.items[0].status.nodeInfo.kubeletVersion}'
# v1.30.4+k3s1

# Créer un Plan de mise à jour
cat > /tmp/k3s-upgrade.yaml << 'EOF'
apiVersion: upgrade.cattle.io/v1
kind: Plan
metadata:
  name: k3s-server
  namespace: system-upgrade
spec:
  concurrency: 1      # Un nœud à la fois
  cordon: true
  serviceAccountName: system-upgrade
  version: v1.31.0+k3s1   # Nouvelle version
  upgrade:
    image: rancher/k3s-upgrade
  channel: https://update.k3s.io/v1-release/channels/stable
  nodeSelector:
    matchExpressions:
    - {key: node-role.kubernetes.io/control-plane, operator: Exists}
---
apiVersion: upgrade.cattle.io/v1
kind: Plan
metadata:
  name: k3s-agent
  namespace: system-upgrade
spec:
  concurrency: 1
  cordon: true
  serviceAccountName: system-upgrade
  version: v1.31.0+k3s1
  prepare:
    image: rancher/k3s-upgrade
    args: ["prepare", "k3s-server"]
  upgrade:
    image: rancher/k3s-upgrade
  channel: https://update.k3s.io/v1-release/channels/stable
  nodeSelector:
    matchExpressions:
    - {key: node-role.kubernetes.io/control-plane, operator: DoesNotExist}
EOF

kubectl apply -f /tmp/k3s-upgrade.yaml

# Suivre la progression
kubectl get plans -n system-upgrade
kubectl get jobs -n system-upgrade
```

---

## Troubleshooting — Problèmes courants

### Pod en CrashLoopBackOff

```bash
# 1. Voir les logs du crash
kubectl logs pod/xpeditis-backend-xxx -n xpeditis-prod --previous

# 2. Décrire le pod
kubectl describe pod xpeditis-backend-xxx -n xpeditis-prod
# Chercher : "Error", "OOMKilled", "Exit Code"

# Causes fréquentes :
# - OOMKilled (Exit 137) → Augmenter limits.memory
# - Exit 1 → Erreur applicative (DB unreachable, env var manquante)
# - Exit 126 → Problème de permissions sur le fichier d'entrée

# 3. Si env var manquante
kubectl exec deployment/xpeditis-backend -n xpeditis-prod -- env | sort | grep -E "DB|REDIS|JWT"
```

### Pod en Pending (pas démarré)

```bash
# Voir pourquoi le pod ne démarre pas
kubectl describe pod xpeditis-backend-xxx -n xpeditis-prod | grep -A 20 Events

# Causes fréquentes :
# "Insufficient cpu/memory" → Pas assez de ressources sur les nœuds → Scale up
# "0/2 nodes are available" → Vérifier les taints/tolerations
# "did not trigger scale-up" → Cluster Autoscaler peut-être désactivé

# Vérifier le Cluster Autoscaler
kubectl logs -n kube-system deployment/cluster-autoscaler | tail -30
```

### L'API backend retourne des 500

```bash
# 1. Vérifier les logs récents
kubectl logs -l app=xpeditis-backend -n xpeditis-prod --since=15m | grep -E "Error|error|500"

# 2. Tester le health check directement
kubectl exec deployment/xpeditis-backend -n xpeditis-prod -- \
  wget -qO- http://localhost:4000/api/v1/health | jq .

# 3. Tester la connexion DB
kubectl exec deployment/xpeditis-backend -n xpeditis-prod -- \
  node -e "
    const { Client } = require('pg');
    const c = new Client({connectionString: process.env.DATABASE_URL || 'postgres://'+process.env.DATABASE_USER+':'+process.env.DATABASE_PASSWORD+'@'+process.env.DATABASE_HOST+':'+process.env.DATABASE_PORT+'/'+process.env.DATABASE_NAME});
    c.connect().then(() => { console.log('DB OK'); c.end(); }).catch(e => { console.error('DB Error:', e.message); });
  "

# 4. Tester la connexion Redis
kubectl exec deployment/xpeditis-backend -n xpeditis-prod -- \
  node -e "
    const Redis = require('ioredis');
    const r = new Redis({host:process.env.REDIS_HOST,port:process.env.REDIS_PORT,password:process.env.REDIS_PASSWORD});
    r.ping().then(res => { console.log('Redis OK:', res); r.quit(); }).catch(e => { console.error('Redis Error:', e.message); });
  "
```

### TLS ne fonctionne pas

```bash
# Vérifier cert-manager
kubectl get certificates -n xpeditis-prod
kubectl describe certificate xpeditis-tls-prod -n xpeditis-prod

# Voir les challenges ACME
kubectl get challenges -n xpeditis-prod

# Si challenge bloqué : vérifier que l'IP du LB est dans le DNS Cloudflare
dig +short api.xpeditis.com
# Doit retourner l'IP du Hetzner LB

# Forcer le renouvellement du certificat
kubectl delete certificate xpeditis-tls-prod -n xpeditis-prod
kubectl apply -f k8s/07-ingress.yaml    # Le certificat sera recréé automatiquement
```

### WebSocket se déconnecte fréquemment

```bash
# 1. Vérifier les sticky sessions dans Traefik
kubectl logs -l app.kubernetes.io/name=traefik -n kube-system | grep -i sticky

# 2. Vérifier Cloudflare WebSocket est activé
# Cloudflare → Votre domaine → Network → WebSockets → ON

# 3. Vérifier le timeout WebSocket
# Cloudflare → Rules → Configuration Rules
# Créer une règle pour api.xpeditis.com/* → WebSocket timeout : 300s

# 4. Vérifier les logs Socket.IO
kubectl logs -l app=xpeditis-backend -n xpeditis-prod | grep -i socket
```

---

## Mise en maintenance planifiée

```bash
#!/bin/bash
# scripts/maintenance-mode.sh

echo "🔧 Activation du mode maintenance"

# 1. Mettre à jour le ConfigMap pour afficher une page de maintenance
kubectl patch configmap frontend-config -n xpeditis-prod \
  --type='json' \
  -p='[{"op":"add","path":"/data/MAINTENANCE_MODE","value":"true"}]'

# 2. Redémarrer le frontend
kubectl rollout restart deployment/xpeditis-frontend -n xpeditis-prod
kubectl rollout status deployment/xpeditis-frontend -n xpeditis-prod

# 3. Prévenir l'équipe
echo "✅ Mode maintenance activé. L'app affiche une page de maintenance."
echo "Pour désactiver : kubectl patch configmap frontend-config -n xpeditis-prod --type='json' -p='[{\"op\":\"remove\",\"path\":\"/data/MAINTENANCE_MODE\"}]'"
```

---

## Surveillance quotidienne (5 min/jour)

```bash
#!/bin/bash
# scripts/daily-check.sh
echo "=== Rapport Quotidien Xpeditis $(date) ==="

echo -e "\n--- CLUSTER ---"
kubectl get nodes
kubectl top nodes

echo -e "\n--- PODS ---"
kubectl get pods -n xpeditis-prod

echo -e "\n--- HPA ---"
kubectl get hpa -n xpeditis-prod

echo -e "\n--- EVENTS RÉCENTS (warnings) ---"
kubectl get events -n xpeditis-prod \
  --field-selector type=Warning \
  --sort-by='.lastTimestamp' | tail -10

echo -e "\n--- HEALTH CHECK ---"
curl -sf https://api.xpeditis.com/api/v1/health | jq '.status' || echo "❌ API DOWN"
curl -sf -o /dev/null -w "Frontend: %{http_code}\n" https://app.xpeditis.com/

echo -e "\n--- CERTIFICATS ---"
kubectl get certificate -n xpeditis-prod

echo -e "\n--- STOCKAGE ---"
kubectl get pvc -n xpeditis-prod

echo "=== Fin du rapport ==="
```

---

## Liens utiles

| Ressource | URL |
|---|---|
| Dashboard Grafana | https://monitoring.xpeditis.com |
| API Swagger | https://api.xpeditis.com/api/docs |
| Hetzner Console | https://console.hetzner.cloud |
| Cloudflare Dashboard | https://dash.cloudflare.com |
| Neon Dashboard | https://console.neon.tech |
| Upstash Console | https://console.upstash.com |
| GitHub Actions | https://github.com/<ORG>/xpeditis2.0/actions |
| GitHub GHCR | https://github.com/<ORG>/xpeditis2.0/pkgs/container |
