
vLLM est le moteur d'inférence de référence pour servir un LLM sur GPU en production. Là où Ollama ou llama.cpp visent l'usage local, vLLM est conçu pour le débit : traiter des dizaines de requêtes concurrentes sans effondrer la latence. Ce guide montre comment le déployer avec Docker, l'interroger via son API compatible OpenAI, régler ses paramètres selon votre GPU, et mesurer son débit réel avec l'outil de bench officiel. Public visé : SRE et ingénieurs plateforme disposant d'un GPU NVIDIA. Les mesures de ce guide ont été relevées sur un H100.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Comprendre ce que PagedAttention et le continuous batching apportent au serving LLM.
- Déployer vLLM avec Docker et son API compatible OpenAI.
- Régler les paramètres clés (
--max-model-len,--gpu-memory-utilization,--tensor-parallel-size) selon votre GPU. - Mesurer le débit et la latence réels avec
vllm bench serve. - Durcir le déploiement pour un usage production (clé d'API, redémarrage, reverse proxy).
Prérequis
Section intitulée « Prérequis »vLLM exige un GPU NVIDIA : il n'a pas d'équivalent du mode CPU d'Ollama. Pour suivre ce guide, vous avez besoin de :
- Un GPU NVIDIA architecture Ampere ou plus récente (A100, L40S, H100, RTX 30/40), avec assez de VRAM pour le modèle visé — comptez environ 30 Go pour un modèle 14B en
bfloat16. - Un driver NVIDIA récent et le NVIDIA Container Toolkit installé, pour exposer le GPU à Docker.
- Docker avec le plugin Compose.
Côté concepts, ce guide suppose acquises les notions des guides Comprendre l'inférence d'un LLM et Servir un LLM : batching et débit — prefill, decode, KV cache, continuous batching, PagedAttention. Si vous n'avez pas de GPU, le guide llama.cpp : serveur d'inférence couvre le serving sur CPU. Pour situer vLLM parmi les autres moteurs, voir le comparatif des backends d'inférence.
Pourquoi vLLM : PagedAttention et continuous batching
Section intitulée « Pourquoi vLLM : PagedAttention et continuous batching »Servir un LLM, c'est facile pour une requête isolée. Le problème surgit avec la charge concurrente : plusieurs utilisateurs en même temps, des longueurs de prompt variées, des réponses qui se terminent à des moments différents. Deux innovations de vLLM règlent ce problème.
La première est PagedAttention. Pendant la génération, le modèle conserve un cache des clés et valeurs d'attention (le KV cache) pour chaque séquence en cours. Géré naïvement, ce cache fragmente la mémoire GPU : on réserve un bloc contigu par séquence, dimensionné au pire cas, et la VRAM se gaspille. PagedAttention applique au KV cache la même idée que la mémoire virtuelle paginée d'un système d'exploitation : le cache est découpé en pages de taille fixe, allouées à la demande. Résultat, vLLM tient beaucoup plus de séquences simultanées dans la même VRAM.
La seconde est le continuous batching. Un serveur classique forme un lot fixe de requêtes, attend qu'elles soient toutes terminées, puis passe au lot suivant — une requête courte coincée avec des longues attend pour rien. vLLM travaille token par token : à chaque itération, il retire les séquences finies et injecte les nouvelles requêtes dans le lot en cours. Le GPU ne reste jamais inactif, et le débit agrégé grimpe fortement.
Lancer vLLM avec Docker
Section intitulée « Lancer vLLM avec Docker »L'équipe vLLM publie l'image officielle vllm/vllm-openai, qui démarre directement un serveur d'API compatible OpenAI. C'est l'approche la plus reproductible : aucune gestion de dépendances Python, CUDA ou PyTorch sur l'hôte.
-
Vérifier que Docker voit le GPU.
Fenêtre de terminal docker run --rm --gpus all nvidia/cuda:12.4.1-base-ubuntu22.04 nvidia-smi -LLa sortie doit afficher votre GPU, par exemple
GPU 0: NVIDIA H100 PCIe. Si la commande échoue, c'est le NVIDIA Container Toolkit qui manque ou est mal configuré. -
Décrire la stack dans un fichier
compose.yml.Cet exemple sert le modèle Qwen2.5-14B-Instruct, sous licence Apache 2.0 et sans restriction d'accès sur Hugging Face.
services:vllm:image: vllm/vllm-openai:latestcontainer_name: vllm-serverrestart: unless-stoppedruntime: nvidiaipc: hostports:- "8000:8000"volumes:- hf-cache:/root/.cache/huggingfacecommand:- --model- Qwen/Qwen2.5-14B-Instruct- --served-model-name- qwen2.5-14b- --max-model-len- "8192"- --gpu-memory-utilization- "0.90"deploy:resources:reservations:devices:- driver: nvidiacount: allcapabilities: [gpu]healthcheck:test: ["CMD-SHELL", "curl -fsS http://localhost:8000/health || exit 1"]interval: 15stimeout: 5sretries: 40start_period: 600svolumes:hf-cache:Trois points méritent un mot.
ipc: hostdonne au conteneur la mémoire partagée dont PyTorch a besoin. Le volumehf-cachepersiste les poids du modèle : ils ne seront téléchargés qu'une fois. Lestart_periodde 600 s laisse au premier démarrage le temps de télécharger le modèle et de compiler les kernels. -
Démarrer le serveur et suivre le chargement.
Fenêtre de terminal docker compose up -ddocker compose logs -f vllmLe premier démarrage est long : vLLM télécharge le modèle (environ 28 Go pour un 14B), le charge en VRAM, puis compile ses kernels CUDA et capture les CUDA graphs. Comptez de dix minutes à un quart d'heure. Cette compilation est mise en cache : les démarrages suivants sont rapides.
-
Vérifier que le serveur est prêt.
Fenêtre de terminal curl -fsS http://localhost:8000/healthdocker inspect -f '{{.State.Health.Status}}' vllm-serverLe
healthcheckdoit afficherhealthy. Les logs se terminent alors parApplication startup complete.
Interroger l'API OpenAI-compatible
Section intitulée « Interroger l'API OpenAI-compatible »vLLM expose la même API REST qu'OpenAI. Tout client ou bibliothèque qui sait parler à l'API OpenAI fonctionne en pointant simplement l'URL de base vers votre serveur. L'endpoint /v1/models liste les modèles servis.
curl -fsS http://localhost:8000/v1/modelsL'endpoint /v1/chat/completions génère une réponse. Le champ model reprend le nom déclaré dans --served-model-name.
curl -fsS http://localhost:8000/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "qwen2.5-14b", "messages": [ {"role": "user", "content": "En une phrase, quel est le rôle de PagedAttention dans vLLM ?"} ], "temperature": 0.2, "max_tokens": 90 }'La réponse est un objet JSON dont choices[0].message.content contient le texte généré. Comme l'API est compatible, le SDK Python openai fonctionne en changeant base_url :
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8000/v1", api_key="aucune-cle")
reponse = client.chat.completions.create( model="qwen2.5-14b", messages=[{"role": "user", "content": "Résume le continuous batching en deux phrases."}], temperature=0.2,)print(reponse.choices[0].message.content)Régler vLLM selon votre GPU
Section intitulée « Régler vLLM selon votre GPU »Les valeurs par défaut conviennent pour démarrer, mais quatre paramètres déterminent le comportement réel du serveur. Le tableau ci-dessous résume leur effet ; les sections suivantes du guide les mettent en pratique.
| Paramètre | Rôle | Conseil |
|---|---|---|
--max-model-len | Longueur de contexte maximale (tokens) | Réduisez-le si la VRAM manque ; chaque token coûte du KV cache |
--gpu-memory-utilization | Fraction de VRAM allouée à vLLM | 0.90 est un bon défaut ; baissez si le GPU est partagé |
--max-num-seqs | Séquences concurrentes dans un lot | Montez-le pour le débit, baissez-le pour la latence |
--tensor-parallel-size | Nombre de GPU sur lesquels répartir le modèle | 1 par GPU unique ; N pour un modèle trop gros pour une seule carte |
Le compromis central est entre contexte et concurrence. La VRAM non occupée par les poids du modèle sert de KV cache partagé entre les séquences. Un --max-model-len élevé réserve plus de cache par séquence, donc moins de séquences simultanées. Inversement, un contexte court libère du cache pour traiter plus de requêtes en parallèle.
Mesurer le débit avec vllm bench serve
Section intitulée « Mesurer le débit avec vllm bench serve »Annoncer « c'est rapide » ne vaut rien sans mesure. vLLM intègre vllm bench serve, un outil qui injecte une charge contrôlée et rapporte débit et latence. Il distingue deux métriques de latence essentielles : le TTFT (Time To First Token, délai avant le premier token, ce que l'utilisateur perçoit comme « ça démarre ») et le TPOT (Time Per Output Token, cadence de génération ensuite).
La commande suivante envoie 200 requêtes de 1024 tokens d'entrée et 256 de sortie. Le --tokenizer pointe le dépôt Hugging Face réel, car le nom servi (qwen2.5-14b) n'est pas un identifiant de modèle valide pour charger le tokenizer.
docker exec vllm-server vllm bench serve \ --backend vllm \ --base-url http://localhost:8000 \ --model qwen2.5-14b \ --tokenizer Qwen/Qwen2.5-14B-Instruct \ --dataset-name random \ --num-prompts 200 \ --random-input-len 1024 \ --random-output-len 256 \ --request-rate infL'option --request-rate change tout. À inf, les 200 requêtes partent d'un coup : on mesure le débit plafond. À une valeur finie (par exemple 8), elles arrivent progressivement : on mesure la latence en service réaliste. Voici les deux mesures relevées sur un H100 PCIe 80 Go avec vLLM 0.21 et Qwen2.5-14B-Instruct.
| Scénario | Débit total | TTFT médian | TPOT médian |
|---|---|---|---|
Débit plafond (--request-rate inf) | 8 354 tok/s | 7 983 ms | 74,6 ms |
Charge modérée (--request-rate 8) | 7 916 tok/s | 230 ms | 53,2 ms |
La lecture est instructive. Entre les deux scénarios, le débit total bouge à peine (8 354 contre 7 916 tokens/seconde), mais le TTFT médian s'effondre de près de 8 secondes à 230 millisecondes. À pleine charge, les requêtes font la queue : le débit est maximal mais chaque utilisateur attend. À charge modérée, le continuous batching maintient un débit quasi identique tout en gardant une latence faible.
La conclusion pour le dimensionnement est nette : viser un taux de requêtes en deçà du plafond préserve la latence perçue. Le bon point de fonctionnement n'est pas le débit maximal, mais le débit le plus élevé qui tient votre budget de TTFT.
Passer en production
Section intitulée « Passer en production »Le compose.yml ci-dessus est déjà proche d'un déploiement réel. La directive restart: unless-stopped relance le conteneur après un crash ou un redémarrage de l'hôte, à condition que le service Docker soit lui-même activé au boot (sudo systemctl enable docker). Inutile d'écrire un service systemd dédié : Docker assure le cycle de vie.
Trois points de sécurité sont non négociables. D'abord, exiger une clé d'API : ajoutez --api-key à la commande vLLM, et chaque requête devra porter l'en-tête Authorization: Bearer <clé>. Cette clé est un secret — passez-la par un fichier .env non versionné. Ensuite, ne jamais exposer le port 8000 directement sur Internet : un serveur vLLM ouvert est une ressource de calcul GPU offerte à n'importe qui. Enfin, placez vLLM derrière un reverse proxy qui termine le TLS et applique un rate limiting.
Dépannage
Section intitulée « Dépannage »Les incidents au démarrage de vLLM se résument à quelques causes récurrentes. Le tableau relie le symptôme observé à sa cause et à la correction.
| Symptôme | Cause probable | Solution |
|---|---|---|
CUDA out of memory au chargement | Modèle trop gros pour la VRAM | Baisser --max-model-len, ou répartir avec --tensor-parallel-size, ou choisir un modèle quantifié |
is not a valid model identifier | Modèle à accès restreint sans token | Accepter la licence sur Hugging Face et fournir HF_TOKEN |
| Démarrage très long la première fois | Téléchargement du modèle + compilation des kernels | Normal ; le cache rend les démarrages suivants rapides |
failed to set up container networking | Réseau Docker non recréé après un redémarrage du démon | Relancer docker compose up -d |
| GPU absent dans le conteneur | NVIDIA Container Toolkit manquant ou non configuré | Installer le toolkit et relancer nvidia-ctk runtime configure |
| Latence qui explose sous charge | Trop de requêtes simultanées | Baisser --max-num-seqs ou lisser le trafic en amont |
Pour observer le serveur en fonctionnement, docker compose logs -f vllm affiche chaque requête traitée, et nvidia-smi montre l'occupation VRAM et l'utilisation GPU en temps réel.
À retenir
Section intitulée « À retenir »- vLLM sert un LLM sur GPU NVIDIA avec un objectif de débit, pas d'usage local mono-utilisateur.
- PagedAttention pagine le KV cache et continuous batching garde le GPU occupé : ensemble, ils maximisent le débit concurrent.
- L'image Docker
vllm/vllm-openaidonne un serveur compatible OpenAI sans gérer CUDA sur l'hôte. - Le premier démarrage est long (téléchargement + compilation) ; le cache rend les suivants rapides.
vllm bench servedistingue TTFT et TPOT : le bon point de fonctionnement vise le débit qui respecte votre budget de latence, pas le débit maximal.- En production, clé d'API, reverse proxy et TLS sont obligatoires — un GPU exposé se fait détourner.