Bemærk
Adgang til denne side kræver godkendelse. Du kan prøve at logge på eller ændre mapper.
Adgang til denne side kræver godkendelse. Du kan prøve at ændre mapper.
Run:ai Model Streamer reads model tensors concurrently and streams them to GPU memory, overlapping storage reads with CPU-to-GPU transfers. This capability can shorten model-loading time and reduce GPU idle time during inference-server startup and scale-out. Use it to load SafeTensors models from Azure Blob Storage into vLLM because it supports direct object-storage loading and integrates with vLLM's model loader. For the upstream integration, see Loading models with Run:ai Model Streamer.
By using distributed accelerator for cloud storage (DACS), this loading path can also reuse model data cached across workloads. vLLM uses Run:ai's Azure backend to load the DACS native client library. Reads retrieve cached data or fetch missing data from Blob Storage.
This article uses Run:ai Model Streamer 0.17.0, the public dacs-client==0.1.0 package, and the v0.1.0 server build. The main procedure installs the dependencies in your workload image. It doesn't require a separate DACS client image or Kubernetes ImageVolume support.
Complete Get started with distributed accelerator for cloud storage on AKS before using this article. To supply the DACS client libraries without rebuilding your workload image, see Mount DACS client libraries with ImageVolume.
Prerequisites
- An existing DACS deployment and its discovery-service hostname.
- An AKS GPU node pool with compatible NVIDIA drivers, a device plugin, and enough GPU memory for your model. The following example uses
bfloat16, which requires a compatible GPU. - For workload-identity authentication, an AKS cluster with the OIDC issuer and Microsoft Entra Workload ID enabled. See Enable OIDC issuer and Microsoft Entra Workload ID on an AKS cluster.
- A compatible GPU vLLM image and a Linux environment with glibc 2.35 or newer. Use the same Python environment as your application.
- A private Blob container for model files.
- A separate uploader user or managed identity with Storage Blob Data Contributor on the model container.
- An inference identity with Storage Blob Data Reader on the model container. Configure one method in Configure Blob authentication.
- Permission to build and push your workload image, and permission for AKS to pull it.
- Azure CLI, AzCopy v10, and the Hugging Face CLI on your preparation machine if you need to upload a model.
Model preparation doesn't require a GPU. You can also check package installation, native-library loading, and Blob authentication on CPU nodes with a compatible application image. Those checks don't validate GPU scheduling, memory sizing, or inference.
Upload model files to Blob Storage
Skip this section if the complete model is already in your private container. For a cold-cache test, use a model prefix that you didn't read through DACS.
Use the uploader identity for this procedure, not the inference identity. For container-scoped role assignments, see Assign an Azure role for access to blob data. Azure control-plane Owner or Contributor alone doesn't grant Blob data access through Microsoft Entra ID.
Download the model
The following example downloads the inference files for SmolLM2-135M-Instruct at a fixed revision. Its model card identifies the license as Apache-2.0. Install the Hugging Face CLI, then run:
MODEL_ID="HuggingFaceTB/SmolLM2-135M-Instruct"
MODEL_REVISION="12fd25f77366fa6b3b4b768ec3050bf629380bac"
MODEL_DIR="$PWD/models/SmolLM2-135M-Instruct-$MODEL_REVISION"
hf download "$MODEL_ID" \
config.json generation_config.json model.safetensors \
tokenizer.json tokenizer_config.json special_tokens_map.json \
vocab.json merges.txt README.md \
--revision "$MODEL_REVISION" \
--local-dir "$MODEL_DIR"
Optionally, use a current CLI's hf cache verify command to verify the downloaded files against Hub checksums:
hf cache verify "$MODEL_ID" \
--revision "$MODEL_REVISION" \
--local-dir "$MODEL_DIR"
Because this download selects only inference files, verification can report missing repository files such as unused model variants. It can also report extra local Hugging Face download metadata. These messages aren't checksum failures. Investigate unexpected files or checksum mismatches before uploading.
For another model, include all SafeTensors shards and any weight index, model and generation configuration, tokenizer files, and applicable license and attribution files. Preserve the required directory structure. Don't stage unrelated ONNX variants, training checkpoints, optimizer files, or credentials.
Upload with AzCopy
Install AzCopy v10. Sign in through Azure CLI as the uploader user, then copy the local directory's contents into a new Blob prefix:
TENANT_ID="<tenant-id>"
STORAGE_ACCOUNT="<storage-account>"
CONTAINER="<container>"
RUN_ID="$(date -u +%Y%m%dT%H%M%SZ)"
MODEL_PREFIX="models/SmolLM2-135M-Instruct-${MODEL_REVISION}-${RUN_ID}"
MODEL_URL="https://${STORAGE_ACCOUNT}.blob.core.windows.net/${CONTAINER}/${MODEL_PREFIX}"
az login --tenant "$TENANT_ID"
export AZCOPY_AUTO_LOGIN_TYPE=AZCLI
export AZCOPY_TENANT_ID="$TENANT_ID"
azcopy copy "$MODEL_DIR/*" "$MODEL_URL" \
--recursive=true \
--overwrite=false \
--exclude-path ".cache;.git"
azcopy list "$MODEL_URL"
The quoted wildcard copies the directory's contents directly into the prefix without adding a local-directory level. The exclusions omit download and Git metadata. Continue only after AzCopy reports no failed transfers and the destination contains all required files. For more information, see Upload directory contents with AzCopy and Authorize AzCopy using Azure CLI.
For an uploader running on an Azure VM, you can instead authorize AzCopy with a managed identity. Keep the container private and remove temporary uploader role assignments after preparation. Allow time for new role assignments to propagate; don't work around authorization errors by adding storage keys or client secrets.
Record the model revision and Blob versions or ETags. The Run:ai model location is az://<container>/<model-prefix>. Keep these files unchanged between cold and warm loads. For another cold test, use another new prefix rather than evicting data from a shared cache.
Prepare the workload image without ImageVolume
Install dependencies during your image build, not at pod startup. Keep your existing application, base image, and entrypoint. Use the application's package manager and requirements or constraints workflow to preserve compatible dependencies, including Torch, CUDA, and NumPy.
Use a GPU-enabled vLLM image with Torch and CUDA versions compatible with your GPU nodes and NVIDIA drivers. Check the image's installed Run:ai core and Azure backend packages before adding dependencies; not every vLLM image includes the Azure backend.
Resolve dependency conflicts in your workload image before deploying. The pip check step in the following section must complete successfully. Use your application's dependency constraints to preserve compatibility with other installed packages.
If your image doesn't already contain compatible Run:ai core and Azure backend packages, add the following instruction to your Dockerfile:
RUN python3 -m pip install --no-cache-dir "runai-model-streamer[azure]==0.17.0"Install the public DACS client package, which supplies the native client libraries, in that same Python environment:
RUN python3 -m pip install --no-cache-dir "dacs-client==0.1.0" && \ python3 -m pip checkRun:ai's Azure extra doesn't install the DACS client libraries. If your application already has compatible Run:ai packages, only the DACS client package installation is needed.
Build and push using your existing Dockerfile and build context. Run the following commands in Bash on your preparation machine. This example builds for x86-64 nodes:
REGISTRY_NAME="<registry-name>" REGISTRY_SERVER="$(az acr show --name "$REGISTRY_NAME" --query loginServer --output tsv)" WORKLOAD_IMAGE="$REGISTRY_SERVER/workload-runai-dacs:0.17.0-0.1.0" az acr login --name "$REGISTRY_NAME" docker build --platform linux/amd64 -t "$WORKLOAD_IMAGE" . docker push "$WORKLOAD_IMAGE"In the same Bash session, resolve the native-library path from the built image:
docker run --rm --entrypoint python3 "$WORKLOAD_IMAGE" \ -c 'from importlib.metadata import distribution; from pathlib import Path; p=Path(distribution("dacs-client").locate_file("py_dacs_client/libStorageDirect.so")).resolve(); assert p.is_file(), p; print(p)'Use the printed absolute path when configuring the workload. Keep the complete client installation, including sibling native libraries.
Prefer an immutable image digest when deploying. Updating the baked-in client requires rebuilding and redeploying the workload image.
Configure Blob authentication
Use AKS workload identity for Blob authentication. The deployment example uses this method with Storage Blob Data Reader scoped to the model container. The uploader's write permission isn't needed for inference. For other authentication methods, see Azure authentication in the Run:ai Model Streamer documentation.
Use AKS workload identity
The deployment example uses the runai-reader ServiceAccount in the runai-dacs namespace. Complete the following steps before deploying the workload:
Enable the OIDC issuer and Microsoft Entra Workload ID on your existing AKS cluster, if they aren't already enabled.
Create a managed identity, or select an existing identity.
If the
runai-dacsnamespace doesn't already exist, create it.kubectl create namespace runai-dacsCreate a Kubernetes ServiceAccount annotated with that identity's client ID.
Create the federated identity credential for the exact namespace and ServiceAccount. You don't need to complete the subsequent Key Vault steps.
Assign the Blob reader role to the managed identity federated to your application ServiceAccount. For a container-scoped assignment example, see Grant read access to model data, substituting this identity for the quickstart's kubelet identity.
The Pod must select the ServiceAccount and have the azure.workload.identity/use: "true" label. AKS injects the identity settings and projected token. Don't manually add client secrets or storage account keys.
Configure image-pull access separately
For private ACR images, configure the kubelet identity's pull permissions as described in Add the client to a workload image. Use AcrPull for classic RBAC. For an ABAC-enabled registry, use Container Registry Repository Reader with appropriate repository access. Don't use --attach-acr for that registry.
Blob-read access for the application's identity doesn't grant image-pull access to the kubelet. For ImageVolume, pull access must cover both the workload and client images, including separate registries or repositories when applicable.
Configure and deploy the workload
Set these four application variables before starting vLLM. They are separate from the identity settings supplied by your authentication method:
| Environment variable | Purpose |
|---|---|
AZURE_STORAGE_ACCOUNT_NAME |
Storage account containing the model. |
CACHE_DISCOVERY_URL |
DACS discovery hostname, such as cache-sample-discovery.<namespace>.svc.cluster.local. |
CACHE_SERVER_PORT |
Cache port, typically 9065. |
RUNAI_STREAMER_EXPERIMENTAL_AZURE_CACHE_LIB |
Absolute native-library path resolved from your workload image. |
Important
Cache discovery isn't optional. A model load can succeed by reading Blob Storage directly when the cache isn't configured. With the Run:ai Azure backend 0.17.0, also specify the native-library path explicitly instead of relying on automatic discovery.
Use Run:ai's model URI format, az://<container>/<model-prefix>, with the account supplied through AZURE_STORAGE_ACCOUNT_NAME. This URI format differs from the native Python client's canonical URI, which includes the account hostname.
Replace the placeholders in the following example and save it as runai-dacs-pod.yaml. Use the model prefix from your upload and the native-library path printed by the built image. The example uses workload identity and contains no ImageVolume or init container:
apiVersion: v1
kind: Pod
metadata:
name: runai-dacs-vllm
namespace: runai-dacs
labels:
azure.workload.identity/use: "true"
spec:
serviceAccountName: runai-reader
restartPolicy: Never
nodeSelector:
agentpool: "<gpu-nodepool-name>"
tolerations:
- key: nvidia.com/gpu
operator: Exists
effect: NoSchedule
containers:
- name: vllm
image: "<workload-image>"
command: ["vllm", "serve"]
args:
- "az://<container>/<model-prefix>"
- "--load-format"
- "runai_streamer"
- "--served-model-name"
- "cached-model"
- "--dtype"
- "bfloat16"
- "--max-model-len"
- "1024"
env:
- name: AZURE_STORAGE_ACCOUNT_NAME
value: "<storage-account>"
- name: CACHE_DISCOVERY_URL
value: "<cache-discovery-hostname>"
- name: CACHE_SERVER_PORT
value: "9065"
- name: RUNAI_STREAMER_EXPERIMENTAL_AZURE_CACHE_LIB
value: "<dacs-native-library-path>"
- name: RUNAI_STREAMER_CHUNK_BYTESIZE
value: "8388608"
- name: SI_streamingChunkSize
value: "8388608"
resources:
requests:
cpu: "4"
memory: 16Gi
limits:
cpu: "4"
memory: 16Gi
nvidia.com/gpu: 1
ports:
- containerPort: 8000
readinessProbe:
httpGet:
path: /health
port: 8000
periodSeconds: 10
timeoutSeconds: 5
volumeMounts:
- name: shm
mountPath: /dev/shm
volumes:
- name: shm
emptyDir:
medium: Memory
sizeLimit: 2Gi
These resources illustrate a small model, not a 30B model. Preserve your workload's required scheduling, resources, probes, shared memory, and other vLLM arguments. Adjust GPU count, tensor parallelism, model length, and memory sizing for your model. For a Deployment or StatefulSet, apply Pod labels under spec.template.metadata and Pod settings under spec.template.spec.
The first deployment starts vLLM and loads the model before the Pod becomes ready. For a cold-cache comparison, complete these steps in order:
Select model data that the cache hasn't read, such as the fresh prefix from the upload procedure. Don't evict data from a shared cache.
Before deploying the application Pod, capture a baseline metrics snapshot from every cache-server Pod as described in Monitor distributed accelerator for cloud storage. Record the timestamp and each server's
cache_server_upload_request_size_bytes_sum,cache_server_download_request_size_bytes_sum, andcache_server_cache_size_bytes.Deploy and inspect the accepted Pod:
kubectl apply --dry-run=server -f runai-dacs-pod.yaml kubectl apply -f runai-dacs-pod.yaml kubectl -n runai-dacs get pod runai-dacs-vllm -o yaml kubectl -n runai-dacs wait --for=condition=Ready pod/runai-dacs-vllm --timeout=30m
Check the workload runtime
Run this check inside the vLLM application container, using the application's Python executable. It verifies package consistency and native-library loading, not Blob authentication or cache usage:
kubectl -n runai-dacs exec -i runai-dacs-vllm -c vllm -- python3 - <<'PY'
import ctypes
import os
import subprocess
import sys
from importlib.metadata import version
for package in (
"vllm",
"torch",
"runai-model-streamer",
"runai-model-streamer-azure",
):
print(f"{package}: {version(package)}", flush=True)
subprocess.run([sys.executable, "-m", "pip", "check"], check=True)
path = os.environ["RUNAI_STREAMER_EXPERIMENTAL_AZURE_CACHE_LIB"]
print(f"Checking DACS native library: {path}", flush=True)
library = ctypes.CDLL(path)
print(f"DACS native library loaded: {path}")
PY
Resolve errors before continuing. For the GPU deployment, also check GPU visibility and vLLM's engine logs to confirm CUDA execution and the intended model dtype. Readiness or a successful library load alone doesn't prove GPU execution or DACS cache use.
Verify cold and warm cache behavior
Use the predeployment metrics baseline collected in Configure and deploy the workload. The model load performed during deployment is the cold load.
Verify the cold load
Inspect the application log:
kubectl -n runai-dacs logs runai-dacs-vllm -c vllm
Look for native ReadChunk stats. RemoteClient counts backing-store weight reads; RemoteCache counts remote-cache reads. Some cache hits during the first load are normal because earlier reads can populate data. GetProperties cache hits describe metadata, not model-weight hits.
Send an inference request:
kubectl -n runai-dacs exec -i runai-dacs-vllm -c vllm -- python3 - <<'PY'
import json
import urllib.request
body = json.dumps({
"model": "cached-model",
"prompt": "Once upon a time",
"max_tokens": 16,
"temperature": 0,
}).encode()
request = urllib.request.Request(
"http://127.0.0.1:8000/v1/completions", data=body,
headers={"Content-Type": "application/json"},
)
with urllib.request.urlopen(request, timeout=600) as response:
result = json.load(response)
print(json.dumps(result, indent=2))
assert result["choices"][0]["text"].strip()
assert result["usage"]["completion_tokens"] > 0
PY
Confirm nonzero cold backing-store reads. Keep the application Pod running while you check asynchronous cache population:
- Sample
cache_server_upload_request_size_bytes_sumandcache_server_cache_size_bytesfrom every cache-server Pod for the selected Cache resource every 10 seconds, for up to five minutes. Use the metrics endpoint procedure. Use fresh samples, not repeated copies of a stored scrape. - Compare the samples with the predeployment baseline. Require an increase in total uploaded bytes and total cached bytes across those servers. Don't require every server to receive data.
- Wait until each server's upload-byte counter remains unchanged for three consecutive sampling intervals after the total upload increase. Confirm that no server restarted and no counter reset during the comparison.
- Save that final sample as the post-cold snapshot, including
cache_server_download_request_size_bytes_sumfrom every server as the baseline for the warm load.
If these conditions aren't met within five minutes, stop the comparison rather than treating the cache as warm. Check client upload errors, cache-server health, and ongoing traffic before retrying. Larger models can need a longer observation window. A missing metric or failed scrape isn't a zero value or evidence that uploads have finished.
Stable counters are a readiness signal, not proof that every model chunk is cached. Shared-cache traffic can also prevent stabilization or account for increases. Use a quiet validation window where possible, and require the fresh-Pod warm-read checks below to confirm distributed-cache reuse.
Verify a warm load in a fresh pod
Save the cold log and pod UID. Recreate only the application pod by using the same model files, cache endpoint, library, and chunk size:
kubectl -n runai-dacs logs runai-dacs-vllm -c vllm > runai-cold.log
kubectl -n runai-dacs get pod runai-dacs-vllm -o jsonpath='{.metadata.uid}' > runai-cold.uid
kubectl -n runai-dacs delete pod runai-dacs-vllm --wait=true
kubectl apply -f runai-dacs-pod.yaml
kubectl -n runai-dacs wait --for=condition=Ready pod/runai-dacs-vllm --timeout=30m
kubectl -n runai-dacs get pod runai-dacs-vllm -o jsonpath='{.metadata.uid}' > runai-warm.uid
kubectl -n runai-dacs logs runai-dacs-vllm -c vllm > runai-warm.log
Repeat the runtime check and inference request. Then, capture a post-warm metrics snapshot from every cache server. Confirm different pod UIDs, an increased remote-source cache hit rate, and fewer backing-store reads. Compare cache_server_download_request_size_bytes_sum with the post-cold snapshot to corroborate warm cache reads. A fresh process avoids mistaking local in-memory prefetch for a distributed-cache hit.
The remote-source hit rate is RemoteCache / (RemoteCache + RemoteClient), excluding local prefetch. Use the latest cumulative snapshot for each native process, mount, and chunk size; don't sum periodic samples. Exclude measurements affected by cache-server restarts or counter resets. Shared server counters corroborate application logs but don't attribute all traffic to one workload.
Metadata can still contact Blob Storage even when warm weight-read counters show zero backing-store reads.
Mount DACS client libraries with ImageVolume
Use this alternative only when the node runtime supports ImageVolume and you have a pullable client image built from the public DACS wheel. Don't assume that a proposed MCR image or tag is published. If no suitable client image is available, use the workload-image procedure in the previous section.
Your GPU vLLM image must already contain compatible Run:ai core and Azure packages. You don't need to install the DACS client package in its Python environment because the image volume supplies the native client libraries.
Adapt the pod example as follows:
Keep your existing compatible vLLM image instead of rebuilding it to install the DACS client libraries.
Add a read-only client image volume and mount:
# Add to spec.volumes. - name: dacs-client image: reference: "<accessible-client-image>" pullPolicy: IfNotPresent # Add to the vLLM container's volumeMounts. - name: dacs-client mountPath: /opt/cache-client readOnly: trueSet
RUNAI_STREAMER_EXPERIMENTAL_AZURE_CACHE_LIBto the absolute path oflibStorageDirect.sounder/opt/cache-client, based on that image's actual layout. Preserve sibling native dependencies.Deploy and confirm that the image volume and read-only mount remain in the live pod specification. Verify the file exists, then run the same application-container runtime and cold/warm checks.
Some older clusters can remove unsupported image-volume fields and mounts from the accepted pod. Kubernetes version and server dry-run alone aren't sufficient. Don't install or preload a conflicting version of the DACS client libraries alongside the mounted client; an absolute library path doesn't isolate its native dependency namespace.
Optimize read performance
Why tune chunk size? Models have different tensor sizes and read patterns, so one chunk size might not deliver the best model-loading performance for every model. Oversized chunks fetch more data than a read needs and increase buffering. Undersized chunks increase request counts and per-request overhead. Choosing a size suited to the model's commonly occurring tensors balances these costs, improves effective weight-read throughput, and reduces model-loading time, including warm loads from DACS. Treat the following recommendations as starting points and measure cold and warm loading times separately. Gains depend on the model, loader concurrency, and storage and network performance.
Choose a chunk size by using the following model-specific recommendations. For other models, inspect the tensor metadata as described in the next section, or use 32 MiB if metadata inspection isn't available.
| Model | Recommended chunk size | Value in bytes |
|---|---|---|
qwen3-coder-30b-a3b-instruct |
3 MiB | 3145728 |
gpt-oss-20b |
2 MiB | 2097152 |
deepseek-r1-0528 |
14 MiB | 14680064 |
deepseek-v3-0324 |
14 MiB | 14680064 |
phi-2 |
2 MiB | 2097152 |
mistral-small-4-119b-2603-nvfp4 |
2 MiB | 2097152 |
ministral-3-3b-instruct-2512 |
2 MiB | 2097152 |
ministral-3-3b-instruct (alias) |
2 MiB | 2097152 |
ministral-3-8b-instruct-2512 |
2 MiB | 2097152 |
ministral-3-8b-instruct (alias) |
2 MiB | 2097152 |
ministral-3-14b-instruct-2512 |
2 MiB | 2097152 |
ministral-3-14b-instruct (alias) |
2 MiB | 2097152 |
| Fallback when metadata inspection isn't available | 32 MiB | 33554432 |
Choose a chunk size for another model
Use a Safetensors metadata viewer or header-inspection tool to inspect all model shards, without loading tensor contents:
- Find each tensor's stored size in bytes: its ending data offset minus its starting offset. Exclude the
__metadata__entry. - Treat every size above 32 MiB as 32 MiB before counting occurrences.
- Select the most frequently occurring size across all shards. If multiple sizes occur equally often, choose the larger one.
- If the selected size is below 2 MiB, use 2 MiB.
For example, if 3-MiB tensors occur most frequently after capping, use 3 MiB (3145728 bytes). If metadata inspection is unavailable, use 32 MiB (33554432 bytes).
Configure the application
Set both RUNAI_STREAMER_CHUNK_BYTESIZE and SI_streamingChunkSize to the resulting byte value in the application container's environment. For example, for a 3-MiB recommendation:
env:
- name: RUNAI_STREAMER_CHUNK_BYTESIZE
value: "3145728"
- name: SI_streamingChunkSize
value: "3145728"
Set these values explicitly; the table describes recommendations, not automatic model detection or built-in defaults. Recreate the application Pod after changing them; cache servers don't need restarting. Readers and population jobs sharing cached data should use the same chunk size.
Run:ai's read size and the native cache-chunk size are separate controls, configured together above. Benchmark the recommendation with your workload, and tune loader concurrency separately. Compare cold and warm loads independently. Changing the cache-chunk size creates different cache keys and can temporarily increase cache occupancy. Change one setting at a time and measure load time, native counters, cache metrics, CPU, GPU and host memory, and network use. Tensor alignment alone doesn't guarantee better performance.
Warning
The public native client's default asynchronous upload-buffer budget is 400 GiB in 0.1.0. It isn't a preallocation or a safe Pod-memory limit. Large-model cold population can exhaust a smaller Pod. For memory-constrained workloads, test synchronous population (SI_cacheEnableSyncUpload=true) or a bounded asynchronous upload budget (SI_cacheMaxUploadBufferSizePerProcessGB). Budget model staging, native upload/prefetch buffers, GPU memory, and /dev/shm separately. These safeguards are optional workload-specific settings, not additional requirements for the small-model example.
Troubleshoot
| Symptom | Check |
|---|---|
| Image pull fails | Kubelet pull permissions, registry permission mode, image digest, and network access. |
| Native library doesn't load | Application Python environment, explicit path, glibc compatibility, and sibling native libraries. |
| Blob authorization fails | Selected identity, reader role, role propagation, federation, and Pod label. For other authentication methods, follow the upstream Azure authentication guidance linked earlier. |
| Model loads without cache traffic | Both discovery settings, provider loading, DNS/network reachability, and native data-read counters. |
| Warm loads still read Blob data | Population completion, Blob versions, matching chunk sizes, evictions, and partial reads. |
| ImageVolume is missing | Live Pod fields and node/runtime support; use the baked-in method if unavailable. |
| Pod is OOM-killed | Model and staging memory, native buffers, GPU/KV-cache sizing, and /dev/shm. |
For a separate direct-Blob comparison, set RUNAI_STREAMER_EXPERIMENTAL_AZURE_CACHE_ENABLED=0 and recreate a test Pod. Setting it to 1 instead makes provider-loading failures fail startup rather than silently fall back. Neither setting proves cache hits; inspect the data-read counters.
Clean up the example
If you created the standalone example Pod, delete it when finished:
kubectl -n runai-dacs delete pod runai-dacs-vllm --wait=true
Remove only model prefixes, images, identities, federation, namespaces, and role assignments that you created for this example and no longer need. Don't delete an existing shared cache or identity. Retained model data is now warm; use a fresh prefix for another cold-cache validation.