- ✓ AIP1 Isambard-AI Phase 1 supported
- ✗ AIP2 Isambard-AI Phase 2 unsupported
- ✗ I3 Isambard 3 unsupported
- ✗ BC5 BlueCrystal 5 unsupported
Kubernetes: LLM Inference with KServe - Experimental Access Only
Warning
This guide describes the experimental Kubernetes service on Isambard-AI Phase 1, which is invite-only and not designed for production workloads. See the Kubernetes overview for details of the service, the cluster and the policies that apply to it.
This guide shows how to serve an LLM with a KServe LLMInferenceService using kubectl.
The example deploys one instance of OpenAI's GPT-OSS-120B model on eight GH200s across two nodes.
A multi-node example, not a sensible deployment
Spreading GPT-OSS-120B over eight GPUs and two nodes is not a sensible way to serve this model. A single GPU would be fine, and would be simpler and faster to start. The example is deliberately oversized to show you how to deploy a model across multiple nodes, which you will need for models too large for one node.
You need kubectl access to follow this guide.
If you would rather use a web interface, the Argo Workflows gpt-oss-120b-2n8g-llmisvc template performs the same steps.
About LLMInferenceService¶
The LLMInferenceService provides a Custom Resource Definition (CRD) to serve generative AI models.
It is built on top of llm-d and combines vLLM's inference engine with Kubernetes orchestration and intelligent routing capabilities.
It supports KV-cache aware scheduling, disaggregated prefill-decode serving, and distributed inference.
All of the required configuration for a model, such as parallelism settings, disaggregated prefill-decode, routing and scheduling, is directly configurable in the LLMInferenceService CRD.
See our Distributed vLLM Inference tutorial for background on tensor and pipeline parallelism.
Some configuration settings, such as parallelism, only apply when using the built-in llm-d container. If you provide your own container image, using an NGC image for example, these settings are not automatically picked up and you need to pass the equivalent options to vLLM yourself.
KServe handles model selection within the model spec of the LLMInferenceService.
You can specify a path to a model in a PVC or the Hugging Face URI for a model.
This is then automatically mounted at /mnt/models in the containers created in the LLMInferenceService pods.
Download the model into a PVC¶
Downloading the model into a PersistentVolumeClaim (PVC) once means it does not need to be downloaded again each time you deploy it.
-
Create a PVC to hold the model. Save the following as
hf-model-cache.yaml:apiVersion: v1 kind: PersistentVolumeClaim metadata: name: hf-model-cache spec: accessModes: - ReadWriteMany storageClassName: longhorn resources: requests: storage: 250GiThe PVC uses
ReadWriteManyso that the pods on both nodes can mount it at the same time. Then create it: -
Create a pod to download the model and the Tiktoken encodings that GPT-OSS needs into the PVC. Save the following as
download-gpt-oss-120b.yaml:apiVersion: v1 kind: Pod metadata: name: download-gpt-oss-120b spec: restartPolicy: Never securityContext: runAsNonRoot: true runAsUser: 1000 fsGroup: 1000 volumes: - name: models persistentVolumeClaim: claimName: hf-model-cache containers: - name: download image: python:3.12-slim env: - name: HOME value: /tmp resources: requests: cpu: "2" memory: 4Gi volumeMounts: - name: models mountPath: /models command: ["bash", "-c"] args: - | set -o errexit pip install --quiet --user huggingface_hub ~/.local/bin/hf download openai/gpt-oss-120b \ --local-dir /models/gpt-oss-120b \ --exclude "original/*" --exclude "metal/*" mkdir -p /models/gpt-oss-120b/tiktoken for encoding in o200k_base cl100k_base; do python -c "import urllib.request; urllib.request.urlretrieve('https://openaipublic.blob.core.windows.net/encodings/${encoding}.tiktoken', '/models/gpt-oss-120b/tiktoken/${encoding}.tiktoken')" doneThe pod meets the cluster's policies: it runs as a non-root user and sets CPU and memory requests.
fsGroupmakes the PVC writable by that user. The--excludeoptions skip copies of the weights in other formats that vLLM does not need.Gated models
To download a gated model from Hugging Face, create a Kubernetes
Secretcontaining your Hugging Face token and expose it to the container as theHF_TOKENenvironment variable.Create the pod and follow its progress:
-
Check that the download finished successfully:
The
STATUSshould beCompletedbefore you continue. If it isError, check the output ofkubectl logs download-gpt-oss-120bbefore deleting the pod. If it is still running, wait for it to finish and check again.Once the download has completed successfully, delete the pod:
Deploy the LLMInferenceService¶
-
Save the following as
gpt-oss-120b-llmisvc.yaml, replacingPROJECTwith your project's short code:apiVersion: serving.kserve.io/v1alpha2 kind: LLMInferenceService metadata: name: gpt-oss-120b-2n8g-llmisvc namespace: PROJECT spec: annotations: modules.isambard.ac.uk/load: aws-ofi-nccl baseRefs: - name: brics-gateway model: name: openai/gpt-oss-120b uri: pvc://hf-model-cache/gpt-oss-120b replicas: 1 parallelism: tensor: 4 pipeline: 2 template: resourceClaims: - name: whole-node resourceClaimTemplateName: gh200-x4-cxi-x4 containers: - name: main resources: requests: {cpu: "72", memory: 64Gi, ephemeral-storage: 20Gi} limits: {cpu: "72", memory: 64Gi, ephemeral-storage: 20Gi} claims: - name: whole-node env: - name: TIKTOKEN_ENCODINGS_BASE value: /mnt/models/tiktoken - name: HF_HUB_CACHE value: /mnt/models worker: resourceClaims: - name: whole-node resourceClaimTemplateName: gh200-x4-cxi-x4 containers: - name: main resources: requests: {cpu: "72", memory: 64Gi, ephemeral-storage: 20Gi} limits: {cpu: "72", memory: 64Gi, ephemeral-storage: 20Gi} claims: - name: whole-node env: - name: TIKTOKEN_ENCODINGS_BASE value: /mnt/models/tiktoken - name: HF_HUB_CACHE value: /mnt/modelsIn this manifest:
parallelismsplits the model across four GPUs on each of two nodes: tensor parallelism within a node and pipeline parallelism across nodes.templatedescribes the pod on the first node andworkerdescribes the pod on the second. Each claims a whole node, with all four GPUs and CXI devices, through thegh200-x4-cxi-x4ResourceClaimTemplate.- The
aws-ofi-ncclmodule enables RDMA over the Slingshot network between the two nodes. baseRefsandrouter.routeattach the model to the Kubernetes Gateway Service. See Kubernetes Gateway Service. Remove them if you do not need to reach the model from outside the cluster.
-
Create the
LLMInferenceService: -
Wait for the model to load. This can take several minutes:
To see the pods and their progress:
Debugging multi-node communication
If the model fails to load across the two nodes, add the environment variables
NCCL_DEBUG=INFOandFI_LOG_LEVEL=DEBUGto both containers to get more detailed logs from NCCL and libfabric.
Once the LLMInferenceService is ready, you can send requests to it through the Kubernetes Gateway Service.
Delete the LLMInferenceService¶
The resource reaper deletes the LLMInferenceService after 24 hours.
To free the GPUs sooner, delete it yourself when you have finished:
The model stays in the hf-model-cache PVC, so you can deploy it again without downloading it.
To delete the PVC and the model: