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.md → Webhooks 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 loginpuis 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¶
ECONNREFUSEDvers 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