IA.2 · Héberger, brancher, sécuriser
Servir un modèle sur Kubernetes
Leçon théorique · questionnaire de validation · gratuit, dans le navigateur
Héberger un modèle, c'est faire tourner un serveur d'inférence : un conteneur qui charge les poids en mémoire du GPU et répond aux requêtes. Kubernetes apporte ce qu'il apporte à toute application (déploiement, sondes, mise à l'échelle, isolation), avec trois particularités : des GPU à attribuer, des modèles de plusieurs dizaines de gigaoctets à charger, et une charge qui se mesure en jetons plutôt qu'en requêtes. Cette leçon est théorique : les clusters d'exercice n'ont pas de GPU.
Ce que vous saurez à la fin : rendre des GPU disponibles dans un cluster, déployer vLLM (et Ollama pour les petits besoins), stocker et charger les poids efficacement, régler les sondes et la mise à l'échelle d'un serveur d'inférence, et situer KServe et les autres outils.
1. Des GPU dans Kubernetes
Un GPU n'est pas visible par défaut : il faut le pilote sur le nœud, et un device plugin qui l'annonce au kubelet comme ressource. Chez NVIDIA, le GPU Operator installe tout (pilote, outils du runtime de conteneurs, device plugin, supervision) :
helm repo add nvidia https://helm.ngc.nvidia.com/nvidia
helm install gpu-operator nvidia/gpu-operator -n gpu-operator --create-namespace
kubectl get nodes -o custom-columns=NOM:.metadata.name,GPU:.status.allocatable.nvidia\\.com/gpu
Un pod demande ensuite des GPU comme du CPU, en limite (un GPU entier, non partagé par défaut) :
resources:
limits: {nvidia.com/gpu: 1}
Les nœuds GPU coûtent cher : réservez-les avec une teinte (nvidia.com/gpu=present:NoSchedule) et une tolérance sur les seuls pods d'inférence (leçon 4.2), pour qu'aucun pod ordinaire ne vienne s'y installer. Le partage d'un GPU (MIG, découpage temporel) existe pour les petits modèles.
2. Déployer vLLM
vLLM est un serveur d'inférence ouvert très répandu : regroupement continu des requêtes, gestion efficace du cache KV, API compatible OpenAI.
apiVersion: apps/v1
kind: Deployment
metadata: {name: llm, labels: {app: llm}}
spec:
replicas: 1
selector: {matchLabels: {app: llm}}
template:
metadata: {labels: {app: llm}}
spec:
tolerations: [{key: nvidia.com/gpu, operator: Exists, effect: NoSchedule}]
containers:
- name: vllm
image: vllm/vllm-openai:v0.11.0 # épinglez une version testée
args: ["--model", "Qwen/Qwen3-8B", "--served-model-name", "qwen3-8b",
"--max-model-len", "16384", "--gpu-memory-utilization", "0.90"]
env:
- name: HF_HOME
value: /modeles
ports: [{containerPort: 8000}]
resources:
requests: {cpu: "4", memory: 24Gi}
limits: {nvidia.com/gpu: 1, memory: 32Gi}
volumeMounts:
- {name: modeles, mountPath: /modeles}
- {name: shm, mountPath: /dev/shm}
startupProbe: # le chargement prend plusieurs minutes
httpGet: {path: /health, port: 8000}
periodSeconds: 10
failureThreshold: 90
readinessProbe:
httpGet: {path: /health, port: 8000}
volumes:
- name: modeles
persistentVolumeClaim: {claimName: modeles}
- name: shm
emptyDir: {medium: Memory, sizeLimit: 8Gi}
--max-model-lenborne le contexte, donc le cache KV : c'est le premier réglage si le serveur manque de mémoire.- Une sonde de démarrage généreuse évite que Kubernetes tue le pod pendant le chargement des poids (leçon 2.13).
/dev/shmen mémoire : nécessaire dès que plusieurs processus ou GPU coopèrent.
Un Service devant, et toute application compatible OpenAI peut l'utiliser : http://llm.ia.svc:8000/v1.
3. Ollama, pour commencer petit
Ollama est plus simple : il télécharge et fait tourner des modèles quantifiés (GGUF), sur GPU ou sur CPU, avec une API sur le port 11434 (compatible OpenAI sous /v1). Idéal pour un poste, une démonstration ou un petit outil interne ; vLLM l'emporte nettement en débit sous charge.
4. Les poids : où et comment
Télécharger 16 Go depuis Internet à chaque démarrage de pod est lent et fragile. Options :
- un PVC partagé, rempli une fois par un Job (
huggingface-cli download …), monté en lecture par les pods ; - une image OCI contenant le modèle (montée comme volume
image, ou en modelcar avec KServe) : le modèle se versionne et se distribue comme une image, depuis votre registre ; - un stockage objet (S3) avec un cache local sur chaque nœud.
Dans tous les cas, épinglez la révision exacte du modèle, et vérifiez sa licence : « ouvert » ne veut pas toujours dire « utilisable commercialement sans condition ».
5. Mise à l'échelle
L'utilisation CPU ne dit rien de la charge d'un serveur d'inférence. Les bons signaux, exposés en métriques Prometheus par vLLM (/metrics) : requêtes en attente, utilisation du cache KV, délai avant le premier jeton. KEDA sait mettre à l'échelle sur une requête Prometheus (leçon 4.4), et l'autoscaler de nœuds ajoute des machines GPU au besoin. Gardez en tête qu'un nouveau réplica met plusieurs minutes à être prêt : on anticipe plutôt qu'on réagit, et on garde un minimum de réplicas pendant les heures ouvrées.
Pour répartir la charge intelligemment entre réplicas (selon leur cache et leur file d'attente plutôt qu'au hasard), le projet Gateway API Inference Extension ajoute un routage spécialisé au-dessus de la Gateway API (leçon 3.7).
6. Les plateformes de service
| Outil | Rôle |
|---|---|
| KServe | ressource InferenceService : serveur, mise à l'échelle (jusqu'à zéro), canary entre versions de modèle, plusieurs moteurs (vLLM, Triton…) |
| llm-d | inférence distribuée de grands modèles sur Kubernetes, autour de vLLM |
| NVIDIA Triton / NIM | serveurs et conteneurs optimisés du constructeur |
| Ray Serve (KubeRay) | service et calcul distribué Python |
Pour un ou deux modèles, un Deployment vLLM bien réglé suffit. Une plateforme se justifie quand de nombreuses équipes publient de nombreux modèles.
Validez la leçon avec le questionnaire.