Vue d'ensemble en couches
Une architecture LLM souveraine on-premise se décompose en six couches fonctionnelles distinctes, chacune avec ses responsabilités et ses interdépendances. Le schéma ci-dessous représente l'architecture cible pour une organisation de 200 à 2000 collaborateurs.
┌─────────────────────────────────────────────────────────────────┐
│ COUCHE APPLICATIONS │
│ Chat UI (Open WebUI) │ Apps métier │ IDE plugins (Continue)│
└───────────────┬────────────────────────────────────────────────┘
│ HTTPS / WebSocket
┌───────────────▼─────────────────────────────────────────────────┐
│ COUCHE API GATEWAY │
│ Kong OSS ─ Rate limiting ─ JWT auth ─ Audit logs ─ Routing │
└───────────────┬─────────────────────────────────────────────────┘
│ HTTP/2
┌───────────────▼─────────────────────────────────────────────────┐
│ COUCHE ORCHESTRATION LLM │
│ LangChain / LlamaIndex ─ RAG pipeline ─ Agent frameworks │
└──────┬────────────────────────────────────┬───────────────────┘
│ OpenAI-compat API │ Vector search
┌──────▼───────────────┐ ┌───────────▼──────────────────┐
│ COUCHE SERVING LLM │ │ COUCHE DONNÉES VECTORIELLES │
│ vLLM 0.4 (primary) │ │ Milvus 2.4 / Qdrant 1.9 │
│ Ray Serve (scaling) │ │ MinIO (stockage objets) │
│ 2-4 nodes GPU │ │ PostgreSQL (métadonnées) │
└──────┬───────────────┘ └──────────────────────────────┘
│ CUDA / NVLink
┌──────▼───────────────────────────────────────────────────────────┐
│ COUCHE INFRASTRUCTURE GPU │
│ Nodes: 4× H100 80GB SXM5 │ NVMe NVLink switch │
│ CPU: 2× AMD EPYC 9654 │ RAM: 768 GB DDR5 ECC │
│ Réseau: 2× 100GbE RDMA │ Storage: 8× 3.84TB NVMe U.2 │
└──────────────────────────────────────────────────────────────────┘
│
┌──────▼───────────────────────────────────────────────────────────┐
│ COUCHE IAM & SÉCURITÉ │
│ Keycloak 24 ─ LDAP/AD sync ─ OAuth2/OIDC ─ MFA │
│ HashiCorp Vault ─ Secrets ─ PKI interne ─ Encryption at rest │
└──────────────────────────────────────────────────────────────────┘
Choix GPU : H100 vs A100 vs L40S
Le GPU est le composant le plus structurant de l'architecture. Son choix conditionne la taille des modèles supportés, les performances d'inférence et le budget sur 3-5 ans. Voici une analyse comparative des trois options principales disponibles en France en 2026.
| GPU | VRAM | BF16 TFLOPS | Bande passante mémoire | Prix achat (€) | Dispo France |
|---|---|---|---|---|---|
| H100 SXM5 | 80 GB HBM3 | 1 979 | 3.35 TB/s | ~28 000–35 000 | Via OVHcloud, Scaleway |
| H100 PCIe | 80 GB HBM2e | 1 513 | 2.0 TB/s | ~22 000–28 000 | Bonne disponibilité |
| A100 SXM4 | 80 GB HBM2e | 312 | 2.0 TB/s | ~14 000–18 000 | Marché secondaire |
| A100 PCIe | 40 GB HBM2e | 312 | 1.55 TB/s | ~8 000–12 000 | Bonne disponibilité |
| L40S | 48 GB GDDR6 | 366 | 864 GB/s | ~9 000–12 000 | Très bonne dispo |
Ce qu'il faut retenir
- H100 SXM5 : meilleur rapport performance/VRAM, indispensable pour les modèles 70B en FP16. Privilégier pour la production haute charge.
- A100 80 GB : excellent choix secondaire pour modèles 32B (ELODIE, Mistral), coût d'achat 35-40% inférieur au H100.
- L40S : GPU « inference-optimisé » GDDR6, idéal pour les modèles quantisés INT8 ≤ 32B. Meilleur TCO pour usage modéré.
- Règle de sizing : un modèle 32B en BF16 requiert ~64 GB VRAM ; en INT8, ~32 GB ; en INT4, ~16 GB.
Pour un déploiement du modèle ELODIE 32B ou KEVINA 32B en production avec 100 utilisateurs concurrents, 2× A100 80 GB (ou 2× H100 PCIe) constituent le minimum viable. Pour 500 utilisateurs, il faut passer à 4× H100 avec Ray Serve en mode distribué.
Calcul VRAM par modèle
La formule standard pour estimer la VRAM nécessaire :
# VRAM estimation formula
# params_billions: nombre de paramètres en milliards
# precision: 2 (BF16/FP16), 1 (INT8), 0.5 (INT4)
# kv_cache_factor: overhead KV cache (typiquement 1.2)
def estimate_vram_gb(params_billions, precision_bytes=2, kv_cache_factor=1.2):
model_size_gb = params_billions * precision_bytes
total_vram = model_size_gb * kv_cache_factor
return round(total_vram, 1)
# Exemples
print(f"ELODIE 32B BF16: {estimate_vram_gb(32, 2)} GB") # 76.8 GB → 2× A100 40GB
print(f"ELODIE 32B INT8: {estimate_vram_gb(32, 1)} GB") # 38.4 GB → 1× A100 40GB
print(f"ELODIE 32B INT4: {estimate_vram_gb(32, 0.5)} GB") # 19.2 GB → 1× L40S
print(f"Llama 3.3 70B BF16: {estimate_vram_gb(70, 2)} GB") # 168 GB → 3× H100 80GB
print(f"Llama 3.3 70B INT8: {estimate_vram_gb(70, 1)} GB") # 84 GB → 2× H100 80GB
Stack logicielle complète
Chaque couche logicielle de l'architecture correspond à un composant open source éprouvé en production. Voici le détail complet avec les versions recommandées en 2026.
Serving LLM : vLLM 0.4+
vLLM est le moteur d'inférence de référence pour les LLM en production. Sa fonctionnalité clé, le PagedAttention, permet de gérer la mémoire KV cache de manière dynamique — similaire à la pagination mémoire d'un OS — éliminant la fragmentation et multipliant le débit par 2 à 4× comparé à Hugging Face Transformers naïf.
# Installation vLLM 0.4.x sur Ubuntu 22.04 + CUDA 12.3
pip install vllm==0.4.3
# Lancement serving ELODIE 32B en BF16 sur 2 GPU
python -m vllm.entrypoints.openai.api_server \
--model /models/elodie-32b \
--tensor-parallel-size 2 \
--dtype bfloat16 \
--max-model-len 8192 \
--max-num-seqs 256 \
--host 0.0.0.0 \
--port 8000 \
--api-key "${VLLM_API_KEY}" \
--enable-prefix-caching \
--gpu-memory-utilization 0.90
Scaling horizontal : Ray Serve
Ray Serve permet de distribuer la charge sur plusieurs nœuds GPU et d'implémenter des stratégies de routing avancées (round-robin, least-loaded, affinity). Il s'intègre nativement avec vLLM depuis la version 0.4.
# ray_serve_llm.py — déploiement Ray Serve + vLLM
import ray
from ray import serve
from vllm import AsyncLLMEngine, SamplingParams
from vllm.engine.arg_utils import AsyncEngineArgs
@serve.deployment(
num_replicas=2,
ray_actor_options={"num_gpus": 2},
max_concurrent_queries=50
)
class ELODIEDeployment:
def __init__(self):
engine_args = AsyncEngineArgs(
model="/models/elodie-32b",
tensor_parallel_size=2,
dtype="bfloat16",
max_model_len=8192,
gpu_memory_utilization=0.90,
enable_prefix_caching=True
)
self.engine = AsyncLLMEngine.from_engine_args(engine_args)
async def __call__(self, request):
data = await request.json()
prompt = data["prompt"]
sampling_params = SamplingParams(
temperature=data.get("temperature", 0.7),
max_tokens=data.get("max_tokens", 2048),
top_p=data.get("top_p", 0.9)
)
results = []
async for output in self.engine.generate(prompt, sampling_params, request_id="req"):
results.append(output)
return {"text": results[-1].outputs[0].text}
ray.init(address="auto")
serve.start(http_options={"host": "0.0.0.0", "port": 8001})
handle = serve.run(ELODIEDeployment.bind())
VectorDB : Milvus 2.4 vs Qdrant 1.9
| Critère | Milvus 2.4 | Qdrant 1.9 | PGVector 0.7 |
|---|---|---|---|
| Architecture | Distribué (etcd + MinIO) | Standalone ou cluster | Extension PostgreSQL |
| Index supportés | HNSW, IVF_FLAT, DISKANN | HNSW, ScaNN | HNSW, IVF |
| Performance 1M vecteurs | 2-5 ms P99 | 1-3 ms P99 | 10-50 ms P99 |
| Filtrage métadonnées | Excellent | Excellent | Natif SQL |
| Scalabilité | Milliards de vecteurs | 100M+ vecteurs | 10M vecteurs max |
| Opérationnel | Complexe (Helm chart) | Simple (binaire Rust) | Très simple |
Stockage objets : MinIO
# docker-compose.yml — MinIO haute disponibilité (4 nœuds)
version: '3.8'
services:
minio1:
image: minio/minio:RELEASE.2024-03-15T01-07-19Z
command: server http://minio{1...4}/data{1...2} --console-address ":9001"
environment:
MINIO_ROOT_USER: "${MINIO_ACCESS_KEY}"
MINIO_ROOT_PASSWORD: "${MINIO_SECRET_KEY}"
MINIO_VOLUMES: "/data1 /data2"
volumes:
- /nvme/minio/data1:/data1
- /nvme/minio/data2:/data2
ports:
- "9000:9000"
- "9001:9001"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
interval: 30s
timeout: 20s
retries: 3
IAM : Keycloak 24
Keycloak gère l'authentification centralisée avec synchronisation Active Directory/LDAP, émission de tokens JWT, et support du protocole OIDC pour l'API Gateway.
# keycloak-realm-config.json — configuration realm LLM
{
"realm": "intelligence-privee",
"enabled": true,
"sslRequired": "external",
"clients": [
{
"clientId": "llm-api",
"protocol": "openid-connect",
"publicClient": false,
"authorizationServicesEnabled": true,
"directAccessGrantsEnabled": false,
"standardFlowEnabled": true
}
],
"roles": {
"realm": [
{"name": "llm-user", "description": "Accès standard LLM"},
{"name": "llm-power", "description": "Modèles avancés + contexte étendu"},
{"name": "llm-admin", "description": "Administration et monitoring"}
]
},
"ldapFederationProviders": [
{
"name": "AD-Sync",
"providerType": "ldap",
"connectionUrl": "ldaps://ad.entreprise.fr:636",
"syncPeriod": 3600
}
]
}
Réseau isolé et sécurité
L'isolation réseau est fondamentale pour une infrastructure LLM souveraine. L'architecture recommandée utilise un VLAN dédié pour les nœuds GPU, complètement isolé du réseau bureautique.
RÉSEAU BUREAUTIQUE (VLAN 100 — 10.10.0.0/24)
│
[Firewall/UTM]
│ DMZ
[Load Balancer] ──── VLAN 200 — 10.20.0.0/24
│ └── Kong API Gateway
│ └── Open WebUI
│
[API Gateway] ────── VLAN 300 — 10.30.0.0/24 (INTERNE SÉCURISÉ)
└── Ray Serve Cluster
└── vLLM nodes (GPU)
└── Keycloak
│
VLAN 400 — 10.40.0.0/24 (DONNÉES)
└── Milvus / Qdrant
└── MinIO
└── PostgreSQL
└── HashiCorp Vault
Isolation réseau obligatoire
Les nœuds GPU (VLAN 300) ne doivent jamais être directement accessibles depuis le réseau bureautique. Tout trafic transite par l'API Gateway authentifiée. Activez le chiffrement inter-nœuds avec WireGuard ou IPSec pour les communications Ray Serve distribué.
Chiffrement au repos
# Chiffrement LUKS pour les volumes NVMe de modèles
cryptsetup luksFormat --type luks2 --key-size 512 \
--hash sha512 --iter-time 5000 /dev/nvme0n1
# Montage automatique via clevis + TPM2
clevis luks bind -d /dev/nvme0n1 tpm2 '{"pcr_bank":"sha256","pcr_ids":"0,1,7"}'
# Vérification
clevis luks list -d /dev/nvme0n1
Haute disponibilité et backups
Une infrastructure LLM de production doit viser un SLA de 99.9% (8.7h de downtime/an maximum). Cela impose une architecture HA sur toutes les couches critiques.
# Architecture HA — composants redondés
API Gateway (Kong):
- Mode: active-active, 2 instances minimum
- Load balancer: HAProxy ou Keepalived VIP
- Config sync: Kong DB mode (PostgreSQL cluster)
vLLM / Ray Serve:
- Mode: multi-replica via Ray
- Failover: automatique (Ray détecte les nœuds down)
- Modèles stockés sur MinIO (partagé NFS-over-RDMA)
Keycloak:
- Mode: cluster Infinispan (cache distribué)
- DB: PostgreSQL en streaming replication
- Instances: 2 minimum, 3 recommandé
Milvus:
- Mode: cluster (etcd x3, MinIO HA, QueryNode x2)
- Backup: snapshot S3-compatible vers MinIO secondaire
MinIO:
- Mode: erasure coding (4 nœuds, parité 2+2)
- RPO: 0 (réplication synchrone)
- RTO: < 30 secondes (bascule automatique)
Stratégie de backup
# Backup quotidien des modèles et configurations
#!/bin/bash
BACKUP_DATE=$(date +%Y%m%d)
BACKUP_BUCKET="s3://backup-llm-$(hostname)"
# 1. Snapshot Milvus
milvus-backup create --name "backup-${BACKUP_DATE}" \
--config /etc/milvus/backup.yaml
# 2. Export config Keycloak
/opt/keycloak/bin/kc.sh export \
--dir /backup/keycloak/${BACKUP_DATE} \
--realm intelligence-privee
# 3. Sync vers MinIO secondaire
mc mirror --overwrite /backup/ \
minio-secondary/${BACKUP_BUCKET}/
# 4. Vérification intégrité
mc stat minio-secondary/${BACKUP_BUCKET}/keycloak/${BACKUP_DATE}/
echo "Backup ${BACKUP_DATE} completed" | \
curl -X POST "${ALERTMANAGER_URL}/api/v1/alerts" -d @-
Sizing : 100, 500 et 1000 utilisateurs concurrents
Le sizing d'une infrastructure LLM dépend de plusieurs variables : taille du modèle, longueur moyenne des contextes, temps de réponse attendu, et pic de charge simultanée. Voici trois configurations de référence validées en production.
| Paramètre | 100 utilisateurs | 500 utilisateurs | 1000 utilisateurs |
|---|---|---|---|
| GPU nodes | 1× serveur 2× A100 80GB | 2× serveurs 4× H100 80GB | 4× serveurs 4× H100 80GB |
| VRAM totale | 160 GB | 640 GB | 1 280 GB |
| CPU (total vCores) | 64 | 256 | 512 |
| RAM système | 512 GB | 2 TB | 4 TB |
| Stockage NVMe | 10 TB | 40 TB | 80 TB |
| Réseau inter-nœuds | 10 GbE | 100 GbE RDMA | 200 GbE InfiniBand |
| Modèle recommandé | ELODIE 32B INT8 | ELODIE 32B BF16 × 2 | KEVINA 32B BF16 × 4 |
| Débit tokens/s | ~500 tok/s | ~2 000 tok/s | ~4 000 tok/s |
| Latence P95 estimée | ~180 ms TTFT | ~150 ms TTFT | ~120 ms TTFT |
| Budget matériel (€) | 60 000–80 000 | 250 000–320 000 | 500 000–650 000 |
Attention au pic de charge
Les 100/500/1000 utilisateurs sont des utilisateurs concurrents (requêtes simultanées), pas des utilisateurs totaux. Pour une organisation de 1000 collaborateurs, le pic réel est généralement de 100-150 requêtes simultanées (taux de simultanéité de 10-15%). Sizez en conséquence pour éviter le sur-dimensionnement.
Configuration vLLM pour la densité de requêtes
# Configuration optimisée pour 500 utilisateurs concurrents
# Sur 4× H100 80GB avec ELODIE 32B BF16
python -m vllm.entrypoints.openai.api_server \
--model /models/elodie-32b \
--tensor-parallel-size 4 \
--pipeline-parallel-size 1 \
--dtype bfloat16 \
--max-model-len 16384 \
--max-num-seqs 512 \
--max-num-batched-tokens 32768 \
--enable-prefix-caching \
--enable-chunked-prefill \
--gpu-memory-utilization 0.92 \
--scheduler-delay-factor 0.1 \
--host 0.0.0.0 --port 8000
Ce qu'il faut retenir
- L'architecture en 6 couches (Applications → API GW → Orchestration → Serving → Infrastructure → IAM) est la référence pour toute plateforme LLM souveraine.
- Le H100 SXM5 est la référence performance, mais le L40S offre le meilleur TCO pour les modèles ≤ 32B quantisés.
- vLLM 0.4+ avec PagedAttention et continuous batching est incontournable : 2-4× plus efficient qu'une inférence naïve.
- Pour 100 utilisateurs concurrents avec ELODIE 32B : 2× A100 80GB suffisent en INT8 (budget ~70k€ matériel).
- L'isolation réseau par VLAN et le chiffrement LUKS des volumes NVMe sont des prérequis non-négociables pour la conformité RGPD.
Concevoir votre architecture LLM souveraine
Nos architectes techniques accompagnent les DSI dans la conception et le déploiement d'infrastructures LLM on-premise adaptées à vos contraintes réelles — datacenter existant, budget, exigences sectorielles et conformité RGPD.
Parler à un architecte →FAQ
Peut-on utiliser des GPU AMD RDNA3 à la place de NVIDIA ?
Techniquement oui : vLLM supporte ROCm 6.x depuis la version 0.3. En pratique, l'écosystème logiciel (CUDA, cuDNN, les bibliothèques d'optimisation comme FlashAttention-2, CUTLASS) reste très largement centré NVIDIA. Pour une production 2026, NVIDIA reste recommandé sauf contrainte spécifique d'approvisionnement. AMD MI300X (192 GB HBM3) est une alternative prometteuse pour les très grands modèles.
Combien de temps prend le chargement d'un modèle 32B en mémoire GPU ?
Avec un modèle stocké sur NVMe U.2 (lecture séquentielle ~6 GB/s) et 2× A100 reliés en NVLink, le chargement d'un modèle ELODIE 32B (environ 64 GB en BF16) prend 10 à 15 secondes. Depuis MinIO via réseau 100 GbE, comptez 30 à 60 secondes. Utilisez le préchargement au démarrage du pod Kubernetes pour éliminer ce délai des premiers utilisateurs.
vLLM supporte-t-il nativement l'API OpenAI ?
Oui. vLLM expose un endpoint /v1/chat/completions et /v1/completions compatibles avec l'API OpenAI. Vous pouvez pointer n'importe quel client OpenAI (Python SDK, LangChain, LlamaIndex, Open WebUI) vers votre instance vLLM en changeant simplement la variable OPENAI_BASE_URL et OPENAI_API_KEY. Zéro modification de code applicatif.
Comment gérer la reprise après un crash GPU (GPU OOM) ?
Configurez Kubernetes avec un liveness probe sur le port vLLM et un restartPolicy Always. En cas de GPU OOM (CUDA out of memory), vLLM renvoie une erreur 500 et redémarre automatiquement via le kubelet. Pour prévenir les OOM, ajustez --gpu-memory-utilization à 0.85-0.90 et activez --max-num-seqs pour limiter la concurrence. DCGM Exporter + Prometheus vous alertera avant saturation avec la métrique DCGM_FI_DEV_FB_USED.