Phase 5: MaaS Model Deployment
This phase deploys LLM models and registers them with the MaaS platform. Each model includes the inference workload (LLMInferenceService) and the MaaS control plane resources (MaaSModelRef, MaaSAuthPolicy, MaaSSubscription) that enable API key management, access control, and rate limiting.
All file paths and oc apply commands in this guide are relative to the rhoai-maas-guide repository root. Make sure you have cloned it and are working from its root directory (see Getting Started).
|
| This guide is not a replacement for the official Red Hat OpenShift AI Models as a Service documentation. It is a companion resource with opinionated Kustomize manifests and automation scripts to accelerate deployment. |
Available Models
simulator (recommended for initial testing)
A CPU-only mock LLM that requires no GPU. It uses the llm-d-inference-sim container to simulate an OpenAI-compatible inference endpoint. This is the best choice for validating the MaaS platform without GPU infrastructure.
-
Runtime: llm-d-inference-sim (CPU-only)
-
Resources: 100m-500m CPU, 256Mi-512Mi memory
-
GPU: Not required
-
Startup time: ~30 seconds
granite-tiny-gpu
Red Hat AI Granite 4.0-h-tiny FP8 Dynamic, a small Granite model (~1B parameters) running on the Red Hat AI Inference Server (vLLM CUDA). Suitable for clusters with modest GPU resources.
-
Runtime: vLLM CUDA (registry.redhat.io/rhaiis/vllm-cuda-rhel9:3.3.0)
-
Model weights: OCI modelcar from registry.redhat.io
-
Resources: 2-4 CPU, 8Gi-24Gi memory, 1x NVIDIA GPU
-
GPU memory: ~8GB required
-
Startup time: 5-15 minutes (image pull + model loading)
gemma
Google Gemma 2 9B IT FP8 running on the Red Hat AI Inference Server (vLLM CUDA). A 9B parameter instruction-tuned model with FP8 quantization, suitable for general-purpose text generation and code tasks. Fits on a single A10G GPU (24GB VRAM).
-
Runtime: vLLM CUDA (registry.redhat.io/rhaiis/vllm-cuda-rhel9:3.3.0)
-
Model weights: OCI modelcar
registry.redhat.io/rhelai1/modelcar-gemma-2-9b-it-fp8:1.5 -
Resources: 2-4 CPU, 8Gi-24Gi memory, 1x NVIDIA GPU
-
GPU memory: ~12GB required (FP8 quantized)
-
Startup time: 5-15 minutes (image pull + model loading)
-
Context length: 4096 tokens (configurable via
--max-model-len)
| Gemma is a US-origin model, making it suitable for deployments with export control requirements. The OCI modelcar is available from registry.redhat.io and works in both connected and disconnected (air-gapped) environments. |
gpt-oss-20b
OpenAI gpt-oss-20b running on the Red Hat AI Inference Server (vLLM CUDA). Requires a capable GPU node (A10G or better) with sufficient VRAM.
-
Runtime: vLLM CUDA (registry.redhat.io/rhaiis/vllm-cuda-rhel9:3.3.0)
-
Model weights: OCI modelcar from registry.redhat.io (~8GB download)
-
Resources: 2-4 CPU, 16Gi-60Gi memory, 1x NVIDIA GPU
-
GPU memory: ~16GB required
-
Startup time: 5-15 minutes (image pull + model loading)
simulator-disconnected
A variant of the simulator for air-gapped clusters. Uses oci:// storage URI instead of hf:// because HuggingFace is unreachable in disconnected environments. Functionally identical to the connected simulator - the inference container generates random responses regardless of the model URI.
-
Runtime: llm-d-inference-sim (CPU-only)
-
Resources: 100m-500m CPU, 256Mi-512Mi memory
-
GPU: Not required
-
Startup time: ~30 seconds (plus OCI modelcar init pull from mirror)
Custom Resource Descriptions
Each model deployment creates the following CRDs under maas.opendatahub.io/v1alpha1:
LLMInferenceService (namespace: llm)
Defines the inference workload: the container, model weights, resource requests, health probes, and gateway routing. This is the actual serving pod that handles inference requests.
MaaSModelRef (namespace: llm)
Registers the LLMInferenceService with the MaaS control plane. The MaaS API uses this reference to discover available models and route API requests to the correct inference endpoint.
MaaSAuthPolicy (namespace: models-as-a-service)
Defines who can access the model. The examples here grant access to all authenticated users (system:authenticated group). You can restrict access to specific users or groups by modifying the subjects field.
MaaSSubscription (namespace: models-as-a-service)
Defines rate-limiting tiers for model access. Each model ships with two tiers:
-
Free tier (priority 10): 100 tokens/min for all authenticated users
-
Premium tier (priority 20): 100,000 tokens/min for all authenticated users
Higher-priority subscriptions take precedence. You can adjust limits, windows, and subject groups to match your usage policies.
Deploying a Model
|
Gateway Pod OOMKill Deploying a model triggers the Istio gateway to load Kuadrant Wasm extensions, which can push the pod past the default If the gateway pod starts OOMKilling after deploying a model, see Phase 2: Gateway OOMKill Prevention for the fix. |
Create the llm namespace if it doesn’t exist and label it for RHOAI:
oc create namespace llm
oc label namespace llm opendatahub.io/generated-namespace=true --overwrite
oc label namespace llm maas.opendatahub.io/gateway-access=true --overwrite
oc label namespace llm opendatahub.io/dashboard=true --overwrite
The maas.opendatahub.io/gateway-access label is required for any namespace that serves models through the MaaS Gateway. Without it, the Gateway will not accept HTTPRoutes from this namespace and the model will not be reachable.
|
The opendatahub.io/dashboard label makes the llm namespace appear as a Data Science Project in the Red Hat OpenShift AI dashboard.
|
Deploy the simulator (no GPU required):
oc apply -k manifests/05-maas-models/simulator/
|
HuggingFace Xet Download Hang The simulator uses
This disables the Xet protocol and falls back to standard HTTP downloads. The patched pod should complete init within 30 seconds. Note that KServe may revert this patch on reconciliation, so reapply if the model is redeployed. GPU models using OCI modelcar images (granite-tiny-gpu, gpt-oss-20b) are not affected by this issue since they do not download from HuggingFace. |
For disconnected (air-gapped) clusters, use the disconnected simulator variant:
oc apply -k manifests/05-maas-models/simulator-disconnected/
Or deploy a GPU model if your cluster has NVIDIA GPUs:
# Gemma 2 9B IT FP8 (12GB+ VRAM, single A10G)
oc apply -k manifests/05-maas-models/gemma/
# Granite 4.0-h-tiny FP8 (8GB+ VRAM)
oc apply -k manifests/05-maas-models/granite-tiny-gpu/
# GPT-oss-20b (16GB+ VRAM, A10G or better)
oc apply -k manifests/05-maas-models/gpt-oss-20b/
Verifying Model Readiness
After deploying, verify that the model is ready:
# Check LLMInferenceService status
oc get llminferenceservice -n llm
# Check that model pods are running
oc get pods -n llm
# Check MaaSModelRef registration
oc get maasmodelref -n llm -o wide
# Check MaaSAuthPolicy and MaaSSubscription
oc get maasauthpolicy -n models-as-a-service
oc get maassubscription -n models-as-a-service
The LLMInferenceService should show a Ready condition. For GPU models, initial startup takes 5-15 minutes while the container image is pulled and model weights are loaded into GPU memory.
Testing Inference
Once the model is ready, test inference through the MaaS API:
# Get the MaaS API domain
CLUSTER_DOMAIN=$(oc get ingresses.config/cluster -o jsonpath='{.spec.domain}')
MAAS_URL="maas.${CLUSTER_DOMAIN}"
# Create an API key (requires the maas-api to be running)
API_KEY=$(curl -sk -X POST "https://${MAAS_URL}/maas-api/v1/api-keys" \
-H "Authorization: Bearer $(oc whoami -t)" \
-H "Content-Type: application/json" \
-d '{"name": "test-key", "subscription": "simulator-free", "expiresIn": "1h"}' \
| jq -r '.key')
# List available models
curl -sk "https://${MAAS_URL}/v1/models" \
-H "Authorization: Bearer ${API_KEY}"
# Get the model endpoint URL from the listing
MODEL_URL=$(curl -sk "https://${MAAS_URL}/v1/models" \
-H "Authorization: Bearer ${API_KEY}" | jq -r '.data[0].url')
# Send a chat completion request (uses the model-specific URL path)
curl -sk "${MODEL_URL}/v1/chat/completions" \
-H "Authorization: Bearer ${API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"model": "facebook/opt-125m",
"messages": [{"role": "user", "content": "Hello!"}]
}'
For GPU models, replace the model name with granite-4-tiny or gpt-oss-20b as appropriate.
Appendix
Directory Structure
Each model follows the same Kustomize layout:
{model}/
kustomization.yaml # Aggregates llm/ and maas/ subdirectories
llm/
model.yaml # LLMInferenceService CR
kustomization.yaml # Sets namespace: llm (+ namePrefix for simulator)
maas/
maas-model.yaml # MaaSModelRef CR
maas-auth-policy.yaml # MaaSAuthPolicy CR (namespace: models-as-a-service)
maas-subscription-free.yaml # Free tier MaaSSubscription
maas-subscription-premium.yaml # Premium tier MaaSSubscription
kustomization.yaml # Lists all MaaS resources
Next step
Proceed to Phase 6: Verification.