Aller au contenu

On-Premises

Objectif & portée

Horodatage

  • Standard : ISO 8601 UTC (Z). L'UTC+1 figure uniquement dans l'UI si nécessaire.

  • Déployer SalamBot on-prem (datacenter privé ou edge) avec persistance et observabilité

  • Couvrir Docker Compose et Kubernetes, incluant réseau, stockage, sécurité et sauvegardes
  • Assurer compatibilité avec les runbooks et exigences SLO/SLA
  • Fuseau : UTC+1 (Maroc)

Architecture on-prem (aperçu)

Service Port Rôle
gateway 80/443 (8080 interne) Ingress/API v1
orchestrateur 8081 Coordination
nlu 8082 LLM wrapper
rag 8083 Retrieval
postgres 5432 Métadonnées
redis 6379 Cache/queues
qdrant 6333 Vector DB
minio 9000/9001 Objets (KB) / Console
prometheus 9090 Métriques
grafana 3001 Dashboards
otel-collector 4317/4318 OTLP gRPC/HTTP

Architecture réseau on-premise

Le diagramme suivant illustre la topologie réseau complète pour un déploiement on-premise :

graph TB
    subgraph "Zone DMZ (Front)"
        LB[Load Balancer<br/>80/443]
        FW[Firewall<br/>WAF]
    end

    subgraph "Zone Application"
        GW[Gateway<br/>:8080]
        ORC[Orchestrateur<br/>:8081]
        NLU[NLU<br/>:8082]
        RAG[RAG<br/>:8083]
    end

    subgraph "Zone Données"
        PG[(PostgreSQL<br/>:5432)]
        RD[(Redis<br/>:6379)]
        QD[(Qdrant<br/>:6333)]
        MN[(MinIO<br/>:9000/:9001)]
    end

    subgraph "Zone Observabilité"
        PR[Prometheus<br/>:9090]
        GR[Grafana<br/>:3001]
        OT[OTel Collector<br/>:4317/:4318]
    end

    subgraph "Réseau Externe"
        USR[Utilisateurs]
        WH[Webhooks<br/>WhatsApp/FB]
        API[APIs Externes<br/>OpenAI/Ollama]
    end

    %% Flux utilisateurs
    USR -->|HTTPS| LB
    WH -->|HTTPS| LB
    LB --> FW
    FW -->|Port 8080| GW

    %% Flux internes app
    GW --> ORC
    ORC --> NLU
    ORC --> RAG

    %% Accès données
    ORC --> PG
    ORC --> RD
    RAG --> QD
    RAG --> MN

    %% Observabilité
    GW --> OT
    ORC --> OT
    NLU --> OT
    RAG --> OT
    OT --> PR
    PR --> GR

    %% Accès externes
    NLU -.->|HTTPS| API

    %% Accès admin
    USR -.->|Port 3001<br/>Restreint| GR
    USR -.->|Port 9090<br/>Restreint| PR
    USR -.->|Port 9001<br/>Restreint| MN

    style LB fill:#e3f2fd
    style FW fill:#fff3e0
    style GW fill:#e8f5e8
    style PG fill:#fce4ec
    style RD fill:#fce4ec
    style QD fill:#fce4ec
    style MN fill:#fce4ec
    style PR fill:#f3e5f5
    style GR fill:#f3e5f5

Prérequis & sizing

⚠️ Sécurité prod on-prem
Interdire les credentials par défaut, imposer TLS 1.2+ (idéalement 1.3), restreindre Grafana/Prometheus/MinIO (réseau/SSO), stocker les secrets dans Vault/KMS, et auditer tout accès d'admin.

  • Hôte (min) : 4 vCPU, 16 Go RAM, SSD 100 Go ; prod : 8+ vCPU, 32+ Go, SSD NVMe
  • Réseau : 1 Gbps interne, sorties Internet optionnelles (MAJ/registries)
  • DNS interne + certificats TLS (AC interne ou ACME)
  • Comptes de service/accès root restreints ; NTP synchronisé

Réseau & sécurité

  • Ingress : Traefik ou Nginx ; TLS 1.2+ (préférer 1.3), HSTS, mTLS interne optionnel
  • Pare-feu : ouvrir 80/443 (ingress), 3001 (Grafana), 9090 (Prometheus, restreint), 9001 (MinIO console, restreint)
  • Zonage réseau : front (DMZ) ↔ app ↔ data ; pas d'accès direct aux DB depuis DMZ
  • Headers requis : Authorization, X-SalamBot-Tenant, X-Request-Id ; signatures webhooks (HMAC)

Stockage & persistance

  • PostgreSQL : volume dédié, WAL archiving optionnel ; sauvegardes quotidiennes
  • MinIO : buckets par tenant ; politiques IAM minimales ; versioning conseillé
  • Qdrant : snapshots activés ; plan de restauration testé
  • Backups offsite chiffrés (AES-256), clés KMS/Vault

Secrets & IAM

  • Stockage dans Vault/KMS on-prem ; pas de secrets en clair dans git/logs
  • Rotation ≤90j (JWT/webhooks/API keys) ; accès par rôles (RBAC+ABAC)
  • Comptes break-glass en coffre avec journalisation et expiration

Déploiement — Docker Compose

services:
  gateway:
    ports: ['8080:8080']
    env_file: [.env]
    depends_on: [orchestrateur]
  orchestrateur:
    depends_on: [postgres, redis]
  nlu:
    environment:
      - OPENAI_API_KEY=${OPENAI_API_KEY}
  rag:
    depends_on: [qdrant, minio]
  postgres:
    volumes: ['pgdata:/var/lib/postgresql/data']
  redis:
    command: ['redis-server', '--save', '', '--appendonly', 'no']
  qdrant:
    volumes: ['qdrant:/qdrant/storage']
  minio:
    environment:
      - MINIO_ROOT_USER=${MINIO_ROOT_USER}
      - MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD}
    command: ['server', '/data', '--console-address', ':9001']
  prometheus:
    ports: ['9090:9090']
  grafana:
    ports: ['3001:3000']
volumes:
  pgdata: {}
  qdrant: {}

Ajout du service OpenTelemetry Collector

services:
  otel-collector:
    image: otel/opentelemetry-collector:0.95.0
    command: ['--config=/etc/otel/config.yaml']
    volumes:
      - ./ops/otel/config.yaml:/etc/otel/config.yaml:ro
    ports:
      - '4317:4317'
      - '4318:4318'

Les variables OTEL_EXPORTER_OTLP_* des services pointent sur otel-collector:4318.

Déploiement — Kubernetes (on-prem)

  • CNI : Calico/Cilium ; StorageClass : CSI (NFS/ceph/RBD) avec RWO/RWX
  • IngressController : Traefik/Nginx ; cert-manager pour TLS
  • Namespace : salambot ; ressources avec requests/limits ; PodSecurity standards
apiVersion: apps/v1
kind: Deployment
metadata:
  name: gateway
  namespace: salambot
spec:
  replicas: 2
  selector: { matchLabels: { app: gateway } }
  template:
    metadata: { labels: { app: gateway } }
    spec:
      containers:
        - name: gateway
          image: salambot/gateway:latest
          ports: [{ containerPort: 8080 }]
          envFrom: [{ secretRef: { name: salambot-secrets } }]
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: gateway
  namespace: salambot
spec:
  tls: [{ hosts: [salambot.local], secretName: tls-salambot }]
  rules:
    - host: salambot.local
      http:
        paths:
          - path: /
            pathType: Prefix
            backend: { service: { name: gateway, port: { number: 8080 } } }

Service manquant pour l'Ingress

apiVersion: v1
kind: Service
metadata:
  name: gateway
  namespace: salambot
spec:
  selector:
    app: gateway
  ports:
    - name: http
      port: 8080
      targetPort: 8080

Observabilité on-prem

  • OTel Collector → Prometheus/Grafana/Loki/Tempo ; exporter OTLP (4317/4318)
  • Dashboards : latence E2E/TTFB, NLU, RAG, erreurs ; alertes burn-rate SLO
  • Journalisation : pas de PII ; corrélation trace_id/span_id/correlation_id

Configuration .env.on-prem

TENANT=demo
ENV=on-prem

# DB/Cache (Local)
POSTGRES_URL=postgres://salambot:${POSTGRES_PASSWORD}@postgres:5432/salambot
REDIS_URL=redis://redis:6379

# Vector DB
QDRANT_URL=http://qdrant:6333
QDRANT_API_KEY=${QDRANT_API_KEY}

# Objets (MinIO)
MINIO_ENDPOINT=http://minio:9000
MINIO_ROOT_USER=${MINIO_ROOT_USER}
MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD}
S3_BUCKET=salambot-kb

# OTel → Local stack
OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
OTEL_SERVICE_NAME=salambot-onprem

# Auth (RS256 recommandé)
JWT_ALG=RS256
JWT_PRIVATE_KEY_PATH=/secrets/jwt/private.pem
JWT_PUBLIC_KEY_PATH=/secrets/jwt/public.pem
JWT_AUDIENCE=gateway
JWT_ISSUER=onprem-idp

# LLM
OPENAI_API_KEY=${OPENAI_API_KEY}

# Webhooks
PUBLIC_BASE_URL=https://salambot.local
WEBHOOK_SECRET=${WEBHOOK_SECRET}

Mise en route

# Docker Compose
cp .env.on-prem.example .env
cp docker-compose.override.on-prem.yml docker-compose.override.yml
docker compose up -d

# Kubernetes
kubectl create namespace salambot
kubectl apply -f k8s/secrets.yaml -n salambot
kubectl apply -f k8s/ -n salambot

# Health check
curl -f http://localhost:8080/health

DNS local : ajoutez 127.0.0.1 salambot.local dans /etc/hosts si vous utilisez l'Ingress avec salambot.local.

Seed & données initiales

# Créer buckets MinIO
mc alias set local http://localhost:9000 ${MINIO_ROOT_USER} ${MINIO_ROOT_PASSWORD}
mc mb local/salambot-kb

# Uploader documents KB
mc cp --recursive ./kb-docs/ local/salambot-kb/demo/

# Indexer dans Qdrant
docker compose exec orchestrateur python -m salambot.jobs.seed_kb \
  --tenant demo --incremental --vector qdrant

Tests fumée on-prem

# Health check
curl -f http://localhost:8080/health

# Test génération (canonique)
curl -X POST http://salambot.local/v1/generate/reply \\
  -H "Content-Type: application/json" \\
  -H "Authorization: Bearer ${JWT_TOKEN}" \\
  -H "X-SalamBot-Tenant: demo" \\
  -d '{
  "schema_version":"1.0",
  "tenant":"demo",
  "channel":"webchat",
  "message_id":"msg_smoke",
  "correlation_id":"corr_smoke",
  "timestamp":"2025-08-14T10:00:00Z",
  "locale":"fr-MA",
  "data":{"prompt":"Bonjour SalamBot","context":[]}
}'

# Test RAG (canonique)
curl -X POST http://localhost:8080/v1/search/query \\
  -H "Content-Type: application/json" \\
  -H "Authorization: Bearer ${JWT_TOKEN}" \\
  -H "X-SalamBot-Tenant: demo" \\
  -d '{"query":"politique remboursement","tenant":"demo","top_k":3}'
# Health check
curl -f http://localhost:8080/health

# Test génération
curl -X POST http://salambot.local/v1/generate/reply \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ${JWT_TOKEN}" \
  -H "X-SalamBot-Tenant: demo" \
  -d '{
  "schema_version": "1.0",
  "tenant": "demo",
  "channel": "webchat",
  "message_id": "msg_smoke",
  "correlation_id": "corr_smoke",
  "timestamp": "2025-01-16T10:00:00Z",
  "locale": "fr-MA",
  "data": {"prompt": "Bonjour SalamBot", "context": []}
}'

# Test RAG
curl -X POST http://localhost:8080/v1/search/query \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ${JWT_TOKEN}" \
  -H "X-SalamBot-Tenant: demo" \
  -d '{"query": "politique remboursement", "tenant": "demo", "top_k": 3}'

Troubleshooting on-prem

Connectivité interne

  • PostgreSQL : vérifier docker compose logs postgres ou kubectl logs -l app=postgres
  • Redis : tester avec redis-cli -h localhost -p 6379 ping
  • Qdrant : curl http://localhost:6333/collections
  • MinIO : console web http://localhost:9001

Performance

  • Latence DB : optimiser index PostgreSQL, pool connections
  • Vector search : ajuster ef_construct et m dans Qdrant
  • Cache Redis : TTL et éviction policy selon usage
  • Stockage : IOPS SSD, monitoring I/O wait

Sécurité

  • Certificats : renouvellement automatique via cert-manager
  • Secrets : rotation via Vault/External Secrets Operator
  • Réseau : NetworkPolicies Kubernetes, iptables Docker
  • Audit : logs d'accès Traefik/Nginx, events Kubernetes

Sauvegardes & restauration

PostgreSQL

# Sauvegarde
docker compose exec postgres pg_dump -U salambot salambot > backup.sql

# Restauration
docker compose exec -T postgres psql -U salambot salambot < backup.sql

Qdrant

# Snapshot
curl -X POST http://localhost:6333/collections/salambot-kb/snapshots

# Télécharger
curl http://localhost:6333/collections/salambot-kb/snapshots/\
  snapshot-2025-01-16.snapshot \
  -o qdrant-backup.snapshot

MinIO

# Sync vers backup externe
mc mirror local/salambot-kb/ backup-site/salambot-kb/

Monitoring & alertes

Métriques clés

  • Latence : p95 < 2s (génération), p95 < 500ms (recherche)
  • Disponibilité : uptime > 99.5%
  • Erreurs : taux < 1%
  • Ressources : CPU < 80%, RAM < 85%, disque < 90%

Dashboards Grafana

  • SalamBot Overview : santé globale, SLI/SLO
  • Application : latence par endpoint, erreurs, throughput
  • Infrastructure : CPU/RAM/disque/réseau par service
  • Business : requêtes par tenant, top queries RAG

Mise à jour & maintenance

Rolling update (Kubernetes)

kubectl set image deployment/gateway \
  gateway=salambot/gateway:v1.2.0 -n salambot
kubectl rollout status deployment/gateway -n salambot

Blue-green (Docker Compose)

# Déployer nouvelle version
docker compose -f docker-compose.blue.yml up -d

# Basculer le trafic (load balancer)
# Arrêter ancienne version
docker compose -f docker-compose.green.yml down

Sécurité & conformité

  • Chiffrement : TLS 1.3 en transit, AES-256 au repos
  • Authentification : JWT + mTLS interne, rotation clés
  • Autorisation : RBAC Kubernetes, IAM MinIO
  • Audit : logs centralisés, corrélation trace_id
  • PII : anonymisation, pas de logs sensibles

Coûts & optimisation

  • Ressources : requests/limits Kubernetes, autoscaling HPA
  • Stockage : compression Qdrant, lifecycle MinIO
  • Réseau : cache Redis, CDN statiques
  • Observabilité : rétention logs/métriques, sampling traces

Seed & données

docker compose exec minio mc alias set local http://minio:9000 \
  "$MINIO_ROOT_USER" "$MINIO_ROOT_PASSWORD"
docker compose exec minio mc mb -p local/salambot-kb || true
docker compose exec orchestrateur python -m salambot.jobs.seed_kb --tenant demo --incremental

Tests fumée

TOKEN="dev-token" TENANT="demo"
# Health
curl -H "Authorization: Bearer $TOKEN" \
  -H "X-SalamBot-Tenant: $TENANT" http://localhost:8080/health
# Génération
curl -X POST http://localhost:8080/v1/generate/reply \
  -H 'Content-Type: application/json' -d '{
  "schema_version":"1.0","tenant":"demo","channel":"webchat",
  "message_id":"msg_smoke","correlation_id":"corr_smoke","timestamp":"2025-08-14T10:00:00Z","locale":"fr-MA",
  "data":{"prompt":"Bonjour","context":[]}}
'
# Recherche RAG
curl -X POST http://localhost:8080/v1/search/query \
  -H 'Content-Type: application/json' \
  -d '{"query":"test","tenant":"demo","top_k":3}'

Sauvegarde & restauration

  • PostgreSQL: pg_dump quotidien + rotation; restauration test mensuelle.
  • MinIO: mirror vers stockage WORM; versions/snapshots activés.
  • Qdrant: snapshots programmés; procédure de recover documentée.

Troubleshooting

  • Ports occupés: ajuster mapping; vérifier SELinux/AppArmor.
  • Lenteur RAG: vérifier Qdrant et index présents; réduire top_k.
  • TLS: vérifier chaîne cert + horloge NTP; refuser ciphers faibles.
  • Secrets: valider injection (Vault/Secrets K8s) et permissions.

Checklists go-live

  • Dashboards/alertes actifs; budgets SLO (voir Ops/slo-sla.md) configurés.
  • Backups vérifiés et restauration testée.
  • Comptes admin et accès break-glass contrôlés.
  • Runbooks d'incident imprimés/partagés (P0/P1/P2).

Références croisées