ITSkillsCenter
Intelligence Artificielle

Servir un modèle DeepSeek en production avec vLLM

13 min de lecture

À l’échelle d’une équipe, appeler une API au jeton finit par coûter cher et par poser la question des données qui sortent de l’entreprise. À un certain volume, héberger soi-même le modèle devient plus économique et plus maîtrisé. Encore faut-il un moteur d’inférence taillé pour la charge : c’est le rôle de vLLM, capable de servir des dizaines de requêtes simultanées avec un débit élevé. Dans ce guide, vous allez déployer un modèle DeepSeek distillé derrière un serveur vLLM compatible OpenAI, puis l’exposer comme une API interne que toute l’équipe pourra appeler.

📍 Guide principal de la série : DeepSeek : modèles, API et déploiement local. Pour situer vLLM face à Ollama et à l’API distante, le guide principal pose les arbitrages.

🎯 Ce que vous allez apprendre

  • Installer vLLM et lancer un serveur d’inférence en une commande.
  • Servir un modèle DeepSeek distillé compatible avec le format OpenAI.
  • Régler les options clés : nombre de cartes, taille de contexte, mémoire.
  • Interroger le serveur depuis le terminal et depuis Python.
  • Exposer la chaîne de raisonnement séparément.
  • Protéger l’accès avec une clé et empaqueter le tout dans un conteneur.

🛠️ Ce que vous allez construire

Une API de modèle de langage interne : un serveur vLLM exposant un modèle DeepSeek sur le réseau de l’entreprise, que vos applications appellent exactement comme l’API d’OpenAI, mais sans facturation au jeton et sans que les données quittent vos murs. Le fil conducteur est la mise en service de ce point d’accès, de son démarrage à sa sécurisation.

Prérequis

  • Une machine Linux équipée d’une carte graphique compatible CUDA et de ses pilotes à jour.
  • Python 3.9 ou plus récent, et de préférence un environnement virtuel.
  • Assez de mémoire graphique : un modèle distillé de 7 à 8 milliards de paramètres tient sur une seule carte récente ; les tailles supérieures réclament plusieurs cartes.
  • Test express : si nvidia-smi affiche votre carte, vous êtes prêt.
  • ⏱️ Temps estimé : environ 45 minutes, hors téléchargement du modèle.

Étape 1 — Installer vLLM

vLLM est une bibliothèque d’inférence qui optimise l’usage de la mémoire graphique grâce à une gestion fine du cache d’attention, ce qui lui permet de traiter beaucoup de requêtes en parallèle. Son installation se fait via pip, de préférence dans un environnement isolé pour éviter les conflits de versions.

python -m venv .venv
source .venv/bin/activate
pip install vllm

L’installation récupère vLLM et ses dépendances, dont les bibliothèques de calcul sur carte graphique — l’opération peut être longue. Si pip se termine sans erreur et que vllm --help affiche l’aide, le moteur est prêt à servir un modèle.

Étape 2 — Lancer le serveur d’inférence

La commande vllm serve télécharge le modèle depuis le dépôt de modèles s’il est absent, le charge en mémoire graphique, et démarre un serveur HTTP. On choisit ici une distillation de DeepSeek-R1 sur base Qwen, un bon compromis entre qualité et ressources.

vllm serve deepseek-ai/DeepSeek-R1-Distill-Qwen-14B \
    --tensor-parallel-size 2 \
    --max-model-len 32768

Au démarrage, vLLM ouvre un serveur sur le port 8000, exposant des points d’entrée identiques à ceux d’OpenAI. Le téléchargement du modèle n’a lieu qu’au premier lancement. Quand la console affiche une ligne signalant que l’application écoute sur 0.0.0.0:8000, le serveur est opérationnel et prêt à recevoir des requêtes.

Point d’étape — Le serveur démarre et écoute sur le port 8000. Pour vérifier : depuis un autre terminal, curl http://localhost:8000/v1/models doit renvoyer la liste contenant le modèle chargé.

Étape 3 — Régler les options qui comptent

Trois paramètres déterminent si votre déploiement tient ou s’effondre. Les comprendre vous évite des heures de tâtonnement face à des erreurs de mémoire saturée.

  • --tensor-parallel-size indique sur combien de cartes graphiques répartir le modèle. Mettez-y le nombre de cartes disponibles : un modèle de 14 milliards de paramètres ne tient pas sur une seule carte de moyenne gamme, mais se répartit sur deux.
  • --max-model-len plafonne la longueur de contexte. Plus elle est grande, plus le cache d’attention consomme de mémoire. La réduire est souvent la solution la plus simple quand le serveur refuse de démarrer faute de place.
  • --gpu-memory-utilization fixe la fraction de mémoire graphique que vLLM s’autorise à prendre (0,9 par défaut). L’abaisser laisse de la marge à d’autres processus sur la même carte.

Si le démarrage échoue sur un message de mémoire insuffisante, le réflexe est de réduire --max-model-len, puis éventuellement de choisir un modèle plus petit. À l’inverse, sur une grosse infrastructure, augmenter ces valeurs exploite pleinement le matériel.

Étape 4 — Interroger le serveur

Le serveur parle le dialecte d’OpenAI : les mêmes points d’entrée, les mêmes formats de requête et de réponse. On peut donc l’appeler en ligne de commande pour un test rapide.

curl http://localhost:8000/v1/chat/completions \
    -H "Content-Type: application/json" \
    -d '{
        "model": "deepseek-ai/DeepSeek-R1-Distill-Qwen-14B",
        "messages": [{"role": "user", "content": "Resume le principe d un cache LRU."}]
    }'

La réponse est un objet JSON au format familier, avec le message de l’assistant dans choices. Côté application, on réutilise sans surprise le SDK d’OpenAI, en pointant simplement la base d’URL vers votre serveur interne.

from openai import OpenAI

client = OpenAI(base_url="http://localhost:8000/v1", api_key="interne")

r = client.chat.completions.create(
    model="deepseek-ai/DeepSeek-R1-Distill-Qwen-14B",
    messages=[{"role": "user", "content": "Explique la difference entre un thread et un processus."}],
)
print(r.choices[0].message.content)

Le nom du modèle correspond exactement à celui passé à vllm serve. C’est tout l’intérêt de la compatibilité : vos applications déjà écrites pour OpenAI ou pour l’API DeepSeek distante basculent vers votre serveur interne en changeant deux lignes.

Étape 5 — Exposer le raisonnement

Les modèles R1 produisent une chaîne de pensée. Par défaut, vLLM la fond dans la réponse, mélangée au résultat. Pour la séparer proprement, on active un analyseur de raisonnement dédié au format DeepSeek-R1 au démarrage du serveur.

vllm serve deepseek-ai/DeepSeek-R1-Distill-Qwen-14B \
    --tensor-parallel-size 2 \
    --max-model-len 32768 \
    --reasoning-parser deepseek_r1

Avec cette option, la réponse sépare le raisonnement de la conclusion : le cheminement atterrit dans un champ reasoning (anciennement nommé reasoning_content dans les versions précédentes), tandis que le résultat reste dans content. Vos applications peuvent alors journaliser le raisonnement pour audit, ou l’afficher dans une zone repliable, sans le voir polluer la réponse principale.

Point d’étape — La réponse JSON contient désormais deux champs distincts. Pour vérifier : interrogez le serveur sur une question de logique et inspectez la réponse brute ; le champ reasoning doit contenir l’analyse, séparée de la conclusion.

Étape 6 — Protéger l’accès avec une clé

Un serveur ouvert sans authentification sur un réseau d’entreprise est une invitation aux abus. vLLM accepte une clé d’API à exiger sur chaque requête : toute requête sans la bonne clé est rejetée.

vllm serve deepseek-ai/DeepSeek-R1-Distill-Qwen-14B \
    --tensor-parallel-size 2 \
    --api-key cle-interne-secrete

Les clients doivent alors présenter cette clé, exactement comme pour une API commerciale. C’est un premier rempart ; en production, on l’adosse à un proxy inverse qui gère le chiffrement TLS et limite le débit par client. Ne laissez jamais le port 8000 exposé directement à l’extérieur sans cette couche de protection.

client = OpenAI(base_url="http://localhost:8000/v1", api_key="cle-interne-secrete")

Étape 7 — Empaqueter dans un conteneur

Pour un déploiement reproductible, l’image Docker officielle de vLLM évite d’installer quoi que ce soit sur l’hôte au-delà des pilotes graphiques. On monte le cache de modèles depuis l’hôte pour ne pas re-télécharger à chaque redémarrage.

docker run --runtime nvidia --gpus all \
    -v ~/.cache/huggingface:/root/.cache/huggingface \
    -p 8000:8000 \
    --ipc=host \
    vllm/vllm-openai:latest \
    --model deepseek-ai/DeepSeek-R1-Distill-Qwen-14B \
    --max-model-len 32768

L’option --runtime nvidia --gpus all donne au conteneur l’accès aux cartes graphiques, le volume monté partage le cache de modèles, et --ipc=host évite une limite de mémoire partagée qui ferait planter le chargement. Le conteneur expose le même serveur sur le port 8000 : du point de vue des clients, rien ne change. C’est la voie recommandée pour un serveur durable, que l’on peut piloter avec un orchestrateur.

Étape 8 — Vérifier le débit sous charge

La promesse de vLLM, c’est le débit en parallèle. Un test simple consiste à lancer plusieurs requêtes simultanées et à constater que le serveur les absorbe sans s’écrouler, grâce au traitement par lots dynamique qui regroupe les requêtes à la volée.

import concurrent.futures
from openai import OpenAI

client = OpenAI(base_url="http://localhost:8000/v1", api_key="cle-interne-secrete")

def une_requete(i):
    r = client.chat.completions.create(
        model="deepseek-ai/DeepSeek-R1-Distill-Qwen-14B",
        messages=[{"role": "user", "content": "Donne un conseil de performance numero " + str(i)}],
        max_tokens=120,
    )
    return r.choices[0].message.content[:40]

with concurrent.futures.ThreadPoolExecutor(max_workers=8) as ex:
    for res in ex.map(une_requete, range(8)):
        print(res)

Huit requêtes partent en parallèle ; vLLM les regroupe et les traite ensemble, ce qui maintient un bon débit là où un serveur naïf les enchaînerait une par une. En montant progressivement le nombre de requêtes simultanées, vous repérez la capacité réelle de votre matériel et dimensionnez en conséquence.

Étape 9 — Suivre la santé du serveur

Un service partagé qu’on ne surveille pas finit toujours par tomber au pire moment. vLLM expose nativement des métriques au format Prometheus sur un point d’entrée dédié, ce qui permet de brancher une supervision sans rien installer de plus côté serveur. Un simple appel suffit à les consulter.

curl http://localhost:8000/metrics

La réponse est une longue liste de compteurs et de jauges. Quelques indicateurs méritent toute votre attention au quotidien. Le nombre de requêtes en cours de traitement révèle la charge instantanée ; le nombre de requêtes en file d’attente est le signal d’alerte par excellence — s’il grimpe et ne redescend pas, votre matériel est saturé et les clients attendent. Le débit en jetons par seconde mesure la capacité réelle, et le délai avant le premier jeton reflète la réactivité perçue par l’utilisateur.

Concrètement, on configure un collecteur Prometheus pour interroger ce point d’entrée à intervalle régulier, puis on visualise les courbes dans un tableau de bord. Deux seuils valent qu’on les surveille de près :

  • File d’attente persistante — si des requêtes patientent en continu, il faut ajouter du matériel, réduire la taille du modèle, ou répartir la charge sur un second serveur.
  • Mémoire graphique au plafond — proche de la limite fixée par --gpu-memory-utilization, le serveur risque de refuser de nouvelles requêtes ; c’est le moment d’agir avant la panne.

Cette boucle d’observation transforme un serveur « qui marche aujourd’hui » en service sur lequel on peut s’appuyer dans la durée. Sans elle, vous découvrez les problèmes par les plaintes des utilisateurs ; avec elle, vous les anticipez sur les courbes.

Point d’étape — Le point d’entrée des métriques répond. Pour vérifier : lancez le test de charge de l’étape 8 tout en consultant /metrics ; le nombre de requêtes en cours doit augmenter pendant la rafale, puis retomber à zéro une fois traitée.

🐞 Pièges fréquents

Symptôme / erreur Cause probable Correctif
CUDA out of memory au démarrage Modèle ou contexte trop grand pour la carte Réduire --max-model-len ou choisir un modèle plus petit
Le serveur ne répond pas de l’extérieur Pare-feu ou écoute limitée à localhost Ouvrir le port et placer un proxy inverse devant
Chargement qui plante en conteneur --ipc=host oublié Ajouter l’option au docker run
Raisonnement mélangé à la réponse Analyseur de raisonnement non activé Ajouter --reasoning-parser deepseek_r1
Débit décevant Trop peu de requêtes en parallèle Augmenter la concurrence côté client

Peser l’intérêt de l’auto-hébergement

Self-héberger n’a de sens qu’à partir d’un certain volume. En dessous, l’API distante, facturée à l’usage et sans matériel à entretenir, reste imbattable. Le point de bascule arrive quand le coût mensuel des jetons dépasse celui d’une carte graphique amortie, ou quand la confidentialité interdit d’envoyer les données vers un tiers. vLLM brille précisément dans ce second cas : tout reste sur votre infrastructure, le débit est élevé, et le coût marginal d’une requête supplémentaire tend vers zéro une fois le matériel en place. Pour un poste de travail individuel ou un usage léger, Ollama reste plus simple ; vLLM vise le service partagé sous charge.

✅ Récapitulatif

Vous avez monté une API de modèle de langage interne : vLLM installé, un modèle DeepSeek distillé servi sur le port 8000 au format OpenAI, les options de mémoire et de parallélisme maîtrisées, le raisonnement exposé séparément, l’accès protégé par une clé, et le tout empaqueté dans un conteneur reproductible. Vos applications l’appellent comme n’importe quelle API compatible, sans facturation au jeton et sans fuite de données.

🧾 Aide-mémoire

Option Rôle
vllm serve <modele> Démarrer le serveur d’inférence
--tensor-parallel-size N Répartir le modèle sur N cartes graphiques
--max-model-len Plafonner la longueur de contexte (et la mémoire)
--gpu-memory-utilization Fraction de mémoire graphique utilisée
--reasoning-parser deepseek_r1 Séparer le raisonnement de la réponse
--api-key Exiger une clé sur chaque requête

💪 À vous de jouer

Servez un modèle plus léger — la distillation de 7 milliards de paramètres — sur une seule carte graphique, puis comparez le débit avec la version de 14 milliards sous huit requêtes simultanées. Mesurez le temps total des deux côtés.

Voir une solution
vllm serve deepseek-ai/DeepSeek-R1-Distill-Qwen-7B \
    --max-model-len 16384

# puis relancer le test de charge de l'etape 8
# le modele 7B, plus leger, encaisse generalement plus de requetes par seconde

Le modèle plus petit répond plus vite et libère de la mémoire pour traiter davantage de requêtes en parallèle ; à vous d’arbitrer entre vitesse et qualité selon vos besoins.

Dans la même série

Pour approfondir

FAQ

Faut-il obligatoirement une carte graphique ?
Pour un débit décent, oui. vLLM peut fonctionner sur processeur pour des essais, mais les performances sérieuses supposent une carte compatible CUDA.

Quelle taille de modèle pour une seule carte ?
Une distillation de 7 à 8 milliards de paramètres tient confortablement sur une carte récente. Au-delà, répartissez sur plusieurs cartes avec --tensor-parallel-size.

Mes données restent-elles privées ?
Oui : le serveur tourne sur votre infrastructure et aucune requête ne sort vers un tiers, ce qui est l’un des grands intérêts de l’auto-hébergement.

Puis-je réutiliser mon code OpenAI ?
Oui, vLLM expose une API compatible. Il suffit de changer la base d’URL et le nom du modèle.

Partager
Service ITSkillsCenter

Application mobile Android et iOS

Création d'application mobile Android et iOS. À partir de 350 000 FCFA.

Démarrer mon projet
Publicité