vLLM — inference haute performance
Quand et pourquoi remplacer Ollama par vLLM pour servir des LLM en production, avec PagedAttention, continuous batching et une API compatible OpenAI.
Table des matières
Pourquoi quitter Ollama
Ollama est un excellent point d'entrée pour évaluer un modèle, développer un prototype ou équiper un poste de travail individuel. Sa simplicité d'installation et sa gestion automatique des poids en font un outil de choix pour découvrir le fonctionnement d'un LLMLLMIAGrand modèle de langage (Large Language Model) entraîné sur d'énormes corpus pour prédire et générer du texte.Voir dans le glossaire local. Mais cette simplicité a un coût architectural : Ollama, construit sur llama.cpp, traite les requêtes de façon globalement séquentielle. Quand un deuxième utilisateur envoie une requête pendant qu'une génération est en cours, il attend — ou dégrade fortement la latence de tout le monde si le serveur force un batching naïf.
Ce comportement est acceptable pour un usage individuel. Il devient un problème dès que l'on parle d'un service partagé : un chatbot interne consulté par cinquante collaborateurs, une API de résumé de documents appelée par plusieurs applications métier, ou un assistant de support client avec des pics de charge. Le symptôme est toujours le même : le temps de réponse par utilisateur explose avec le nombre d'utilisateurs simultanés, alors que le GPUGPUIAProcesseur graphique parallélisant massivement les calculs matriciels ; indispensable à l'entraînement et à l'inférence des modèles de deep learning.Voir dans le glossaire, lui, n'est utilisé qu'à une fraction de sa capacité réelle.
Le critère de bascule n'est pas la taille du modèle ni la puissance du GPU, mais le nombre de requêtes concurrentes que le service doit absorber. En dessous de deux ou trois utilisateurs simultanés occasionnels, Ollama suffit largement. Au-delà d'un usage réellement partagé, vLLM devient nécessaire pour exploiter correctement le matériel.
vLLM est un moteur d'inference open source conçu dès l'origine pour le service à grande échelle. Il a été développé au Sky Computing Lab de l'université de Berkeley et est aujourd'hui l'un des moteurs les plus utilisés en production pour les modèles Llama, Mistral, Qwen ou Mixtral. Sa promesse : multiplier le débit par un facteur de 2 à 20 par rapport à une implémentation naïve, à matériel identique, tout en exposant une API compatible avec celle d'OpenAI.
PagedAttention : la mémoire comme goulot d'étranglement
Pour comprendre l'apport de vLLM, il faut d'abord comprendre pourquoi le service de LLMgrand modèle de langageIAModèle entraîné à prédire le token suivant d'une séquence de texte. Toutes ses capacités apparentes — résumer, traduire, coder — découlent de cette unique tâche.Voir dans le glossaire est un problème de mémoire avant d'être un problème de calcul.
À chaque étape de génération, un modèle transformeur doit consulter l'historique de tous les tokenstokenIAFragment de texte manipulé par un modèle de langage, généralement plus court qu'un mot — trois à quatre caractères en français. La tarification et la limite de contexte se comptent en tokens.Voir dans le glossaire déjà générés pour la requête en cours. Pour éviter de recalculer cet historique à chaque nouveau token, les moteurs d'inference conservent un cache KV (key-value cache) : les représentations internes (clés et valeurs de l'attentionattentionIAMécanisme par lequel un modèle pondère l'importance de chaque token du contexte lorsqu'il en traite un autre, quelle que soit la distance qui les sépare.Voir dans le glossaire) de tous les tokens précédents, stockées en mémoire GPU. Plus la séquence est longue, plus ce cache grossit, et plus il occupe de mémoire vive du GPU — au détriment du nombre de requêtes que l'on peut traiter en parallèle.
Les implémentations classiques allouent ce cache de façon contiguë, en réservant à l'avance un bloc de mémoire correspondant à la longueur maximale possible d'une séquence. Le problème : la plupart des requêtes ne consomment jamais cette longueur maximale, mais la mémoire réservée reste bloquée pendant toute la durée de la requête. Ce gaspillage, mesuré par les auteurs de vLLM, atteint couramment 60 à 80 % de la mémoire allouée au cache KV.
PagedAttention résout ce problème en s'inspirant directement de la pagination mémoire des systèmes d'exploitation. Plutôt que de réserver un bloc contigu par requête, le cache KV est découpé en petites pages de taille fixe, allouées à la demande au fur et à mesure que la séquence progresse, et potentiellement dispersées en mémoire physique. Un système d'indirection fait le lien entre la séquence logique et l'emplacement réel des pages.
Le bénéfice direct est double : moins de mémoire gaspillée par requête, donc davantage de requêtes actives simultanément sur le même GPU, et surtout la possibilité de partager les pages entre requêtes qui commencent par le même préfixe — un système promptpromptIAConsigne ou contexte fourni à un modèle de langage pour orienter sa réponse. La qualité du prompt conditionne souvent la qualité du résultat.Voir dans le glossaire commun à toute l'entreprise, par exemple, n'est stocké qu'une seule fois en mémoire.
Si votre application envoie systématiquement le même prompt système long à chaque requête (consignes de ton, contextefenêtre de contexteIAQuantité de texte qu'un modèle peut prendre en compte simultanément : question, documents fournis et historique. Au-delà, les éléments les plus anciens sortent du champ.Voir dans le glossaire métier, exemples few-shotFew-shotIATechnique de prompt où l'on fournit quelques exemples entrée-sortie pour guider le modèle sans réentraînement.Voir dans le glossaire), vLLM peut réutiliser le cache KV de ce préfixe entre requêtes. Le gain est particulièrement net avec des prompts système de plusieurs centaines de tokens.
Continuous batching : ne jamais laisser le GPU attendre
La seconde innovation de vLLM concerne l'ordonnancement des requêtes. Le batching classique, dit « statique », regroupe un lot fixe de requêtes, les traite ensemble jusqu'à ce que toutes soient terminées, puis ne démarre le lot suivant qu'une fois le précédent entièrement vidé. Problème : les requêtes d'un même lot ne se terminent presque jamais en même temps, puisque la longueur de génération varie d'une requête à l'autre. Le GPU reste donc sous-utilisé en attendant les requêtes les plus longues du lot.
Le continuous batching (aussi appelé batching en vol, ou in-flight batching) change de logique : dès qu'une requête se termine, sa place est immédiatement réattribuée à une nouvelle requête en attente, sans attendre la fin du lot entier. Le GPU traite ainsi un flux continu de requêtes à différents stades d'avancement, ce qui maximise l'utilisation du matériel à tout moment.
Combiné à PagedAttention, ce mécanisme permet à vLLM d'atteindre un débit nettement supérieur à celui des moteurs à batching statique, particulièrement quand le trafic est irrégulier — ce qui est le cas de la quasi-totalité des usages d'entreprise.
Une API compatible OpenAI
Atout pratique majeur : vLLM expose un serveur HTTP dont l'API reproduit fidèlement celle d'OpenAI (/v1/chat/completions, /v1/completions, /v1/embeddings). Concrètement, toute application déjà écrite pour appeler l'API OpenAI ou Azure OpenAI peut basculer vers un modèle hébergé en interne en ne changeant que l'URL de base et la clé d'API — souvent une simple variable d'environnement.
curl http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "meta-llama/Llama-3.1-8B-Instruct",
"messages": [{"role": "user", "content": "Résume ce texte en trois points."}],
"max_tokens": 300,
"temperature": 0.3
}'
Cette compatibilité facilite considérablement les migrations : le code applicatif (LangChain, LlamaIndex, SDK OpenAI officiel, ou simple client HTTP maison) fonctionne généralement sans modification.
Déploiement en Docker
Le déploiement le plus courant repose sur l'image Docker officielle de vLLM, qui embarque déjà CUDA et les dépendances nécessaires.
docker run --runtime nvidia --gpus all \
-v ~/.cache/huggingface:/root/.cache/huggingface \
-p 8000:8000 \
--ipc=host \
vllm/vllm-openai:latest \
--model meta-llama/Llama-3.1-8B-Instruct \
--max-model-len 8192 \
--gpu-memory-utilization 0.90
Quelques paramètres méritent une attention particulière :
--gpu-memory-utilizationfixe la part de la mémoire GPU que vLLM peut réserver pour ses pages de cache KV et ses poids. Une valeur trop haute (proche de 1) expose à des erreurs de mémoire saturée si d'autres processus partagent le GPU ; une valeur trop basse limite inutilement le nombre de requêtes concurrentes.--max-model-lenborne la longueur de contexte effectivement servie. La réduire par rapport au maximum théorique du modèle libère de la mémoire pour davantage de requêtes en parallèle.--ipc=hostest nécessaire pour que le partage de mémoire entre processus internes de vLLM fonctionne correctement dans le conteneur.
vLLM évolue vite et chaque version cible une plage précise de versions de CUDA et de pilotes NVIDIA. Une image trop récente sur un hôte aux pilotes anciens échoue au démarrage avec des erreurs peu explicites. Vérifiez la compatibilité annoncée dans les notes de version avant toute mise à jour en production, et testez sur un environnement de préproduction identique au serveur cible.
Déploiement en systemd
Pour les environnements sans orchestrateur de conteneurs, un déploiement natif via systemd reste pertinent. Il suppose un environnement Python dédié (virtualenv ou conda) avec vLLM installé.
# /etc/systemd/system/vllm.service
[Unit]
Description=Serveur d'inference vLLM
After=network.target
[Service]
User=vllm
WorkingDirectory=/opt/vllm
Environment="CUDA_VISIBLE_DEVICES=0,1"
ExecStart=/opt/vllm/venv/bin/python -m vllm.entrypoints.openai.api_server \
--model meta-llama/Llama-3.1-8B-Instruct \
--tensor-parallel-size 2 \
--port 8000
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
Ce mode de déploiement facilite l'intégration avec les outilstool callingIACapacité d'un agent ou d'un LLM à invoquer des outils externes (API, calcul, recherche) pendant le raisonnement.Voir dans le glossaire de supervision système existants (journalctl, monit, Prometheus node exporter) et évite la couche supplémentaire de Docker sur des serveurs GPU dédiés à un seul usage.
Tensor parallel : répartir un modèle sur plusieurs GPU
Certains modèles dépassent la mémoire d'un seul GPU, ou l'on souhaite simplement augmenter le débit disponible. Le tensor parallel répartit les poids et les calculs de chaque couche du modèle sur plusieurs GPU, qui travaillent alors de concert sur chaque requête via des communications interconnexion rapides (NVLink idéalement, PCIe sinon).
vLLM active ce mode via le paramètre --tensor-parallel-size, qui doit correspondre au nombre de GPU disponibles sur la machine. Un modèle de 70 milliards de paramètres, par exemple, tient difficilement sur un seul GPU de 24 Go en pleine précisionprécisionIAProportion des alertes émises par un modèle qui sont justifiées. Elle s'oppose au rappel : améliorer l'une dégrade l'autre.Voir dans le glossaire, mais se répartit correctement sur quatre GPU avec --tensor-parallel-size 4.
Le tensor parallel sert avant tout à faire tenir un modèle trop volumineux pour un seul GPU, ou à réduire la latence par requête. Il ne multiplie pas mécaniquement le débit global : la communication inter-GPU introduit un surcoût, et au-delà d'un certain nombre de GPU, les gains de latence diminuent. Pour augmenter le débit d'un modèle qui tient déjà sur un GPU, il est souvent plus efficace de dupliquer des instances vLLM indépendantes derrière un répartiteur de charge que d'ajouter du tensor parallel.
Arbitrer entre throughput et latence
Un déploiement vLLM se règle toujours selon un compromis entre deux objectifs contradictoires : maximiser le nombre de tokens générés par seconde à l'échelle du service (throughput), ou minimiser le temps de réponse perçu par chaque utilisateur (latence).
| Paramètre | Favorise le throughput | Favorise la latence |
|---|---|---|
Taille de lot maximale (--max-num-seqs) |
élevée | basse |
--gpu-memory-utilization |
élevée (plus de requêtes en parallèle) | modérée (marge de sécurité) |
Longueur de contexte (--max-model-len) |
réduite | selon besoin réel |
| Nombre d'instances vs tensor parallel | plusieurs instances | tensor parallel sur un modèle unique |
| Quantisation (AWQ, GPTQ) | oui, si acceptable en qualité | à évaluer selon l'usage |
Un chatbot interactif, où l'utilisateur attend une réponse en direct, doit privilégier la latence : un temps de première réponse (time to first token) sous la seconde et un débit de génération perçu fluide priment sur le nombre total de requêtes traitées par minute. Un pipeline de traitement par lot — résumé nocturne de milliers de documents, extraction d'information en masse — peut au contraire tolérer une latence individuelle plus élevée en échange d'un débit global maximal.
Une équipe interne exploite le même modèle Llama 3.1 8B pour deux usages distincts sur des serveurs séparés : un assistant conversationnel accessible en journée, configuré avec une file d'attente courte et une utilisation mémoire prudente (0.80) pour garantir une latence stable même en pointe ; et un service de classification de tickets exécuté chaque nuit, configuré avec une utilisation mémoire agressive (0.95) et une file d'attente longue, qui traite plusieurs dizaines de milliers de tickets en quelques heures sans contrainte de latence individuelle.
Observabilité minimale
vLLM expose nativement un point de terminaison /metrics au format Prometheus, directement exploitable par une stack de supervision existante (Prometheus/Grafana). Les indicateurs à surveiller en priorité pour un déploiement multi-utilisateurs sont peu nombreux mais décisifs :
- Débit en tokens par seconde (génération et traitement du prompt séparément) : indique si le GPU est correctement exploité.
- Taille de la file d'attente (requêtes en attente de traitement) : une file qui croît en continu signale une saturation durable, pas un simple pic.
- Temps jusqu'au premier token (time to first token) : mesure directement l'expérience perçue par l'utilisateur, distincte du débit de génération.
- Taux d'utilisation du cache KV : proche de la saturation, il annonce des refus ou des files d'attente qui s'allongent avant même que le symptôme soit visible côté utilisateur.
- Nombre de requêtes actives simultanément : à comparer à la capacité théorique estimée lors du dimensionnement.
Sans ces métriques, un service qui ralentit progressivement passe souvent inaperçu jusqu'à ce que les utilisateurs se plaignent. Le taux d'utilisation du cache KV et la longueur de la file d'attente sont les deux signaux avancés les plus fiables : ils bougent avant que la latence perçue ne se dégrade visiblement.
Cas entreprise multi-utilisateurs
Prenons un scénario représentatif : une entreprise de deux cents collaborateurs souhaite déployer un assistant interne (aide à la rédaction, recherche documentaire, synthèse) accessible via une interface web pendant les heures de bureau, avec un pic estimé à une trentaine d'utilisateurs actifs simultanément.
La démarche de dimensionnement typique suit ces étapes :
- Choisir un modèle adapté au budget mémoire disponible — un modèle de 7 à 14 milliards de paramètres en quantisation AWQ tient sur un seul GPU 24 Go tout en laissant de la marge pour le cache KV.
- Estimer la longueur moyenne des échanges pour fixer un
--max-model-lenréaliste plutôt que le maximum théorique du modèle, souvent inutilement large et coûteux en mémoire. - Démarrer avec une seule instance vLLM et charger le service avec un outil de test de charge (par exemple
vllm benchou un script simple envoyant des requêtes concurrentes) pour mesurer le débit réel et le temps de réponse sous charge. - Ajouter des instances derrière un répartiteur de charge (nginx, Traefik, ou un load balancer applicatif) si une seule instance ne couvre pas le pic estimé, plutôt que de complexifier immédiatement avec du tensor parallel.
- Mettre en place la supervision (métriques Prometheus, alerte sur la file d'attente) avant la mise en production, pas après les premiers incidents.
Ce cas illustre un principe général : la bascule d'Ollama vers vLLM n'est pas qu'un changement d'outil, c'est un changement de posture, du prototypage individuel vers l'exploitation d'un service partagé qui doit être dimensionné, testé sous charge et superviséapprentissage superviséIAMéthode où chaque exemple d'entraînement est accompagné de la réponse attendue. C'est la famille la plus employée en entreprise, mais elle exige des données étiquetées — souvent le poste de coût principal.Voir dans le glossaire comme n'importe quel service d'entreprise critique.
Ce qu'il faut retenir
vLLM ne remplace pas Ollama dans tous les usages : pour un poste de travail individuel ou un prototype rapide, Ollama reste plus simple à installer et à faire évoluer. La bascule vers vLLM se justifie dès qu'un LLM local doit servir plusieurs utilisateurs ou applications simultanément, grâce à deux mécanismes complémentaires — PagedAttention pour une gestion efficace de la mémoire du cache KV, et le continuous batching pour maintenir le GPU occupé en continu. Son API compatible OpenAI simplifie la migration applicative, tandis que le tensor parallel permet de répartir un modèle volumineux sur plusieurs GPU quand la mémoire d'une seule carte ne suffit pas. Le réglage finFine-tuningIAAjustement des poids d'un modèle pré-entraîné sur un jeu de données spécifique pour adapter son comportement à un domaine ou une tâche cible.Voir dans le glossaire du service repose ensuite sur un arbitrage assumé entre throughput et latence, piloté par des métriques simples mais indispensables : débit, file d'attente, temps de premier token et taux d'utilisation du cache KV.
L'essentiel à retenir
Ce chapitre explique pourquoi Ollama, adapté au poste de travail et aux tests, devient un goulot d'étranglement dès qu'un LLM local doit servir plusieurs utilisateurs simultanés en entreprise. Il détaille les deux innovations techniques de vLLM — PagedAttention pour la gestion de la mémoire du cache KV et le continuous batching pour le débit — puis montre comment déployer vLLM en conteneur Docker ou en service systemd, avec ou sans tensor parallel sur plusieurs GPU. Il aborde enfin les arbitrages entre throughput et latence, ainsi que les métriques minimales à surveiller pour piloter un déploiement multi-utilisateurs.
Questions fréquentes
Faut-il abandonner Ollama complètement une fois vLLM déployé ?
vLLM fonctionne-t-il sans GPU, sur CPU uniquement ?
Quelle quantité de mémoire GPU faut-il pour servir un modèle de 8 milliards de paramètres avec vLLM ?
Le tensor parallel nécessite-t-il des GPU identiques ?
Comment tester la charge que peut supporter un déploiement vLLM avant la mise en production ?
Peut-on servir plusieurs modèles différents avec une seule instance vLLM ?
Quel est l'impact de la quantisation sur les performances de vLLM ?
Faut-il un load balancer dédié devant plusieurs instances vLLM ?
Progression sauvegardée dans votre navigateur.
Quiz de validation
Quiz Player
Quiz de validation
Plusieurs réponses possibles — validez ensuite.
Vrai ou faux.
Quiz indisponible (données invalides).