Aller au contenu

Cloud Local

Objectif & portée

Horodatage

  • Tous les timestamps JSON sont en UTC avec suffixe Z. L'heure locale (UTC+1) est une préférence d'affichage UI.

  • Scénario cloud-local : gateway/orchestrateur/NLU/RAG en local ; données et observabilité en cloud

  • Couvre : API v1, webhooks entrants via tunnel, RAG relié à Vector DB managé
  • Fuseau : UTC+1 (Maroc)

Topologies supportées

  • T1 : Local app + RDS (Postgres), ElastiCache/Redis, S3, Pinecone/Qdrant Cloud
  • T2 : Local app + S3/Grafana Cloud seulement (DB/Vector locaux pour coût)
  • T3 : Local app + Secrets Manager/KMS (aucun secret en clair en .env)

Prérequis

⚠️ Sécurité dev vs prod
Les identifiants par défaut et endpoints sans TLS ne doivent pas être utilisés côté cloud. Utiliser un secrets manager (AWS Secrets Manager/KMS), activer TLS, restreindre Grafana/Prometheus par VPN/SSO, et ne pas exposer la console MinIO publiquement.

  • Docker ≥ 24, Compose v2, Git
  • Accès cloud : AWS (ou équivalent) + credentials (IAM user/role) limités
  • Outils tunnel webhooks : cloudflared ou ngrok
  • Optionnel : Terraform pour provisionner RDS/S3/Redis/Vector

Architecture cloud-local (aperçu)

Composant Local/Cloud Port/Endpoint Rôle
gateway Local http://localhost:8080 Ingress/API v1
orchestrateur Local http://localhost:8081 Coordination
nlu Local http://localhost:8082 LLM wrapper
rag Local http://localhost:8083 Retrieval
Postgres (RDS) Cloud postgres://…:5432 Métadonnées
Redis (ElastiCache) Cloud redis://…:6379 Cache/queues
Vector DB (Pinecone/Qdrant Cloud) Cloud https://… Embeddings/RAG
Objets (S3) Cloud s3://salambot-kb KB/documents
Observabilité Cloud Grafana/Loki/Tempo/Prom Dashboards/alerting

Diagramme d'architecture cloud hybride

flowchart TB
    subgraph "Local Environment"
        GATEWAY[🌐 Gateway :8080]
        ORCHESTRATOR[🎯 Orchestrateur :8081]
        NLU[🧠 NLU :8082]
        RAG[📚 RAG :8083]

        GATEWAY --> ORCHESTRATOR
        ORCHESTRATOR --> NLU
        ORCHESTRATOR --> RAG
    end

    subgraph "Cloud Services (AWS)"
        RDS[(🗄️ RDS Postgres)]
        ELASTICACHE[(⚡ ElastiCache Redis)]
        S3[📦 S3 Bucket]
        PINECONE[🔍 Pinecone Vector DB]

        subgraph "Observabilité Cloud"
            GRAFANA[📊 Grafana Cloud]
            LOKI[📝 Loki]
            TEMPO[🔗 Tempo]
            PROMETHEUS[📈 Prometheus]
        end
    end

    subgraph "External Services"
        OPENAI[🤖 OpenAI API]
        TUNNEL[🌍 Cloudflare Tunnel]
        WEBHOOKS[📞 Webhooks FB/WA]
    end

    %% Connexions Local → Cloud
    GATEWAY -.->|Métadonnées| RDS
    GATEWAY -.->|Cache/Sessions| ELASTICACHE
    ORCHESTRATOR -.->|Métadonnées| RDS
    RAG -.->|Documents KB| S3
    RAG -.->|Embeddings| PINECONE
    NLU -.->|LLM Calls| OPENAI

    %% Observabilité
    GATEWAY -.->|Traces/Logs| GRAFANA
    ORCHESTRATOR -.->|Traces/Logs| GRAFANA
    NLU -.->|Traces/Logs| GRAFANA
    RAG -.->|Traces/Logs| GRAFANA

    GRAFANA --> LOKI
    GRAFANA --> TEMPO
    GRAFANA --> PROMETHEUS

    %% Exposition publique
    TUNNEL -->|Public URL| GATEWAY
    WEBHOOKS -->|Callbacks| TUNNEL

    %% Flux de données
    USER[👤 Utilisateur] --> WEBHOOKS
    USER --> TUNNEL

    %% Styles
    style GATEWAY fill:#4caf50
    style ORCHESTRATOR fill:#2196f3
    style NLU fill:#ff9800
    style RAG fill:#9c27b0
    style RDS fill:#f44336
    style ELASTICACHE fill:#e91e63
    style S3 fill:#795548
    style PINECONE fill:#607d8b
    style GRAFANA fill:#3f51b5
    style OPENAI fill:#00bcd4
    style TUNNEL fill:#ffc107

    classDef local fill:#e8f5e8,stroke:#4caf50,stroke-width:2px
    classDef cloud fill:#e3f2fd,stroke:#2196f3,stroke-width:2px
    classDef external fill:#fff3e0,stroke:#ff9800,stroke-width:2px

    class GATEWAY,ORCHESTRATOR,NLU,RAG local
    class RDS,ELASTICACHE,S3,PINECONE,GRAFANA,LOKI,TEMPO,PROMETHEUS cloud
    class OPENAI,TUNNEL,WEBHOOKS external

.env.cloud-local (exemple)

TENANT=demo
ENV=cloud-local

# DB/Cache (Cloud)
POSTGRES_URL=postgres://user:pass@rds-endpoint:5432/salambot
REDIS_URL=redis://elasticache-endpoint:6379

# Vector DB
PINECONE_API_KEY=pc-...
PINECONE_INDEX=salambot-kb

# Objets
AWS_REGION=eu-west-1
S3_BUCKET=salambot-kb

# OTel → Grafana Cloud (ou votre stack)
OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp-gw:4318
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
OTEL_SERVICE_NAME=salambot-local

# Webhooks (tunnel public)
PUBLIC_BASE_URL=https://<subdomain>.trycloudflare.com

# Auth
OPENAI_API_KEY=sk-...
JWT_AUDIENCE=gateway
JWT_ISSUER=local-idp

Note : Ne pas committer .env ; privilégier export via gestionnaire de secrets cloud.

docker-compose.override.yml (cloud-local)

services:
  gateway:
    environment:
      - POSTGRES_URL=${POSTGRES_URL}
      - REDIS_URL=${REDIS_URL}
      - PUBLIC_BASE_URL=${PUBLIC_BASE_URL}
      - OTEL_EXPORTER_OTLP_ENDPOINT=${OTEL_EXPORTER_OTLP_ENDPOINT}
      - OTEL_EXPORTER_OTLP_PROTOCOL=${OTEL_EXPORTER_OTLP_PROTOCOL}

  orchestrateur:
    environment:
      - POSTGRES_URL=${POSTGRES_URL}
      - REDIS_URL=${REDIS_URL}
      - PINECONE_API_KEY=${PINECONE_API_KEY}
      - PINECONE_INDEX=${PINECONE_INDEX}
      - AWS_REGION=${AWS_REGION}
      - S3_BUCKET=${S3_BUCKET}

  rag:
    environment:
      - PINECONE_API_KEY=${PINECONE_API_KEY}
      - PINECONE_INDEX=${PINECONE_INDEX}
      - AWS_REGION=${AWS_REGION}
      - S3_BUCKET=${S3_BUCKET}

  nlu:
    environment:
      - OPENAI_API_KEY=${OPENAI_API_KEY}

Mise en route

cp .env.cloud-local.example .env
cp docker-compose.override.cloud-local.yml docker-compose.override.yml
docker compose up -d
curl -f http://localhost:8080/health

Webhooks (exposition publique)

Utilisez un tunnel (cloudflared/ngrok) pour exposer la gateway, puis suivez API/reference.mdWebhooks pour l'URL de callback, la signature HMAC et l'idempotence.

Seed & données

  • Uploader documents KB vers s3://${S3_BUCKET} (préfixe par tenant)
  • Indexer dans Vector DB via job seed_kb (utilise Pinecone)
docker compose exec orchestrateur python -m salambot.jobs.seed_kb \
  --tenant demo --incremental --vector pinecone

Tests fumée

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

# Génération (canonique)
curl -X POST http://localhost:8080/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":[]}
  }'

# Recherche 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}'

Note API : la spécification Webhooks/HMAC est centralisée dans API/reference.md#webhooks.

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

# Test génération (avec auth si activée)
curl -X POST http://localhost:8080/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
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

Connectivité cloud

  • RDS/ElastiCache : vérifier security groups (port 5432/6379 depuis votre IP)
  • S3 : valider IAM permissions (s3:GetObject, s3:PutObject sur bucket)
  • Pinecone : tester API key via curl -H "Api-Key: ${PINECONE_API_KEY}" https://api.pinecone.io/indexes

Tunnel webhooks

  • Cloudflare : cloudflared tunnel login puis créer tunnel persistant
  • ngrok : compte Pro requis pour domaines fixes
  • Firewall : autoriser trafic entrant sur port 8080

Observabilité

  • Grafana Cloud : vérifier endpoint OTLP et API key
  • Logs locaux : docker compose logs -f gateway orchestrateur
  • Métriques : http://localhost:8080/metrics (si activé)

Performance

  • Latence Vector DB : Pinecone EU-West vs US-East selon localisation
  • RDS : instance type et IOPS provisionnés
  • Cache Redis : TTL et éviction policy selon usage

Sécurité & conformité

  • Secrets : utiliser AWS Secrets Manager ou équivalent
  • Réseau : VPC peering ou VPN pour isolation
  • Audit : CloudTrail activé sur ressources AWS
  • PII : chiffrement en transit (TLS) et au repos (RDS/S3)

Coûts & optimisation

  • RDS : Aurora Serverless v2 pour charges variables
  • ElastiCache : instances t4g.micro pour dev/test
  • S3 : Intelligent Tiering pour documents KB
  • Pinecone : index p1.x1 suffisant pour <100k vecteurs

Observabilité (cloud)

  • Exporter traces/metrics via OTLP HTTP 4318 vers votre gateway.
  • Logs applicatifs → Loki/ELK (via agent local ou collector sidecar).
  • Dashboards: latence E2E/TTFB, RAG, erreurs ; alertes burn-rate SLO.

Tests de validation

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' \
  -H "Authorization: Bearer $TOKEN" \
  -H "X-SalamBot-Tenant: $TENANT" \
  -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' \
  -H "Authorization: Bearer $TOKEN" \
  -H "X-SalamBot-Tenant: $TENANT" \
  -d '{"query":"test","tenant":"demo","top_k":3}'

Sécurité cloud

  • Secrets via AWS Secrets Manager/KMS ; rotation ≤90j.
  • IAM: rôles à privilège minimal pour S3/RDS/Vector.
  • Aucun PII dans logs/labels ; voir Security/pii.md.
  • Audit: corrélation trace_id/correlation_id ; voir Security/audit.md.

Coûts & quotas (guidelines)

  • Limiter top_k RAG et taille des documents en dev.
  • Nettoyer buckets S3 de test et indexes Pinecone inactifs.
  • Arrêter RDS/Redis si offres stop/start disponibles.

Dépannage cloud

  • ECONNREFUSED vers RDS/Redis: vérifier VPC/SG/ACL et allowlists IP.
  • 403 S3: vérifier policy bucket et credentials.
  • Traces absentes: protocole OTLP (http/protobuf) et endpoint 4318.
  • Webhooks invalides: signature HMAC et dérive temporelle ±5 min.

Nettoyage

docker compose down
# Optionnel: purge images locales
docker image prune

Références croisées